# MC4Y-Suite – Projektdokumentation („das Gehirn“)

Dies ist die technische Projektdokumentation der MC4Y-Suite von Peter Hoenig
(MY COACHING FOR YOU, www.my-coaching4you.de). Sie beschreibt alle Werkzeuge und
Module, ihre Dateiformate, den Bau- und Prüfablauf sowie die Gestaltungs- und
Qualitätsregeln – die „Landkarte“ zur Suite.

**So nutzen Sie diese Datei:** Geben Sie den gesamten Text einem KI-Assistenten wie
Claude als Projektanweisung bzw. Kontext. Fragen Sie dann nach einer Erklärung eines
Werkzeugs oder lassen Sie Veränderungen bzw. neue Werkzeuge im beschriebenen Stil
entwickeln. Bauen und prüfen Sie das Ergebnis mit den beschriebenen Python-Skripten.
(The documentation is in German; you may work with it in any language.)

**Lizenz:** GPL-3.0-or-later. Die Suite läuft im Browser bzw. mit einem kleinen
PHP-Server, ohne externe Bibliotheken und ohne Cloud; die Daten bleiben beim Nutzer.

**Datenschutz:** Aus dieser Datei wurden alle persönlichen und betrieblichen Angaben
entfernt bzw. durch Platzhalter in spitzen Klammern ersetzt (z. B. Anschrift, Telefon,
Server-, Netz- und Zugangsdaten, Zertifikate). Sie enthält nur die öffentliche,
technische Beschreibung – keine Kunden-, Rechts- oder Vertragsdaten.

**Marken** wie Spiral Dynamics® (National Values Center Consulting), ITIL®, TOGAF®,
ArchiMate®, COBIT® sowie Produktnamen (z. B. Claude von Anthropic, Microsoft, LinkedIn)
werden nur beschreibend verwendet. Die Coaching-Modelle sind Reflexions- und
Gesprächsmodelle, keine Diagnoseinstrumente.

Stand dieser Momentaufnahme: 27. Oktober 2026. Die jeweils aktuelle Fassung finden Sie unter
www.my-coaching4you.de.

---

## Ordnerstruktur (Projektordner `mc4y-website/`)

Maßgeblicher Speicherort (seit 29.09.2026): `E:\Nextcloud-EAM\HiDrive\users\<benutzer>\Geschäftsplanung\WebSite_Neu\mc4y-website\mc4y-website`.
Der alte Ordner unter `E:\HiDrive\...` ist obsolet – dort nichts mehr lesen oder speichern (gilt für alle Projekte von Peter).

```
generator/            Python-Generator (build.py, core.py, pages_*.py)
  blog/*.md           Blogbeiträge (Markdown + Kopfbereich), _VORLAGE.md
  inhalte/            Im Editor/außerhalb geänderte Fassungen bestehender Seiten (<slug>.html + .json)
  seiten/             Eigene neue Seiten (<slug>.json + <slug>.html), u. a. ressourcen
  menue.json          Menüleiste (Editor-Reiter „Menü“) – Inhalt von Peter
  downloads.json      Anzeigenamen/Beschreibungen je Ordner und Datei in website/downloads, Umzüge (siehe 3b) – Inhalt von Peter
  seitenstand.json    Datum der letzten inhaltlichen Änderung je Seite (live/test), pflegt build.py – siehe „Bauen“
website/              Erzeugte LIVE-Version (wird hochgeladen)
  assets/css|js|img   site.css, site.js, Bilder
  files/              Öffentliche Dateien zum Verlinken (PDFs), auch Unterordner
  downloads/          Downloads für Ressourcen-Seite/Download-Listen: seit 16.10.2026 nur noch experimentell/ (nicht ausreichend
                      getestet) und ressourcen/ (getestet), darunter beliebige Unterordner (Start: ebooks, software, formulare) – 3b
  werkzeuge/          ERZEUGT: Rahmenseiten mit Menüleiste je HTML-Datei aus downloads/ und files/ (siehe 3c) –
                      der Build löscht und schreibt hier alle *.html bei jedem Lauf neu
  kontakt-senden.php  Formularversand per mail() (Honeypot, Zeitprüfung)
  blog-kommentare.php Kommentarfunktion (siehe Abschnitt „Blog-Kommentare“ unten). Speichert live in
                      blog/kommentare/<slug>.json auf dem SERVER – existiert lokal nicht, entsteht erst
                      dort beim ersten Kommentar. Niemals mit hochladen/löschen/überschreiben verwechseln.
website-test/         Erzeugte TEST-Version (nur für <test-domain>)
mc4y-editor.html      Browser-Editor (Chrome/Edge, File System Access API)
genogramm-editor.html Eigenständiges Coaching-Werkzeug (NICHT Teil der Website, nie hochladen):
                      Genogramme erstellen, *.genogramm.json speichern/laden, Export PDF/PPTX/ODP/VSDX/SVG/PNG.
                      Keine externen Bibliotheken; ZIP-, PDF-, PPTX- und VSDX-Erzeugung selbst geschrieben.
                      Alle Ausgaben entstehen aus einer gemeinsamen Grundformen-Liste (buildPrims).
                      PPTX in PowerPoint geprüft; VSDX nur XML-geprüft (kein Visio vorhanden).
soziales-atom-editor.html  Eigenständiges Coaching-Werkzeug (NICHT Teil der Website, nie hochladen):
                      Soziales Atom (Moreno), *.sozialatom.json. Gleiche Architektur wie der Genogramm-Editor;
                      Export-Teil (ZIP/PPTX/VSDX/PDF/SVG/PNG) 1:1 von dort übernommen – Korrekturen in beiden
                      Dateien nachziehen. 20 Symbole (ICONS, nur Polygone/Ellipsen/Rechtecke), 4 Linienarten
                      (LINKS), Bedeutung der Linien frei festlegbar (Legende), Nähe-Kreise um „Ich“,
                      Mann/Frau wahlweise als Piktogramm oder Moreno-Symbol (Dreieck/Kreis).
                      Seit 02.10.2026 zusätzlich Export LibreOffice Impress (.odp, buildODP: ODF 1.3, gleiche Grundformen wie
                      PPTX → draw:polyline/polygon/rect/ellipse/frame in einer draw:g-Gruppe, Folie 33,867 × 19,05 cm wie
                      PPTX-Breitbild, Strichmuster „Strich“/„Punkt“ in styles.xml). Geprüft: XML wohlgeformt, in PowerPoint
                      geöffnet (107 Formen wie PPTX); LibreOffice selbst nicht installiert. buildODP 1:1 auch im
                      Genogramm-Editor (nur Bezeichnungen „Genogramm“; Beispiel: 162 Formen, in PowerPoint geprüft).
ressourcengarten-editor.html  (seit 02.10.2026) Eigenständiges Coaching-Werkzeug (NICHT Teil der Website, nie hochladen):
                      Ressourcengarten (Garten als Metapher der Ressourcen), *.ressourcengarten.json, format
                      'mc4y-ressourcengarten'. Aus soziales-atom-editor.html abgeleitet (Bedienung, Speichern, Export
                      PDF/PPTX/ODP/VSDX/SVG/PNG identisch – Korrekturen am Export in allen drei Editoren nachziehen).
                      94 eigene, farbige Symbole in 10 Gruppen (ICONS mit Farben C, nur Polygone/Ellipsen/Rechtecke),
                      Umschalter „Symbole farbig“ (settings.colored; kit() macht bei Schwarz-weiß Farbflächen zur
                      Hinterlegung), Symbolsuche in der Palette (symQuery), Verbindungen Verbindung/Stark/Versorgung
                      (Welle)/Weg (gestrichelt doppelt)/Grenze (Zaun mit Pfosten)/gestört, Flächen mit Füllung (Wiese,
                      Beet, Wasser, Kies …, frame.fill/solid). Keine feste Methodenquelle hinterlegt – Bedeutungen legt
                      Peter fest; Hinweis „Reflexions- und Gesprächsmodell, kein Diagnoseinstrument; keine Psychotherapie“.
                      Exporte geprüft (PPTX/ODP in PowerPoint je 198 Formen, VSDX-XML, PDF, PNG farbig/sw).
ressourcenbaum-editor.html  (seit 02.10.2026) Eigenständiges Coaching-Werkzeug (NICHT Teil der Website, nie hochladen):
                      Ressourcenbaum, *.ressourcenbaum.json, format 'mc4y-ressourcenbaum'. Aus ressourcengarten-editor.html
                      abgeleitet (gleiche Symbole/Exporte, plus nest/axt/kirsche). Zusätzlich: Baumvorlage doc.tree
                      (drawTree: Erdreich, Wurzeln/Äste per taper(), Krone aus Ellipsen mit nur äußerem Umriss, Stamm zuletzt;
                      size, season SEASONS, soil, captions + cap.krone/stamm/wurzeln editierbar, locked = nicht anklickbar)
                      und beschriftbare Elemente INNER (Blatt, Frucht, Blüte, Knospe, Wurzel, Rindenschild, Tropfen,
                      Sonnenstrahl, Wolke, Stein, Schild, Herz; drawInner: Form wächst mit dem Text, Farbe INNER_FILLS,
                      Platzhaltertext ph wird beim Setzen markiert). Exporte geprüft (PPTX/ODP in PowerPoint je 171 Formen,
                      VSDX-XML, PNG farbig/sw). Methode ohne festen Kanon – Zonentexte und Bedeutungen frei.
systembrett-editor.html (seit 02.10.2026) Eigenständiges Coaching-Werkzeug: Systembrett, *.systembrett.json, format
                      'mc4y-systembrett'. Aus ressourcengarten-editor.html abgeleitet (Bedienung, Export-Teil identisch).
                      Mehrere Bilder je Datei: doc.scenes [{id,name,note,nodes,links,notes,frames}], doc.cur; doc.nodes/
                      links/notes/frames sind nicht gespeicherte Getter auf das aktuelle Bild (attach() – nach JEDEM
                      JSON.parse von doc aufrufen, z. B. restoreFrom). Figuren fe/fr/fn (eckig/rund/neutral, Größe 16/24)
                      in Draufsicht mit „Nase“ = Blickrichtung n.rot (0 = oben, Uhrzeigersinn); drehen per Griff 'rh:'
                      (15°-Raster, Umschalt stufenlos), Q/E, rotBtns, Werkzeug 'face' (Blick auf Element/Stelle).
                      Symbole SYMDEF (Thema, Ziel, Ressource, Balken, Kegel, Zylinder, Herz, Blitz, Frage, Pfeil), Farben
                      mit Bedeutung (settings.cmean → Legende), Linien inkl. Konflikt (Zickzack) und Wirkung (Pfeil, flip).
                      Brett (settings.board/boardStyle/rand/caption), Blicklinien (gaze), Spuren zum vorigen Bild (ghost:
                      gleiche id im Vorbild → gestrichelte alte Lage + Pfeil; Bildkopie behält ids). Szenenleiste #scenes.
                      Export = aktuelles Bild. Geprüft: PPTX/ODP in PowerPoint (48 Formen), VSDX-XML, PDF, PNG.
                      SYMBOLE + PERSPEKTIVE (08.10.2026 abends, Peters Wunsch „Symbole auch ins alte Systembrett, Perspektivenwechsel
                      aus 2D-Sicht“): gleiche Schlüssel wie im 3D-Brett – Menschen mann|frau|junge|maedchen (human/female/child, skin/
                      hair wie 3D: SKINS/HAIRS, Standard im SYMDEF, n.skin/n.hair nur bei Abweichung), EXTRA 15 Tiere/10 Pflanzen/
                      23 Gegenstände (cat, animal; rot nur Tiere und LONG = mauer|wand|zaun). Gezeichnet als flache Bildsymbole PICTO
                      (52 Funktionen (k, M), Einheitskoordinaten −1..1, kit2 pl/pg/el/rc → nur Grundformen, also alle Exporte; Farbrollen
                      M.m/d/l/p/sk/hr, schwarz-weiß = weiß gefüllt). Menschen und Tiere stehen auf rundem Standplatz (tokenR) mit Nase;
                      Bildsymbol bleibt aufrecht, nur die Nase dreht; drawLong zeichnet Mauer/Wand/Zaun von oben (drehbar). Spuren nur
                      als Umriss. Palette: Suchfeld #symSearch (fold ä→ae …), aufklappbare Gruppen h3.pg (palOpen, localStorage
                      'mc4y-systembrett-palette', Tiere/Pflanzen/Gegenstände anfangs zu). Form-Auswahl mit optgroup (CATS), Hautton/Haare.
                      ANSICHT DREHEN: view.rot (Welt-Gruppe translate·scale·rotate; m2s/s2m/viewAt, animateView interpoliert rot/k/
                      Mittelpunkt). Leiste #viewbar unten links: Vorn/Links/Rechts/Hinten (VIEWS2 0/−90/90/180) + Auswahl „Aus Sicht
                      von …“ (#povSel). Grundform-Eigenschaft up:[x,y] = Drehpunkt, um den Texte/Bildsymbole/Notizen/Legende am
                      Bildschirm um −view.rot zurückgedreht werden (toSVG, nur Bildschirm; Exporte immer Ansicht von vorn, Titel über dem
                      Brett nur ungedreht – screenRot). SICHT EINER FIGUR/EINES TIERES (canSee): pov {id, key}, view.rot = −Figur.rot,
                      Figur unten Mitte (povTarget: Maßstab so, dass das Brett VOR der Figur passt), Leiste #povbar (◂ ▸ ⟲ ⟳ ✕),
                      Tasten P / Tab / Umschalt+Tab / Esc, Q/E dreht die Figur und die Sicht folgt (povFollow über pose-key). Außerhalb
                      des Blickfelds blass (markDim → p.dim → opacity .25). Blickfeld-Fächer drawWedges (screenOnly, ±sichtWinkel/2 bis
                      Brettrand), settings.sicht aus|auswahl|alle und sichtWinkel 60–180 (rechts ohne Auswahl, „Perspektive“).
                      Eigenschaften einer Figur: „👁 Aus Sicht …“ + Liste Im Blick / Nicht im Blick / Schaut an (seeInfo, dirWord).
                      Pfeiltasten und Auswahlrahmen arbeiten bildschirmbezogen; neue drehbare Elemente schauen bei gedrehter Ansicht
                      zum oberen Bildrand. Frühe 3D-Exporte (zylinder/fe/fr + s3d) wandelt normScene (up3d) zurück. Geprüft im Browser:
                      Galerie aller 52 Symbole farbig/sw, Spuren, vier Ansichten, Sicht Mutter/Tim mit Q/E/Tab/Esc, Ziehen + Pfeiltasten +
                      Auswahlrahmen bei 90°, Palette-Suche/Gruppen, Form-/Haut-/Haarwechsel, Exporte (PPTX/ODP/VSDX-XML wohlgeformt,
                      je 69 Formen, PDF/SVG ohne NaN, PNG angesehen), 3D → 2D → 3D ohne Abweichungen. PowerPoint selbst nicht geöffnet.
                      GRÖSSE STUFENLOS (08.10.2026, Peters Wunsch): n.size beliebig mit 1 Nachkommastelle, Anzeige in % von sizeBase
                      (Figuren 24, Symbole 18), clampSize 25–400 %. Größengriff 'sz:' (weißes Quadrat an einer Ecke des Auswahlrahmens,
                      bildschirmbezogen über up, SIZE_CORNERS weicht dem Drehgriff aus) → drag.type 'size' (Abstand zur Mitte / Start-
                      abstand, Umschalt = 10-%-Schritte). Alt + Mausrad (alle markierten Knoten, ein Undo-Schritt je Drehbewegung über
                      altWheelT), Tasten +/− (scaleSel 1,1), Regler data-f sizePct (step any) + Knöpfe Kind/Erwachsene bzw. klein/normal/
                      groß, Mehrfachauswahl „− kleiner/+ größer“. FIG_SIZES/SYM_SIZES entfernt. Dreh- und Größengriff haben feste
                      Bildschirmgröße (u = 1/view.k; zoomAt rendert bei Auswahl neu). Geprüft mit der Maus auch bei gedrehter Ansicht.
                      10.10.2026 (zum 3D-Umbau): LINKS + distanziert {dots} (Strich 'dot') und abgebrochen {broken} (Lücke in der Mitte mit
                      Querbalken); Platzhalter ex in docPanel ergänzt. Knoten-Häkchen „Namen auf dem Brett zeigen“ (data-f showLabel →
                      n.hideLabel) und „verstorben“ (dead; nur Menschen/Figuren/Tiere): labelLines liefert bei hideLabel nichts, sonst
                      „† “ vor der ersten Zeile; drawNode zeichnet bei dead ein kleines graues Kreuz rechts oben AUF dem Symbol (poly mit up).
                      ÜBERNAHME DER 3D-NEUERUNGEN (10.10.2026 abends, Peters Wunsch „alle Änderungen, sofern sinnvoll, auch fürs 2D-Brett“):
                      EXTRA + 6 Räume, „Freizeit / Reisen“ (auto dorthin), „Emotionen“ (face2: Gesicht + Züge), „Werte“ (medal2d aus
                      WERTE_EMB) – gleiche Schlüssel/Farben wie 3D, eigene PICTO-Zeichnungen. kit2 hat jetzt h, mono und o.warp(x, y).
                      Haltungen: POSES, n.pose nur Menschen; POSE_WARP (gebeugt/kniend/sitzend verzerren), poseArms (Arme/Hände, SHOULDER2/
                      HEAD2); Palette #palPose (palPose, spec „mann@24@kniend“, newNode liest p[2]); Eigenschaft „Haltung“.
                      Palette: Gruppen als section.pgrp inkl. „Verbindungen“ und „Sonstiges“, ziehbar (palDrag), Reihenfolge palOrder in
                      localStorage 'mc4y-systembrett-palette-reihenfolge', PAL_DEFAULT_ORDER Verbindungen, Menschen, Emotionen, Werte, …, Sonstiges.
                      Beziehungen: linkShape pfadbasiert (o.pts, o.k = Strichstärke, pathAt/pathCum), linkGeo mit l.bend {u, v} in cm (×10),
                      Bogen-Griff 'bh:' (handlePrims, drag 'bend', Doppelklick/„Gerade ziehen“ = gerade), l.width/settings.linkWidth
                      (LINK_WIDTHS), l.layer/settings.linkLayer (Standard vorn): buildPrims zeichnet Linien „hinten“ → Formen (drawNode 'shape')
                      → Linien „vorn“ → Namen ('label'). linksHidden (Taste B, nicht gespeichert), settings.labels (Taste N), l.hideLabel.
                      Auswahl-Markierung liegt jetzt über dem Brett (Brett-Grundformen board:true, render ordnet Brett – Auswahl – Rest – Griffe;
                      vorher lag sie unsichtbar unter der Brettfläche). Stapeln: n.on, SEAT2, supportAt, stackOrder (Untersätze zuerst),
                      stackAll (Gestapeltes wandert beim Ziehen/Pfeiltasten mit), stackAfterMove (Alt = nicht stapeln), „Herunternehmen“.
                      Bilder umsortieren (moveScene, DnD in #scenes, sceneMove), Vergleich: cmp {a, b}, viewPrims() = beide Bretter mit
                      shiftPrims (cmpOff), bearbeitet wird doc.cur (gold umrandet), anderes Brett own 'cmp' → cmpActivateOther; toModel/
                      Auswahlrahmen/fitView/Exporte rechnen mit dem Versatz; Perspektive im Vergleich aus. Leisten: --lw/--rw, lay in
                      'mc4y-systembrett-layout', #splitL/R, #tabL/R, „⛶ Brett groß“ (#btnBig), Taste F = Einpassen. Werte: Name beim
                      Aufstellen vorbelegt (außer w_frei). Geprüft: Galerien aller neuen Symbole farbig und schwarz-weiß, Haltungen, Bogen
                      mit der Maus, Strichstärken/Ebenen, Vergleich (Klick, Ziehen im linken Brett), Umsortieren, Stapeln mit der Maus,
                      Gruppen ziehen, Trennleiste, Exporte PPTX/ODP/VSDX (XML wohlgeformt), PDF/SVG ohne NaN, PNG.
3d-systembrett-editor.html (seit 08.10.2026, Peters Wunsch; das 2D-Systembrett bleibt daneben bestehen) „3D-Systembrett“:
                      Eigene WebGL-2-Darstellung OHNE Bibliotheken (kein three.js): Geo-Klasse mit gBox/gFrustum/gSphere/gAnnulus/
                      gDisc, gExtrudeXY (stehende Umrisse, Ohrenschnitt triangulate) und gPrismXZ (liegende), Lambert+Halbkugel-Licht,
                      Schattenwurf per Shadow-Map (sampler2DShadow, 2048², PCF), Holz/Filz/Raster/Rand im Fragment-Shader (uMat),
                      Picking per Farbkennung in eigenem FBO (Knoten 1…, Verbindungen 0x10000+i). Koordinaten in cm: x rechts,
                      z zur Klientenseite (vorn), y oben, Brett-Oberfläche y = 0, Tisch y = −BT (2,4) – Figuren lassen sich neben das
                      Brett auf den Tisch stellen (groundY). rot 0 = Blick nach −z (hinten), im Uhrzeigersinn; Modell-Vorderseite −z,
                      Modellmatrix T·RY(−rot)·S(scale). Datenformat 'mc4y-systembrett-3d' (*.systembrett3d.json): settings {shape
                      quadrat|rechteck|rund, size 40–120 cm, style hell|dunkel|papier|filz|grau, rand, grid, labels, colored, shadows,
                      sicht aus|auswahl|alle, sichtWinkel, ghost, legend, meaning, cmean}, scenes [{id,name,note,nodes,links,extra2d}],
                      Knoten {id, s, x, z, rot, scale (1 = Erwachsene, 0.67 = Kind), color, label, focus}. Schlüssel s und Linienarten
                      wie im 2D-Brett. from2D()/to2D(): 1 cm = 10 Einheiten, Figuren size 24 ↔ scale 1, Symbole size2d (18/16);
                      Notizen/Bereiche des 2D-Bretts bleiben als scene.extra2d für den Rückweg erhalten (Rundlauf geprüft: 0 Abweichungen).
                      Öffnen erkennt beide Formate (2D → neuer Dateiname, 2D-Datei wird nie überschrieben); Export › „Als 2D-Systembrett
                      speichern“. Kamera cam: mode orbit (yaw/pitch/dist/tx/tz, Ansichten VIEWS frei|oben|vorn|links|rechts|hinten,
                      Tasten 1–6, Übergänge cam.anim kind orbit bzw. eye) oder pov (Sicht einer Figur: Augenhöhe MODEL.eye·scale,
                      fov 72, Figur ausgeblendet – hiddenId, auch während des Hinein-/Herausfliegens über anim.hide; Ziehen dreht die
                      Figur, wenn cam.follow, sonst nur povYaw; ←/→ 15°, Tab nächste Figur, Esc zurück; Spuren ausgeblendet). Griffe
                      als HTML-Überlagerung (#hRot Drehen am Boden vor der Figur, 15°-Raster, #hScale Größe rechts neben dem Namen,
                      30–300 %, seit 08.10.2026 stufenlos (scale auf 0,001 gerundet, Regler step any, zusätzlich Alt + Mausrad mit
                      einem Undo-Schritt je Drehbewegung; +/− weiter in Schritten), #hPov „👁 Sicht“); Namen #ovL (lblEls, nach Projektion). Blickfeld-Fächer (endet am Brettrand,
                      edgeDist) und seeInfo(): „Im Blick“ (±sichtWinkel/2), „Nicht im Blick“, „Schaut zu dieser Figur“ (±15°), nur
                      beschreibend. Bildwechsel animiert (sceneAnim/figPose), Spuren = durchscheinende frühere Lage + gestrichelter Pfeil.
                      Export: PNG der aktuellen Ansicht (renderImage: MSAA-FBO → readPixels → 2D-Canvas + drawLabels2d), Bilderbogen
                      (sheetHtml: aktuelle Ansicht, oben, vorn, Sicht jeder Figur, Tabelle „Wer hat wen im Blick?“, optional alle Bilder)
                      → Druck-iframe („Als PDF speichern“). Zwischenstand localStorage 'mc4y-systembrett-3d-entwurf'. Beispiel
                      „Familie und Berufswahl“ (fiktiv, Ist-Bild → Lösungsbild). Geprüft im Browser: Darstellung, Figurensicht Mutter/
                      Vater, Draufsicht, Seitenansicht, rundes Filzbrett, Griffe mit der Maus, Aufstellen + Namen tippen, Verbinden,
                      Tisch, 2D-Rundlauf, PNG-/Bilderbogen-Erzeugung (17 Bilder). Name in editoren-version.py NAMES.
                      MENSCHEN-FIGUREN (08.10.2026, Peters Wunsch „Formen, die wie Mann, Frau und Kind aussehen, verschiedene Farben“):
                      SYMDEF mann|frau|junge|maedchen (human, female, child, s2d fe|fr), Palette-Gruppe „Menschen“, Kinder mit Größe 0.68
                      aufgestellt (Modell hat Kinderproportionen bei Erwachsenenhöhe, damit Kind/Erwachsene-Knöpfe gleich bleiben).
                      humanModel(female, child): Schuhe, Beine/Hose bzw. Rock, Oberkörper (gestaucht, Geo.add rechnet Normalen mit der
                      inversen Transponierten), Schultern, Arme (mRZ), Hände, Kopf, Augen + Nase (= Blickrichtung −z), Haare (Kappe,
                      bei Frau/Mädchen hinten länger, Mädchen Zöpfe). Farbrollen: main = Kleidung (n.color), skin (n.skin, SKINS 7 Töne),
                      skinD, hair (n.hair, HAIRS 8 Farben inkl. grau/weiß), pants (aus der Kleidungsfarbe abgedunkelt), dark (Schuhe), eye.
                      Ohne „Farbig“ alles in Holztönen. 2D-Export seit 08.10.2026 abends mit gleichem Schlüssel s (das 2D-Brett kennt
                      Menschen, Tiere, Pflanzen, Gegenstände selbst), skin/hair nur bei Abweichung, Größe base2d (Erwachsene 24, Kinder 16,
                      Symbole size2d); s2d/s3d/autoLabel nur noch zum Lesen früher Exporte. Beispiel nutzt jetzt Menschen-Figuren.
                      TIERE, PFLANZEN, GEGENSTÄNDE (08.10.2026, Peters Wunsch): EXTRA = Gruppen [Titel, cat tiere|pflanzen|dinge,
                      [Schlüssel, Bezeichnung, Standardfarbe]] → SYMDEF {cat, animal, size2d 18, s2d 'zylinder'}; 15 Tiere, 10 Pflanzen,
                      23 Gegenstände. Modelle XM[schlüssel](a) mit buildExtra-Baukasten (a.box/cyl/ell/sph/ext/leg4/eyes/at(Matrix)/eye;
                      Rollen main/mainD/mainL oder feste Farbe '#RRGGBB', ohne „Farbig“ → woodTone); r/top aus der Geometrie, Vorderseite −z.
                      Tiere: canSee() (Sicht, Blickfeld, „Schaut zu“) mit Augenpunkt eyePos (povState rechnet ihn mit nodeModel um).
                      Palette: Suchfeld #palSearch (fold: ä→ae …), aufklappbare Gruppen (palOpen in localStorage 'mc4y-systembrett-3d-palette',
                      Tiere/Pflanzen/Gegenstände anfangs zu), Raster .pgrid bei > 10 Einträgen, Trennstellen hyph(). Bildchen THUMBS: jedes
                      Modell per thumbOf() in eigenem MSAA-FBO (96 × 78, transparent, Farben vormultipliziert zurückgerechnet) → PNG,
                      gestaffelt erzeugt (fillThumbs/thumbStep, alle 48 in ~0,13 s). Art-Auswahl mit optgroup je Gruppe (CATS).
                      Neue Elemente schauen beim Aufstellen zur Kamera (placeAt, 15°-Raster). 2D-Export: siehe oben (gleicher Schlüssel).
                      Geprüft: Galerien aller drei Gruppen, Suche, Aufstellen,
                      Sicht des Hundes (Mensch wirkt riesig), Blickfeld-Text, 2D-Rundlauf ohne Abweichungen.
                      UMBAU 10.10.2026 (Peters Wunschliste, Sicherung editor-backups/*_3d-systembrett-umbau):
                      BILDSCHIRM: Spalten über CSS-Variablen --lw/--rw, Objekt lay {lw, rw, noleft, noright} in localStorage
                      'mc4y-systembrett-3d-layout', applyLayout(refit); Trennleisten #splitL/#splitR (Ziehen, 140–460 bzw. 200–520 px),
                      Laschen #tabL/#tabR (data-act togLeft/togRight; liegen mittig AUF der Trennleiste), „⛶ Brett groß“ (#btnBig,
                      bigBoard = beide aus). Einpassen fitView()/Taste F/„⤢ Einpassen“; presetOrbit nutzt fitDist(o, aspect) → {dist, tx,
                      tz}: Binärsuche über den Abstand, je Abstand Newton-Verschiebung des Drehpunkts entlang der Bodenblickrichtung,
                      damit das Bild mittig im freien Bereich liegt; fitMargin() = tatsächliche Höhe von #scenes/#paneT0 oben und
                      #viewbar/#povbar unten; Namensschilder bekommen Platz (room, 26 px + 15 je Zusatzzeile).
                      VERGLEICH: cmp = {a, b} (Bild-ids), cmpIdx/cmpOn/activeSide/paneX/paneW; frame() zeichnet zwei Hälften (viewport +
                      scissor, drawScene(cs, W, H, fb, x0)), gemeinsame Kamera; Klick in eine Hälfte = activateSide (doc.cur wechselt);
                      Überlagerung je Hälfte (updateOverlay(cs, pane), Schlüssel mit Seitenpräfix, overlayDone/ovSeen); Titel #paneT0/1,
                      Trennlinie #cmpLine; PNG-Export setzt beide Bilder mit Überschrift nebeneinander. Bildleiste: Bilder per HTML5-DnD
                      umsortieren (moveScene), Abzeichen L/R, Auswahl #cmpA/#cmpB, ⇆ tauschen. pick/mpos/rayAt rechnen je Hälfte.
                      BEZIEHUNGEN: quadratische Bézier mit l.bend {u, v} (Versatz der Kurvenmitte längs/quer zur Geraden, cm) – linkPath/
                      pathAt/linkGeo ersetzen linkEnds; Griff #hBend (Ziehen; |v| < 0,8 und |u| < 1,5 = gerade; Doppelklick/„Gerade
                      ziehen“). Neue Arten distanziert (dots) und abgebrochen (broken). Auswahl = goldener Saum (sonst weiß). Ebene:
                      settings.linkLayer vorn (Standard) | hinten, je Linie l.layer; vorn = zweiter Durchgang mit depthFunc GREATER
                      (Alpha 0,8) → nie verdeckt. Picking der Linien MIT Tiefentest (Figur davor gewinnt). linksHidden (Taste B) nur
                      vorübergehend, nicht gespeichert. Namen: Taste N = settings.labels; je Knoten n.hideLabel, je Linie l.hideLabel.
                      HALTUNGEN: POSES (11: stehen, gebeugt, kniend, sitzend, arme, greifen, faust, verschraenkt, gesicht, zeigen, winken),
                      n.pose nur bei Menschen und ≠ stehen; humanModel(female, child, pose) mit Gelenkkette (mAlign/mToward, Oberkörper-
                      transform U mit Neigung, Rock Us mit 0,3·Neigung, Armrichtungen ARMS, Kopf Hm); Modelle lazy über mdl(n)/mdlKey
                      ('mann|kniend'). Palette: Auswahl #palPose (palPose) über „Menschen“, Bildchen in der Haltung, placeAct s@sc@pose.
                      STAPELN: n.on = id des Untersatzes, Höhen SEAT (Stuhl, Bett, Sofa, Tisch …), nodeY rekursiv, supportAt beim
                      Aufstellen/Ziehen/Pfeiltasten (Alt = nicht stapeln), Kinder wandern beim Verschieben und Drehen (setRot) mit;
                      „Herunternehmen“ (offSupport); Löschen des Untersatzes löst on.
                      VERSTORBEN: n.dead (Menschen, Figuren, Tiere) → Kreuz MODEL._cross über dem Kopf (crossScale), Farben über
                      partColor mit Grau gemischt, Name mit „† “ (labelLines), labelY rechnet das Kreuz ein.
                      NEUE SYMBOLE: Gegenstände + kinderzimmer, bad, schrank, abstellkammer, garage, gartenhaus (auto jetzt in Freizeit);
                      Gruppe „Freizeit / Reisen“ (cat freizeit, 23: auto, fahrrad, dreirad, fussgaenger, bus, zug, flugzeug, schiff, boot,
                      meer, see, fluss, berge, wald, wiese, strand, zelt, spielplatz, stadt, grossstadt, einkaufszentrum, museum,
                      bibliothek); Gruppe „Emotionen“ (cat gefuehle, 16: freude, glueck, liebe, stolz, hoffnung, gelassenheit,
                      ueberraschung, trauer, wut, angst, sorge, scham, schuld, einsamkeit, erschoepfung, verwirrung) = Gesicht auf Sockel
                      (emo/faceKit, EMO_R). buildExtra um tube/pris/oval/band erweitert. Beide Gruppen anfangs zugeklappt.
                      2D-AUSTAUSCH: IN2D = Schlüssel, die das 2D-Brett kennt (seit dem Abend alle, s. „WERTE“ unten); unbekannte gingen als
                      zylinder + s3d (+ autoLabel); pose/dead/hideLabel/on und Link-bend/layer/hideLabel werden mitgegeben; size mit
                      1 Nachkommastelle (vorher ganzzahlig → Tiergröße 0,6 kam als 0,611 zurück). Rundlauf geprüft: 0 Abweichungen.
                      Geprüft im Browser: alle 109 Modelle + 44 Haltungsvarianten, Galerien, Stapeln (Katze auf Stuhl, Hund im Bett),
                      Kreuz, Bogen mit der Maus, Laschen/Trennleisten/Brett groß/Einpassen, Vergleich (Klick, synchrones Drehen, PNG),
                      Umsortieren, Haltung aufstellen/ändern, B/N/F, Ebene je Linie, Bilderbogen (23 Bilder), 2D-Darstellung.
                      NACHTRAG 10.10.2026 abends (Peters Korrekturen): Kreuz dezent – MODEL._cross 0,15 × 1,2 cm, Querbalken 0,66,
                      Farbe #56616C, steht AUF dem Kopf (top·scale − 0,06), crossScale 0,6–1,2; labelY + 1,2·crossScale. Palette-
                      Gruppen frei verschiebbar: je Gruppe section.pgrp, h3.pg draggable mit Griff ⋮⋮, HTML5-DnD (palDrag, dropT/dropB),
                      Reihenfolge palOrder in localStorage 'mc4y-systembrett-3d-palette-reihenfolge', sortGroups(); Standard
                      PAL_DEFAULT_ORDER = Verbindungen, Menschen, Emotionen, Figuren, Symbole, Tiere, Pflanzen, Gegenstände, Freizeit /
                      Reisen; Knopf „Reihenfolge der Gruppen zurücksetzen“ (data-palreset) nur bei Abweichung. Strichstärke:
                      LINK_WIDTHS (0,3 Haarlinie … 2,2 sehr kräftig, Faktor auf 4,2 mm), settings.linkWidth (Standard 1) und je Linie
                      l.width (fehlt = wie eingestellt); linkGeo: Breite 0,42·k, Abstände/Pfeil/Querbalken mit kk = max(0,7, k),
                      Lichthof schmaler bei feinen Linien, Trefferfläche unverändert. 2D-Austausch: l.width und settings linkLayer3d/
                      linkWidth3d werden durchgereicht (Rundlauf geprüft). Im 2D-Brett Kreuz ebenfalls klein und grau auf dem Symbol.
                      WERTE (10.10.2026 abends, Peters Wunsch): Gruppe „Werte“ (cat werte, 26 Schlüssel w_…: treue, ehrlichkeit, offenheit,
                      verlaesslichkeit, vertrauen, anhaenglichkeit, liebe, zusammenhalt, familie, ordnung, disziplin, verantwortung, respekt,
                      gerechtigkeit, freiheit, sicherheit, mut, hilfsbereitschaft, geduld, gesundheit, erfolg, wissen, kreativitaet, tradition,
                      nachhaltigkeit, frei = „Eigener Wert (frei benennbar)“). Sinnbilder als gemeinsame Daten WERTE_EMB (Block
                      WERTE-SINNBILDER, IDENTISCH im 2D-Brett – Änderungen in beiden Dateien): Einheitskoordinaten, y nach unten, E.pg/el/ring/
                      ln/cap, Farbe 'w' weiß bzw. 'm' Medaillenfarbe. 3D: medal(key) = Sockel + stehende Medaille (MEDAL_R 1,75, Höhe
                      MEDAL_Y 2,65), Rand 'mainL', Sinnbild als flaches Relief vorn und gespiegelt hinten, Rolle 'emb' (weiß; ohne „Farbig“
                      dunkles Holz). Name beim Aufstellen vorbelegt (außer w_frei, dort Platzhalter „Name des Werts“). PAL_DEFAULT_ORDER mit
                      „Werte“ nach „Emotionen“; fehlende Gruppen werden in gespeicherte Reihenfolgen hinter ihrem Vorgänger eingefügt.
                      2D-AUSTAUSCH seitdem vollständig: IN2D = alle EXTRA-Schlüssel (kein Zylinder-Ersatz mehr), Einstellungen linkLayer/
                      linkWidth/labels mit GLEICHEN Schlüsseln (from2D liest auch die kurzzeitigen *3d-Schlüssel), Größe mit 2 Nachkommastellen
                      (Kinder 0,68 kamen sonst als 0,681 zurück). Rundlauf 3D → 2D → 3D mit allen 129 Symbolen, Haltungen, Untersatz,
                      Bogen, Strichstärken, Ebenen, ausgeblendeten Namen: 0 Abweichungen.
                      ZIEHPUNKTE + ABSTAND (10.10.2026 spätabends, Peters Kritik „Abstand zu groß, mehrere Ziehpunkte nötig, Standard fein,
                      noch feinere Stufe“) – in BEIDEN Brettern gleich: l.via [{t, v}] (t = Anteil A → B der Mittelpunkte, v seitlich in cm;
                      2D ×10), Kurve = Catmull-Rom durch A, Ziehpunkte, B (splinePts, VIA_N 16); l.bend gilt weiter als ein Punkt (viaOf) und
                      wird beim ersten Bearbeiten in via umgewandelt. Griffe: ◆ je Punkt (ziehen; Doppelklick = entfernen), „+“ je Abschnitt
                      (addAt; ziehen = Punkt an dieser Stelle einfügen); v < 4 mm rastet auf 0, nur Nullpunkte = gerade. 3D: Griffe als HTML in
                      #hVia (viaHandles, Elemente wiederverwendet; Ziehen per Delegation, drag.type 'via'); 2D: handlePrims 'bv:id:i'/'ba:id:i',
                      drag 'via', Doppelklick über lastDown (Zeigererfassung macht sonst die Zeichenfläche zum Ziel). Abstand: 3D linkGap =
                      Fußradius (meshData m.foot = größter Abstand der Punkte unter 1,2 cm Höhe, modelFoot) · scale + 3 mm; 2D edgeGap =
                      sichtbarer Umriss in Laufrichtung (Standplatz, Rechteck bei Balken/Würfel/Mauer, sonst Radius) + 3 Einheiten.
                      LINK_WIDTHS + 0,18 „hauchfein“, LINK_WIDTH_STD 0,75 (fein) für neue Bretter und Dateien ohne Angabe; Grenzen 0,1–3.
                      Geprüft mit der Maus in 2D und 3D (Punkt einfügen, zweiten Punkt, Doppelklick entfernen), Rundlauf via/bend/width,
                      2D-Exporte ohne NaN.
analyse-editor.html   (seit 03.10.2026) Analyse- und Gesprächs-Editor: TEMPLATES swot, tows, soar, grow (Phasen), ssc
                      (Start–Stop–Continue), pmi, retro, frei; Felder/Hinweise/Leitfragen frei änderbar, layout grid|phases.
                      Board ist HTML (Karten als Textareas, Ziehen am Griff per HTML5-DnD, ord-Werte), Punkte doc.votes
                      {id 'v_<karte>_<name>', card, who, n} mit Budget doc.dots, Fokus/Phase doc.focus, Zeitbox doc.timer
                      {end, dur}, anonym, Präsentationsansicht. Exporte aus buildPrims (Export-Teil aus ressourcengarten),
                      CSV, Drucken. *.analyse.json, format 'mc4y-analyse'. Peters Auftrag nannte „Landsiedel“ – keine
                      Inhalte von dort übernommen; Methoden allgemein (GROW nach Whitmore, SOAR nach Stavros/Hinrichs, PMI de Bono).
                      Seit 13.10.2026 zusätzlich TEMPLATES johari (Johari-Fenster), wertequadrat (Werte- und Entwicklungsquadrat nach
                      Schulz von Thun), eisenhower (Eisenhower-Matrix), energie (Energiebilanz: was gibt/kostet Kraft) – vor 'frei'.
                      TEAM-SITZUNGEN (optional): website/team-sitzung.php (mit hochladen! Ordner team-sitzungen/ entsteht auf
                      dem Server, .htaccess deny, wie blog/kommentare BEHALTEN bzw. nicht löschen). POST JSON als text/plain
                      (kein CORS-Preflight; CORS * für lokal geöffnete Editoren), Aktionen create/get/put/leave/end, Code 8
                      Zeichen, optionale PIN (sha256 mit Salz), Leitungs-Schlüssel owner zum Beenden (löscht sofort), TTL 7 Tage,
                      Ratenbegrenzung je IP-Hash, Server entschärft < > zu ‹ ›. Client: SYNC, Abgleich per Differenz zum
                      shadow (je Element JSON-Zeichenkette, gleiche Schlüsselreihenfolge in itemsOf/applyRemote!), Polling
                      1,5 s, letzte Änderung gewinnt je Karte, lokale ungesendete Änderungen haben Vorrang. Einladungslink
                      #sitzung=CODE, Wiedereintritt nach Neuladen über sessionStorage. Lokal getestet mit Python-Nachbau
                      (scratchpad team_mock.py) – PHP selbst noch NICHT auf STRATO getestet (beim ersten Einsatz prüfen).
                      editoren-version.py: PRIVACY-Satz für diesen Editor angepasst (Team-Sitzung überträgt Daten).
                      PIN ist Pflicht (min. 6 Zeichen, genPin() schlägt 8 zufällige vor; Server prüft 'pin_fehlt').
                      Abgelaufene Sitzungen löscht team-sitzung.php bei jeder Nutzung (Marke _aufgeraeumt, max. stündlich).
                      Einwilligung: Pflicht-Häkchen #sOk im Sitzungsdialog (Starten und Beitreten), Text verweist auf
                      datenschutz.html#team-sitzungen. Datenschutzerklärung seit 03.10.2026 mit Abschnitt „7. Coaching-Werkzeuge
                      und Team-Sitzungen“ (Rechtsgrundlage Einwilligung Art. 6 Abs. 1 lit. a, Widerruf = Sitzung verlassen),
                      folgende Abschnitte 8–11; auf Peters Wunsch in generator/inhalte/datenschutz.html eingefügt (Sicherung
                      editor-backups/2026-10-03_10-24-30_datenschutz).
kartenarbeit-editor.html (seit 03.10.2026) Kartenarbeit mit Motiv- und Impulskarten (Peters Auftrag nannte metacards.net und
                      Coachingspace – keine Inhalte/Bilder von dort übernommen). *.kartenarbeit.json, format 'mc4y-kartenarbeit'.
                      Board wie Systembrett (Runden = doc.scenes mit Gettern via attach(), Kopie „+ Runde“ für Perspektivwechsel),
                      Karten-Knoten {deck, card, x, y (Mitte), w, down, mark, label, labelPos; freie Karte deck '_frei' + text/head/color}.
                      Mitgelieferte Sets (DECKS): 54 eigene Motivzeichnungen (karten_motive: MOTIFS, Koordinaten 100 × 75, motifKit mit
                      Zuschnitt clipPoly/clipLine, nur flache Grundformen), Textsets Fragen zum Perspektivwechsel, Rollen und Blickwinkel,
                      Werte, Stärken/Ressourcen (Gruppen i/a), Gefühle (Gruppen p/n), Bedürfnisse, Satzanfänge – Karten-ids = slug(Text),
                      NICHT umformulieren ohne Rücksicht auf gespeicherte Boards. Eigene Sets (Bild- oder Textkarten, Bilder per Canvas auf
                      Format zugeschnitten, max. 1000 px, JPEG als data-URL) in IndexedDB 'mc4y-kartenarbeit' (Store decks; Store state =
                      Zwischenstand, NICHT localStorage wegen Bildgröße) und als Kopie in doc.decks jeder Board-Datei (prepareDecks beim
                      Speichern); Export/Import *.kartenset.json (format 'mc4y-kartenset'). Bilder aufs Board ziehen = Set „Bilder dieses
                      Boards“ (local). Undo-Schritte ohne doc.decks (snapshot). Ziehen/zufällig (verdeckt)/3-5-7 verdeckt/alle offen,
                      Umdrehen F, Markieren M, „Nicht markierte entfernen“, Großansicht (Doppelklick, lastDown wegen Pointer-Capture),
                      Legevorlagen LAYOUTS (Bereiche; Bereich verschieben nimmt Karten darin mit), Verbindungen, Kartenprotokoll CSV,
                      Druckbogen eines Sets (iframe-Druck). Export-Teil aus ressourcengarten-editor, im Bauskript um Grundform 'img'
                      erweitert (bounds/toSVG mit Blob-URLs am Bildschirm, PPTX p:pic, ODP draw:image, VSDX Foreign/Bitmap, PDF DCTDecode
                      XObject, PNG über Blob-URL). Erstmals aus Vorlage + Motivdatei per Bauskript (nur Sitzungs-Scratchpad) erzeugt;
                      künftige Änderungen direkt in kartenarbeit-editor.html. Geprüft: PPTX/ODP in PowerPoint (82 Formen inkl. 2 Bilder), PDF (PyMuPDF), VSDX-XML, SVG, PNG,
                      IndexedDB-Rundlauf, Datei-Rundlauf, Ziehen aus der Liste mit der Maus.
zielbild-editor.html  (seit 03.10.2026) Zielbild / Vision Board / Moodboard (Peters Auftrag nannte metacards.net – nichts
                      übernommen). *.zielbild.json, format 'mc4y-zielbild'. Aus kartenarbeit-editor abgeleitet (Motive MOTIFS und
                      Export-Teil mit Bildern von dort; Bauskript ergänzt Drehwinkel p.rot für Text und Bild in bounds/toSVG/PPTX
                      a:xfrm rot/ODP draw:transform (odpGeo)/VSDX Angle/PDF Tm bzw. cm – in PowerPoint und PDF geprüft).
                      doc.boards (mehrere Zielbilder, Getter items/frames via attach), Board {fmt FORMATS a3q|a3h|b169|handy|quadrat|
                      frei, bg, tex punkte|linien|kork}; Leinwand = Rechteck ab (0,0). doc.images = Bildersammlung der Datei {src JPEG
                      data-URL max. 1400 px, w, h} – nicht in Undo-Schritten. Elemente doc.items (Reihenfolge = Ebenen) mit type
                      img|motif (w, frame ohne|rand|polaroid, shadow, tape, caption), note (w, h, color, text, fs, align, bold),
                      text (fs, bold, color, bg, align), sticker (kind aus STICKERS = 22 eigene Zeichnungen, size, color, text bei
                      blase/banner), swatch (size, color, label); alle mit x, y (Mitte) und rot. Zeichnen achsenparallel, dann
                      rotatePrims() (Rechteck/Ellipse → Polygon, Text/Bild → rot). Griffe rh: (drehen, 5°-Raster) und sh: (Größe in
                      lokalen Achsen). Bereiche (frames) nehmen beim Verschieben Elemente mit. Vorlagen TEMPLATES (Lebensbereiche,
                      Heute–unterwegs–in drei Jahren, Mein Jahr, Ich in der Mitte, Beruflich–privat, Moodboard), „Farben aus dem
                      Bild“ (paletteFromImage), Einfügen per Strg+V (Bild oder Text), Zwischenstand in IndexedDB 'mc4y-zielbild'.
inneres-team-editor.html (seit 03.10.2026) Inneres Team nach Friedemann Schulz von Thun (Modell als Idee genutzt, Texte/Vorschläge
                      eigene Formulierungen; keine eingetragene Marke „Inneres Team“ gefunden – Namensnennung des Urhebers beibehalten).
                      *.inneres-team.json, format 'mc4y-inneres-team'. Export-Teil aus zielbild-editor. Umriss doc.settings.outline
                      kopf|oval|ohne (outlinePts), Phasen doc.scenes (Getter members/links/notes/frames), Mitglied {name, msg = Kernbotschaft
                      in Sprechblase, need = Anliegen, note, role leitung|haupt|rand|aussen, size 1–5 = Einfluss (Figurgröße SCALE, Blase
                      gestrichelt/leise bzw. fett/laut), color, bx/by = Blasenlage relativ, null = automatisch über der Figur}. Blase eigenes
                      Ziehziel 'b:<id>'. Beziehungen LINKS bund|gegen|spannung|einfluss, Legende, Ergebnis-Box doc.protocol.ergebnis (+ schritte)
                      unter dem Bild (doc.res). Protokoll drucken (alle Phasen mit Bild + Tabelle), CSV. Vorschläge SUGGEST (16 typische Stimmen).
                      Geprüft: PPTX/ODP in PowerPoint (97 Formen), PDF, VSDX-XML, PNG, Ziehen/Blase/Beziehung mit der Maus.
traeumer-macher-kritiker-editor.html (seit 03.10.2026) Walt-Disney-Strategie nach Robert Dilts – Name „Disney“ bewusst NICHT im
                      Datei-/Werkzeugnamen, nur beschreibend mit Markenhinweis (Disney Enterprises, Inc.) in Hilfe, Seitenleiste, Protokoll.
                      *.tmk.json, format 'mc4y-tmk'. Export-Teil aus zielbild-editor. Positionen = Bereiche doc.zones {kind aus KINDS:
                      traeumer|macher|kritiker|neutral (CORE) + Vorschläge kunde|mentor|bewahrer|foerderer|zukunft|entscheider|eigen,
                      label, sub, q = Leitfragen (eigene Formulierungen), color, x, y (Mitte), w, h}; Symbole drawIcon (eigene Grundformen).
                      Notizen doc.notes {x, y, text, round, color}; Zugehörigkeit nur geometrisch (zoneAt). Aktive Position doc.active,
                      Weg doc.path (ids) als nummerierte Bézier-Pfeile (drawPath), Runde doc.round (+1 bei jedem erneuten Eintritt in den
                      Träumer). Leiste #posbar zum Wechseln, Taste N = Notiz in aktiver Position (freeSlot), Zeit je Position (timer, nicht
                      gespeichert). Anordnungen arrange(dreieck|reihe|kreis) – Notizen wandern mit. Ergebnis/Schritte-Box, Protokoll, CSV.
                      Geprüft: PPTX/ODP in PowerPoint (163 Formen), PDF, VSDX-XML, PNG, Bedienung (Wechsel, N, Timer, Runden).
ikigai-editor.html    (seit 13.10.2026, Peters Auftrag „Baue mir einen Editor für das IKIGAI-Modell“) Eigenständiges Coaching-Werkzeug (NICHT Teil
                      der Website, nie eigenmächtig hochladen). *.ikigai.json, format 'mc4y-ikigai' version 1. Westliches Vier-Kreise-Bild (Hinweis in
                      Hilfe/Protokoll: Ikigai = japanischer Begriff; Kamiya 1966, Mogi „Freude an kleinen Dingen“; Schaubild nach Andrés Zuzunaga, 2014 von
                      Marc Winn mit Ikigai verbunden; Leitfragen eigene Formulierungen; Reflexionsmodell, kein Diagnoseinstrument). Markenrecherche
                      13.10.2026 (eine Websuche): keine Ikigai-Marke für Coaching gefunden, nicht ausgeschlossen. Kreise CK = L (liebe, oben), K (kann,
                      links), W (Welt braucht, rechts), B (bezahlt, unten), DIR/CCOL (Nachbarfarben 90° auseinander → klare Mischfarben). Bereichsschlüssel
                      m = Buchstaben in Reihenfolge LKWB (13 geometrisch mögliche: 4 einzeln, PAIRS LK Leidenschaft/LW Mission/WB Berufung/KB Beruf,
                      TRIPLES, CENTER LKWB; LB/KW allein gibt es nicht – ideaKey ergänzt den stärkeren Nachbarkreis), F = Kasten „Kleine Freuden im
                      Alltag“ (rechts), P = „Noch offen (Parkplatz)“ (links), '' = außerhalb. Kärtchen {id, text, m, u, v (Lage in Einheiten des
                      Radius R → Größe änderbar), kind eintrag|idee, note, color, star, sc {L,K,W,B} 0–5}; Zugehörigkeit = regionAt(Mittelpunkt) beim
                      Ablegen, sonst explizit m (placeIn). geo() zwischengespeichert: Kreise als 144-Ecke, Schnittflächen per Sutherland–Hodgman
                      (clipPoly) und Malreihenfolge 1er → 2er → 3er → Mitte (Füllfarben fillOf = 28 % deckende Schichten, Mitte gold), Raster-Stichproben
                      je Bereich (samples, anchors), Beschriftungen mit freizuhaltenden Rechtecken (keep; Dreier-Texte nicht). freeSpot sucht den
                      nächsten Platz ganz im Bereich ohne Überdeckung; ohne perfekten Platz lieber über den Rand ragen als Kärtchen verdecken.
                      reflow() nach Größe/Überlappung (alle Bilder), tidy() = „ordnen“. Vorlagen TPL person|orga (Organisation/Team als Einstieg in
                      Vision/Mission im 8EM) mit circles {label, short, sub, q}, names, asks (Weiterfragen je Bereich). Bilder doc.scenes (z. B. Heute /
                      In drei Jahren; „＋ Bild“ = Kopie mit gleichen Kärtchen-ids, sceneDiff neu/verschoben/entfällt), Ikigai-Check doc.check {ist, soll}
                      0–10 (Bild + Zusammenfassung „größter Abstand“), doc.satz (Ikigai-Satz), doc.steps (eine Zeile je Schritt). Bedienung: Sammeln
                      links (Enter, Leitfragen-Klick), Palette ziehen/klicken, Doppelklick = neues Kärtchen bzw. Text im Kärtchen (#inEd; Enter/Tab/Esc),
                      Doppelklick selbst erkannt (lastDown) – das dblclick des Browsers ging verloren, weil der erste Klick neu zeichnet; Ziehen mit
                      Bereichsschild (#dragTag), Einrasten (Alt = genau dort), Rechtsklick-Menüs, große Rückgängig-Pfeile + Verlauf, „⛶ Bild groß“
                      (localStorage 'mc4y-ikigai-layout'), Zwischenstand IndexedDB 'mc4y-ikigai'. Export-Teil (bounds … buildPNG) 1:1 aus
                      traeumer-macher-kritiker-editor.html (Bauskript nur im Scratchpad; Änderungen jetzt direkt in der Datei). Protokoll (protocolHtml:
                      je Bild Bild + Bereiche zweispaltig + Veränderungen, Check, Ideen, Satz, Schritte), CSV. Geprüft im Browser: 72 Darstellungen
                      (Größe × Überlappung × Schalter × 2 Bilder) ohne NaN, Zugehörigkeit nach reflow, alle Panels, Vorlage Organisation, leeres Modell,
                      normalize idempotent, Datei-Rundlauf, Bilder unabhängig + Rückgängig; PPTX/ODP/VSDX-XML wohlgeformt (je 206 Formen), PDF im
                      Chrome-Betrachter, SVG/PNG; mit der Maus: Eintragen, Ziehen in Schnittmenge, Doppelklick + Tab, Rechtsklick → Bereich, Pfeile
                      + Verlauf, Schieber, Palette auf das Bild ziehen; Tasten N/F2/Pfeile/Strg+D/Entf. PowerPoint selbst nicht geöffnet.
bodenanker-editor.html (seit 13.10.2026, Peters Auftrag „alle Werkzeuge … ohne Werkzeug machbar und Empfehlungen bauen“) Bodenanker-Brett
                      (Positionen im Raum), *.bodenanker.json, format 'mc4y-bodenanker'. Aus traeumer-macher-kritiker-editor.html abgeleitet
                      (Bauskript nur im Scratchpad; Änderungen direkt in der Datei): FORMATS Wahrnehmungspositionen (Ich/Du/Beobachter/Meta),
                      Drama-Dreieck (Verfolger/Opfer/Retter + Beobachter, Ausstieg), Tetralemma im Raum (fünf Positionen), Zeitlinie am Boden,
                      eigene Positionen; KINDS/LAYPOS/coreZones, arrange(kind, noUndo), switchFormat (Notizen bleiben), weitere Positionen
                      anderer Formate links dazuholen; aktive Position, Weg, Runden, Zeit je Position, Protokoll, CSV wie im TMK-Editor.
WERKZEUGE MIT GEMEINSAMEM BOARD-KERN (seit 13.10.2026, gleicher Auftrag): logische-ebenen-editor.html, u-prozess-editor.html,
                      geschaeftsmodell-editor.html, riemann-thomann-editor.html, veraenderung-editor.html, wirkungsgefuege-editor.html,
                      seit 13.10.2026 abends auch stakeholder-editor.html (EAM-Werkzeuge, siehe unten) – Hinweis „SECHS“ unten gilt für alle sieben.
                      QUELLEN in werkzeug-kern/ (nicht hochladen): kern.html + kern_a.js (Hilfsfunktionen) + mod_<werkzeug>.js (const M) +
                      kern_b.js (Datenmodell, freeSpot/placeIn/reflow/tidy, Zeichnen) + Export-Teil (bounds … buildPNG, beim Bauen aus
                      traeumer-macher-kritiker-editor.html gelesen) + kern_c.js (Panels, Bilder, Rückgängig mit Verlauf, Dateien, Protokoll,
                      CSV, Ereignisse); module.json ordnet Datei ↔ Modul, Hilfe (mod_*.help.html), Titel, CSS, Dialoge zu. ÄNDERUNGEN AN DIESEN
                      SECHS WERKZEUGEN NUR IN werkzeug-kern/, dann `py werkzeug-kern/bauen.py alle --force` (ruft editoren-version.py auf) –
                      direkte Änderungen an den HTML-Dateien gingen beim nächsten Bauen verloren. Kern abgeleitet vom Ikigai-Editor: Kärtchen
                      {id, kind, text, m, x, y, note, color, star, …} frei auf der Fläche, Bereich m aus der Lage; Bilder doc.scenes [{id, name,
                      items, links, …}] („＋ Bild“ = Kopie, sceneDiff), names/asks/q je Bereich aus Vorlage M.TPL, settings, result/steps; eigene
                      Doppelklick-Erkennung (lastDown), „⛶ Bild groß“, IndexedDB-Zwischenstand je Werkzeug (M.db). Wichtige Haken in M: REG/
                      COLLECT (dürfen Getter sein), geo() → {regions (rect oder pts), labels (pushLabel, keep = Kärtchen weichen aus), extent,
                      anchors}, regionAt, drawBack/drawMid/drawFront/drawItem/itemGeo, cardStyle {fill, stroke, dash, tag}, beforeBuild,
                      afterMove/duringMove/afterNew, onPointerDown/Move/Up, onDouble, onKey, onEscape, selectable/selPanel/deleteSel, cardExtra/
                      regionExtra/docSections/settingsHtml/evalExtra, setField (eigene Felder; Rückgabe 'geo' = Geometrie neu), cardMenu/
                      areaMenu/ctxMenu, actions, protocolScene/protocolExtra/protoPrefix/protoSkip, csvCols/csvExtra, normItem/normScene/
                      normLink/normalize, example, noRegTable/noTidy/noGaps. Kern-Rechtsklick hat „Art“ (kindSel), wenn ein Werkzeug mehrere
                      Kärtchenarten hat. Prüfskript (Scratchpad, in localStorage 'kt' der Vorschau abgelegt): Beispiel laden, alle Bilder ×
                      alle Häkchen-Einstellungen ohne NaN, PPTX/ODP/VSDX-XML wohlgeformt (52 Dateien je Werkzeug), PDF ohne NaN, PNG, alle
                      Panels, Protokoll, CSV, leere Vorlagen, normalize idempotent – alle sechs fehlerfrei; Bild jeweils angesehen.
                      PowerPoint selbst nicht geöffnet (Export-Teil unverändert aus dem TMK-Editor, dort in PowerPoint geprüft).
                      • logische-ebenen-editor.html: Logische Ebenen nach Robert Dilts (angelehnt an Bateson), format 'mc4y-logische-ebenen',
                        *.ebenen.json. Sechs Ebenen als Pyramide (LV oben → unten), Vorlagen person|orga (Brücke zum 8EM in der Hilfe), Arten
                        Ressource/Bremse, je Bild scene.st (Stimmigkeit ok|teils|nein), scene.prob (Thema zeigt sich), scene.hebel; Pfeil
                        „Veränderungen oben wirken meist nach unten“. Mit der Maus geprüft (Doppelklick, Ziehen, Strg+Z, Rechtsklick).
                      • u-prozess-editor.html: Theorie U nach C. Otto Scharmer, format 'mc4y-u-prozess', *.u-prozess.json. Vorlagen sieben
                        (Herunterladen, Hinsehen, Hinspüren, Anwesend werden, Verdichten, Erproben, In die Welt bringen; Gesten dazwischen,
                        „Offenes Denken/Herz/Wille“ in der Mitte) und fuenf (Bezeichnungen wie in den Theorie-U-Fragebögen: Gemeinsam beginnen,
                        Gemeinsam wahrnehmen, Innehalten, Gemeinsam erproben, In die Welt bringen); Vorlagenwechsel ordnet Kärtchen, aktive und
                        abgeschlossene Stationen über U_MAP zu und ordnet neu (tidy). Je Bild scene.aktiv/scene.done, Leiste ◀ ▶ (uStep), Arten
                        innere Stimme (it.voice urteil|zynismus|angst|sonst – Namen wie in fragebogen-u-*) und Idee/Prototyp.
                      • geschaeftsmodell-editor.html: „Geschäftsmodell-Board“, format 'mc4y-geschaeftsmodell', *.geschaeftsmodell.json. BMC-PRÜFUNG
                        (13.10.2026): Das Business Model Canvas steht unter CC BY-SA 3.0 (Strategyzer AG; Strategyzer bittet bei Software-Einbindung
                        um Kontakt). Nur CC BY-SA 4.0 ist einseitig GPLv3-kompatibel erklärt, 3.0 nicht → das Canvas selbst NICHT unter GPL
                        übernehmbar. Ideen und übliche Bausteine eines Geschäftsmodells sind frei; daher eigene Gestaltung: Zweck und Vision oben,
                        Wertfluss als Pfeilkette Netzwerk und Partner → Fähigkeiten und Ressourcen → Angebot und Leistung → Nutzen für die Kunden →
                        Wege und Beziehung → Kundengruppen und Anliegen, darunter Menschen und Kultur („Dient es den Menschen?“), Kosten | Erlöse,
                        Wirkung; KEINE BMC-Anordnung (kein Neun-Felder-Raster mit Nutzenversprechen in der Mitte), keine BMC-Feldnamen, kein
                        „Canvas“ im Namen. Hilfe: „kein Nachbau des Business Model Canvas der Strategyzer AG“. Vorlagen unternehmen|person
                        (Selbstständigkeit). Art „Annahme“ mit Prüfstand it.ast (''|pruefen|bestaetigt|widerlegt) und it.risk (kritisch),
                        Fragen zur Stimmigkeit (z. B. Nutzen ohne Kundengruppe), Annahmen-Tabelle im Protokoll.
                      • riemann-thomann-editor.html: Riemann-Thomann-Kreuz, format 'mc4y-riemann-thomann', *.riemann-thomann.json. Quadranten
                        nd|dd|nw|dw (Nähe links, Distanz rechts, Dauer oben, Wechsel unten), Pole mit Stichworten (optional Übertreibungen), Ringe.
                        Personen-Markierungen = freie Arten selbst|fremd|wunsch|druck (text = Name; gleicher Name = eine Person, Linien vom
                        Selbstbild), Lage in Worten und Prozent (rtDesc/rtPct, RT_R 420), Vorlagen person|team. Keine klinischen Riemann-Begriffe.
                      • veraenderung-editor.html: Kraftfeldanalyse nach Kurt Lewin (Vorlage kraft: Ziel, treibende/hemmende Kräfte mit Stärke it.st
                        1–5, automatisch gereiht in beforeBuild – Reihenfolge = Lage y, Pfeillänge = Stärke, Summen, Maßnahmen; Tasten 1–5,
                        „Nach Stärke ordnen“) und Veränderungskurve in sieben Phasen (Vorlage kurve: Schock … Integration, u. a. nach Streich;
                        Personen/Gruppen rasten auf der Kurve ein – chSnap/chCurveY –, Phase aus der Lage, „Was hilft“ je Phase). Format
                        'mc4y-veraenderung', *.veraenderung.json; Vorlagenwechsel mit Rückfrage (Kärtchen liegen danach neben dem Bild);
                        „Beispiel“ lädt das Beispiel der gerade gewählten Vorlage.
                      • wirkungsgefuege-editor.html: Wirkungsgefüge und Einflussmatrix nach Frederic Vester („Sensitivitätsmodell“ bewusst nicht
                        verwendet), format 'mc4y-wirkungsgefuege', *.wirkungsgefuege.json. Keine Bereiche; Faktoren = Kärtchen, Wirkungen = scene.links
                        {a, b, pol '+'|'-', w 1–3, delay, label}; Verbinden über den goldenen Griff (ziehen oder anklicken, dann Ziel), Rechtsklick,
                        Dialog #wgDlg „Einflussmatrix“ (Klick 0 → +1 … +3 → –1 … –3, Rechtsklick rückwärts, ein Rückgängig-Schritt je Klick).
                        Aktiv-/Passivsumme, Q, Rollen aktiv|kritisch|reaktiv|puffernd (Teilung am Mittelwert), Rollenbild unter dem Netz,
                        Rückkopplungen (Kreise bis Länge 7, höchstens 60, Vorzeichenprodukt: verstärkend/ausgleichend, Hervorheben per Klick).
                        Mit der Maus geprüft: Griff ziehen, Matrix klicken/Rechtsklick, Esc, Strg+Z.
                      • stakeholder-editor.html (13.10.2026, EAM-Werkzeug Nr. 14): Stakeholder-Landkarte, Modul mod_sh.js/mod_sh.help.html, format
                        'mc4y-stakeholder', *.stakeholder.json. Waagerecht Interesse/Betroffenheit (rechts hoch), senkrecht Einfluss (oben hoch),
                        Felder hh/hl/lh/ll mit Strategie (SH_STRAT; angelehnt an das verbreitete Einfluss-Interesse-Raster nach Mendelow, eigene
                        Bezeichnungen), Vorlagen projekt|organisation. Stakeholder = freie Arten = Haltung (pro, neutral, krit, contra, offen),
                        it.grp = Gruppe, Felder SH_F rolle/anliegen/sorge/massn/verantw (Freitext – eigenständiges Werkzeug, nicht 8EM), Einfluss/
                        Interesse in Prozent aus der Lage, Bilder „Heute“/„Angestrebt“ mit Pfeilen der Veränderung. KERN-ÄNDERUNG dafür: M.textFields
                        (Felder, bei deren Eingabe kern_c.js das Eigenschaften-Panel NICHT neu zeichnet – sonst ging der Fokus beim Tippen verloren).
                        Geprüft wie die übrigen (KT-Prüfskript: 2 Bilder, 18 Varianten, 52 XML-Dateien, PDF, Panels, Protokoll, CSV), alle sieben
                        Kern-Werkzeuge danach neu gebaut und ohne Fehler/NaN geladen.
                      Seit 15.10.2026 abends: `py werkzeug-kern/bauen.py … --ohne-version` (Zwischenstand ohne Stempel) und Stammdaten-Unterstützung im Kern
                      (M.sd, siehe stammdaten-editor.html „NACHTRAG 15.10.2026 abends“) für Stakeholder, Riemann-Thomann und Veränderungskurve.
3D-WERKZEUGE MIT GEMEINSAMEM 3D-KERN (seit 13./14.10.2026, Peters Auftrag „baue die von Dir vorgeschlagenen Tools als 3D-Variante und stelle
                      das 3D im Namen des Tools voran, wie beim Systembrett“; die 2D-Werkzeuge bleiben unverändert daneben bestehen):
                      3d-bodenanker-editor.html, 3d-traeumer-macher-kritiker-editor.html, 3d-logische-ebenen-editor.html, 3d-u-prozess-editor.html,
                      3d-soziales-atom-editor.html, 3d-inneres-team-editor.html, 3d-lebenslinie-editor.html (Namen in editoren-version.py NAMES).
                      Eigenständige Coaching-Werkzeuge, NICHT Teil der Website, nie eigenmächtig hochladen. QUELLEN in werkzeug-kern-3d/ (nicht
                      hochladen): kern3d.html (Seite, CSS, Kopfzeile, Druck-Dialog, gemeinsame Hilfe; Platzhalter {{…}}) + k3_a.js + FIGUREN-TEIL
                      aus 3d-systembrett-editor.html (von „/* ---------- Figuren, Symbole, Linien“ bis „/* ---------- Datenmodell“: SYMDEF, EXTRA,
                      POSES, SEAT, LINKS, VIEW_KEYS …) + k3_b.js (Matrizen, Geo/Grundkörper inkl. gSlab/ribbon3, WebGL-Shader, Texte als Texturen
                      textTex, mkQuad) + MODELL-TEIL aus dem 3D-Systembrett (von „/* ---------- 3D-Modelle“ bis zum Kamera-Abschnitt: buildModels,
                      humanModel, buildExtra, XM, partColor) + mod3_*.js (const M) + k3_c1.js … k3_c4.js (Datenmodell, Bühne, Kamera, Darstellung,
                      Bedienung, Leisten, Panels, Dateien, PNG, Bilderbogen, Start). module3d.json ordnet Datei ↔ Module/Hilfe/Titel/Texte zu.
                      ÄNDERUNGEN NUR IN werkzeug-kern-3d/, dann `py werkzeug-kern-3d/bauen3d.py alle --force` (ruft editoren-version.py auf;
                      --ohne-version für Zwischenstände). Das 3D-Systembrett selbst wird nur GELESEN (nicht aus dem Kern gebaut) – neue Symbole
                      dort erscheinen beim nächsten Bau auch in den sieben Werkzeugen; die Marken-Kommentare dort NICHT umbenennen (bauen3d.py
                      bricht sonst mit Meldung ab). Kern = Systembrett-Technik (eigenes WebGL 2, Schatten, Picking, Bilder mit Animation, Vergleich,
                      Spuren, Sicht einer Figur, Blickfeld, Verbindungen mit Ziehpunkten, Stapeln, Rückgängig 150 Schritte mit großen Pfeilen,
                      Zwischenstand localStorage M.key+'-entwurf', Layout M.key+'-layout', Palette mit Suche/Gruppen/Bildchen, PNG, Bilderbogen
                      zum Drucken/PDF) plus: Bühne M.stage() → {parts [{g, color, mat, alpha, shadow, pickId}], decals, hw, hd, ground(x, z)
                      (Höhenfeld, rayGround tastet ab), table, rand, grid, edge, bg} (zwischengespeichert über M.stageKey()); Texte auf Flächen als
                      Decals {spec, kind floor|front, m, light, alpha} (Boden-Texte drehen sich zur Kamera); Elementarten über M.kinds (SYMDEF-
                      Ergänzungen mit model, face, see, eyeH, seat/seatFor, noStack/noRot/noScale/noMove/noDelete/noLink/noColor, ph, init …);
                      Werkzeug-Leiste #modbar (M.modbar, placeBars hält Ansichtsleiste, Werkzeugleiste und Legende überlappungsfrei); Haken
                      (Auswahl im Kopf von k3_c1.js, vollständig nur in den mod3_*.js ablesbar: overlay, labels2d, nodePanel/altPanel/docPanel, stageTap, dblGround,
                      lift, followers, dynamic, freeView, act 'm:…', onField 'm_…', sheet, from2D/to2D …). Ausgabe nur PNG und Bilderbogen;
                      Vektorexporte (PPTX/ODP/VSDX/PDF/SVG) über „Als 2D-… speichern“ und das 2D-Werkzeug. Alle Beispiele fiktiv.
                      • 3d-bodenanker-editor.html: format 'mc4y-bodenanker-3d', *.bodenanker3d.json (mod3_raum_bodenanker.js + mod3_raum.js).
                        Bodenanker = Bodenblätter 'a_<Position>' mit Text (Formate wie 2D: Wahrnehmungspositionen, Drama-Dreieck, Tetralemma,
                        Zeitlinie, eigene), Notizkarten 'karte', Gehfigur (n.walker) betritt Positionen (enterPos: Weg doc.path, Runden, Zeit je
                        Position), Weg als nummerierte Pfeile, Anordnung Standard/Reihe/Kreis, Formatwechsel. 2D-Austausch mit bodenanker-editor
                        (format 'mc4y-bodenanker', 1 cm = 20 Einheiten; Notizen landen in der Inhaltsfläche ihrer Position).
                      • 3d-traeumer-macher-kritiker-editor.html: format 'mc4y-tmk-3d', *.tmk3d.json (mod3_raum_tmk.js + mod3_raum.js), gleiche
                        Technik, Positionen Träumer/Macher/Kritiker/Neutral + Vorschläge, eigene 3D-Sinnbilder. „Disney“ nur beschreibend mit
                        Markenhinweis (Hilfe, Fußnote, Bilderbogen), nie im Namen. 2D-Austausch mit traeumer-macher-kritiker-editor ('mc4y-tmk').
                      • 3d-logische-ebenen-editor.html: format 'mc4y-logische-ebenen-3d', *.ebenen3d.json (mod3_stationen.js + mod3_stn_ebenen.js): sechs Ebenen
                        als begehbare Treppe (Stationen = Plattformen mit Stirn-/Bodentexten), Kärtchen als stehende Tafeln 'k_<Art>' (Ressource/
                        Bremse), Gehfigur, Stimmigkeit je Bild, Vorlagen person|orga. 2D-Austausch mit logische-ebenen-editor (Kern-Format: items mit
                        Bereich m; Lage rect2d in der Station).
                      • 3d-u-prozess-editor.html: format 'mc4y-u-prozess-3d', *.u-prozess3d.json (mod3_stationen.js + mod3_stn_u.js): U als Weg mit Rampen hinab und
                        hinauf, Vorlagen sieben|fuenf (Wechsel ordnet über U_MAP3 zu), aktive/abgeschlossene Station je Bild, innere Stimmen (voice),
                        Ideen/Prototypen. 2D-Austausch mit u-prozess-editor.
                      • 3d-soziales-atom-editor.html: format 'mc4y-soziales-atom-3d', *.sozialatom3d.json (mod3_atom.js): „Ich“ in der Mitte, Nähe-Ringe (settings rings),
                        Menschen/Symbole/Tiere aus dem Systembrett, Linien Verbindung/doppelt/Strich-Varianten, Abstand zum Ich in den Eigenschaften.
                        2D-Austausch mit soziales-atom-editor (1 cm = 5 Einheiten; 3D-Eigenheiten als s3d/scale3d/rot3d/color3d/dead3d mitgegeben,
                        Rundlauf ohne Abweichungen).
                      • 3d-inneres-team-editor.html: format 'mc4y-inneres-team-3d', *.inneres-team3d.json (mod3_team.js): Mitglieder als Figuren bzw. Emotionsgesichter mit
                        Kernbotschaft (Sprechblase), Anliegen, Rolle (Leitung auf dem Podest, Haupt-, Rand-, Außenmitglied), Einfluss = Größe, „Kopf“
                        als durchsichtige Kuppel, Beziehungen bund/gegen/spannung/einfluss, Phasen als Bilder, Ergebnis/Schritte (doc.protocol),
                        16 Vorschläge. 2D-Austausch mit inneres-team-editor (1 cm = 8 Einheiten; Notizen/Bereiche bleiben erhalten).
                      • 3d-lebenslinie-editor.html: format 'mc4y-lebenslinie-3d', *.lebenslinie3d.json (mod3_leben.js): Daten wie 2D (lanes, periods, events, moods,
                        settings start/end/birth/scale …), Bühne = Zeitachse mit Jahren/Alter, Bänder je Lebensbereich mit Zeitraum-Platten,
                        Ereignisschilder, Gefühlskurve als Band mit Kugeln; Figur geht per Regler/◀ ▶ durch die Zeit, schaut zurück/nach vorn
                        (Sicht = Blick über die Jahre); Doppelklick legt je nach Lage Ereignis/Zeitraum/Gefühl an. 2D-Austausch mit lebenslinie-
                        editor (Planung, Notizen, Legende bleiben erhalten; Rundlauf identisch).
                      Geprüft im Browser (Prüfskripte in localStorage 'kt3' bzw. 'kt3pair' der Vorschau): je Werkzeug Beispiel, normalize idempotent,
                      alle Ansichten, Sichten, Bilder, Vergleich, Kopie/Rückgängig/Wiederholen, PNG, Bilderbogen, leeres Modell ohne Fehler/NaN;
                      PAARTEST je Werkzeug: Beispiel des 2D-Werkzeugs (iframe) → 3D → zurück ins 2D-Werkzeug geladen, gleiche Anzahlen, 2D-SVG
                      ohne NaN (alle sieben bestanden); Bedienung mit der Maus während der Entwicklung (Aufstellen, Ziehen, Positionen betreten,
                      Stationen, Regler). Nach dem Stempeln alle sieben geladen (Build, Logo, Lizenz vorhanden).
                      LEER-HINWEIS (18.10.2026, Build 11:39, Testfall 3D-06): „Das Brett ist noch leer“ (#empty) lag bei 800 px teils unter der Ansichts-
                      leiste. k3_c3.js placeBars ruft emptyPlace(vbBottom, vb) (#empty bottom = max(56, Abstand der Leiste unten + Leistenhöhe + 10));
                      im 3D-Systembrett emptyPlace() am Ende von renderViewBar und in resize(). Geprüft bei 800 × 700 ohne Überlappung.
lebenslinie-editor.html (seit 02.10.2026) Eigenständiges Coaching-Werkzeug: Timeline / Biografiearbeit (Peters Auftrag nannte
                      „Praxis Strehlau“ – dazu sind keine Konventionen bekannt, allgemein umgesetzt), *.lebenslinie.json,
                      format 'mc4y-lebenslinie'. Aus ressourcengarten-editor.html abgeleitet (Export-Teil identisch).
                      Zeitachse X(t) = (t − settings.start) · settings.scale (px/Jahr), Alter aus settings.birth. Lebensbereiche
                      doc.lanes, Zeiträume doc.periods {lane, fy, fm, ty, tm; ty=null = dauert an} als Balken (ziehen = verschieben/
                      Bereich wechseln, Griffe zh0/zh1 = Dauer), Ereignis-Karten doc.events {y, m, title, text, emo, val, dx, dy}
                      (Karte frei verschiebbar, Achsenpunkt 'ea:' ziehen = Datum; arrangeCards/placeFree stapeln ohne Überlappung),
                      Emotions-Marker doc.moods {y, m, emo, val} in der Gefühlskurve (Höhe = Stärke −5…+5), EMOS 11 Gefühle mit
                      Gesichtern (drawFace, nur Grundformen). Gefühlskurve verbindet Ereignisse mit Gefühl + Marker. Hinweis auf
                      Belastungsgrenzen der Biografiearbeit. Geprüft: PPTX/ODP in PowerPoint (307 Formen), VSDX-XML, PDF A3, PNG.
                      ZUKUNFT UND RÜCKWÄRTSPLANUNG (13.10.2026): doc.plan {goal, gy, gm, lane, steps [{title, dur Monate}]} – vom Ziel aus
                      rückwärts gerechnet (planDates); „In die Lebenslinie übernehmen“ (planApply) legt Schritte als gestrichelte Zeiträume
                      (plan:true) und das Ziel als goldene Karte an, färbt die Zukunft (settings.future, drawFuture), ersetzt frühere Planung
                      (planRemove); Planungsansicht (planView), Hinweis bei Schritten in der Vergangenheit; Zeiträume links der Achse abgeschnitten.
entscheidung-editor.html (seit 02.10.2026) Eigenständiges Coaching-Werkzeug: Tetralemma UND Entscheidungswaage in einer
                      Datei (Umschalter doc.mode tetra|waage), *.entscheidung.json, format 'mc4y-entscheidung'. Aufbau wie
                      lebensrad-editor (Formular links, Bild aus Werten, Export-Teil 1:1 aus ressourcengarten-editor).
                      Tetralemma (Varga von Kibéd/Sparrer, eigene Leitfragen): TPOS 5 Positionen je {label,text,score 0–10},
                      Raute + 5. Position gestrichelt außerhalb. Waage: KINDS zwei|proco|vier (Veränderungswaage, 4 Felder
                      über item.side/cat), Gewicht 1–5, Neigung ±15° nach Summen, Bauchgefühl gut[0..1], verdict() neutral
                      (±10 % = ausgeglichen), Hinweis bei Kopf/Bauch-Widerspruch. Klick ins Bild springt zum Eingabefeld.
                      CSV-Export beider Teile. Geprüft: PPTX/ODP in PowerPoint (98 Formen), VSDX-XML, PDF, PNG farbig/sw.
lebensrad-editor.html (seit 02.10.2026) Eigenständiges Coaching-Werkzeug (NICHT Teil der Website, nie hochladen):
                      Lebensrad/Rollenrad, *.lebensrad.json, format 'mc4y-lebensrad' version 1. Kein freier Zeichen-Editor,
                      sondern Diagramm aus Werten: doc.segments [{id,name,color,v[],note}] (3–16), doc.series (1–4 Wertereihen,
                      style flaeche|linie|gestrichelt, doc.active = mit der Maus gesetzte Reihe), max 10|5, style torte|netz,
                      colorMode bunt|reihe. Vorlagen PRESETS (Lebensrad 8/10, Rollenrad mit Zufriedenheit/Zeitanteil/Wichtigkeit,
                      vier Lebensbereiche nach Peseschkian, eigenes Rad). Klick/Ziehen ins Rad = Wert (Abstand zur Mitte; Ziehen
                      bleibt im Startbereich), Rechtsklick löscht; Tabelle links mit Notiz je Bereich. Auswertung nur beschreibend
                      (Ø, Spannweite, größte Unterschiede Reihe 2 − Reihe 1), optional ins Bild. Export-Teil (bounds/toSVG und
                      ZIP/PPTX/ODP/VSDX/PDF/SVG/PNG) 1:1 aus ressourcengarten-editor.html – Korrekturen dort nachziehen;
                      Text-Anker „end“ wird in buildPrims auf „start“ umgerechnet. Zusätzlich CSV-Export. Unter 980 px gestapelt.
                      Geprüft: PPTX/ODP in PowerPoint (79 Formen), VSDX-XML, PDF-Text, PNG Torte und Netz.
                      ZEITKUCHEN UND TEAMUHR (13.10.2026): style kuchen (je Wertereihe ein Kuchen aus Anteilen an der Summe, doc.unit UNITS
                      h|p|x, Auswertung = größte Verschiebungen in Prozentpunkten; buildKuchen/evalKuchen) und style uhr (Teamuhr, Phasen der
                      Teamentwicklung nach Bruce Tuckman; doc.markers = Einschätzungen je Person heute/gewünscht, doc.mActive, Klick setzt
                      „heute“, Umschalt+Klick „gewünscht“, Ziehen/Rechtsklick; Zeiger = Schwerpunkt; buildUhr/evalUhr, uhrDown). STYLE_OK.
                      Vorübergehende Marker-Lagen nur in einer lokalen Map (nicht in doc). „→“ nicht in PDF-Texten (WinAnsi).
                      Spaltenköpfe der Wertereihen (seit 21.10.2026) über `spaltenKurz(name)`: erstes Wort, ab 8 Zeichen auf ≤ 6 Zeichen bis zum letzten Mitlaut
                      + Punkt („Zufr.“, „Zeitan.“, „Wicht.“ / „Satisf.“, „Share“, „Import.“; vorher abgeschnitten „Satisfa“). Englische Kurzformen deutscher Daten
                      über zusatz in sprache/werkzeuge/lebensrad-editor.html.json („<1></1>Zufr.“ → „<1></1>Satisf.“ usw.) – bei neuen Vorlagen-Reihen ergänzen.
buchhaltung-editor.html (seit 14.10.2026, Peters Auftrag „Buchhaltungs-Tool für KMU, einfache und wahlweise doppelte Buchführung, Rechnungen
                      erfassen und finanzamtskonform stellen, private steuerrelevante Vorgänge getrennt, Berichte als PDF, v. a. quartalsweise
                      Voranmeldung“). Eigenständiges Werkzeug (NICHT Teil der Website, nie eigenmächtig hochladen). QUELLEN in buchhaltung-quellen/
                      (nicht hochladen): seite.html (Gerüst, Hilfe-Dialog helpDlg – Schließen oben = data-act helpX, unten closeHelp für den Stempel),
                      stil.css, b1_grund.js (Geld in Cent ct/eur, Datum, Ablagen OrdnerAblage = File System Access/OPFS, BrowserAblage = IndexedDB
                      'mc4y-buchhaltung' Store files, SpeicherAblage = Beispiel; inPapierkorb statt Löschen; ZIP schreiben/lesen; xlsxLesen; CSV),
                      b2_stamm.js (FALL Steuerfälle Eingang, UART Umsatzarten Ausgang, USTVA = Zeilen/Kz USt 1 A 2026, EUER = Zeilen/Kz Anlage EÜR 2025 –
                      per PyMuPDF aus dem amtlichen Vordruck ausgelesen –, AW Anlagearten, EST private Kategorien, KAT_DEFAULT Belegarten mit Kürzeln wie
                      in Peters Liste, KONTEN skr03/skr04 = eigene Auswahl „in Anlehnung“ mit Rollen bank/kasse/vst19/ust19/…), b3_daten.js (Modell,
                      aendern() = einziger Weg für Änderungen: Momentaufnahme je Datensatz, Vergleich, Sperrprüfung sperrVerstoss, Undo 80 Schritte,
                      Protokoll {n,t,akt,art,id,op,vor,nach,p,h} mit SHA-256-Kette; protokollPruefen = Kette + Abgleich Datensatz ↔ letzter Protokoll-
                      stand; belegWerte, awEff (≤250 Aufwand, ≤800 GWG, sonst AfA; Sammelposten je Jahr st.gwgWahl), ustPosten/ustvaBerechnen (Ist:
                      Zahlungseingang, Soll: Leistungsdatum, Storno im Zeitraum der Änderung; Vorsteuer nach max(Datum, „Rechnung erhalten am“);
                      Bemessungsgrundlage volle Euro abgeschnitten, Steuer 81/86 daraus), euerPosten (Zahlungsdatum, 10-Tage-Regel für USt-Zahlungen
                      über b.fuer, Bewirtung 70/30, Geschenke 50-€-Grenze), anlagenListe/afaPlan, buchungssaetze/kontenSalden/bilanzGuv, privatZusammen,
                      belegHinweise (u. a. ausländische Steuersätze, ausländische Anbieter mit deutscher USt – AUSL_ANBIETER ohne deutsche Firmen –,
                      Doppel nur bei gleicher bzw. fehlender Rechnungsnummer), reHinweise = Pflichtangaben § 14 Abs. 4 UStG), b4_pdf.js (eigene PDF-
                      Erzeugung: Helvetica/WinAnsi, Breiten über Canvas „Arial“, FlateDecode, JPEG; berichtHtml/berichtPdf aus EINEM Modell R; weiches
                      Trennzeichen in Tabellenköpfen; rechnungPdf nach DIN 5008 Form B mit Falz-/Lochmarken), b5_erechnung.js (xrechnungXml = XRechnung 3.0
                      CII, Gutschrift 381 mit positiven Beträgen, Kategorie O ohne USt-IdNr.; Lesen CII + UBL, pdfAnhangXml für ZUGFeRD/Factur-X,
                      belegAusERechnung), b6_berichte.js (15 Berichte BERICHTE), b7–b11 Oberfläche (Übersicht, Belege, Rechnungen, Kunden, Privat, Anlagen,
                      Buchungen nur bei doppelter Buchführung, Auswertungen, Einstellungen; Import von Excel/CSV mit Peters Spalten; Beispiel = fiktive
                      Firma „Muster Coaching & Beratung“, Adresse #beispiel). ÄNDERUNGEN NUR IN buchhaltung-quellen/, dann `py buchhaltung-quellen/bauen.py`
                      (ruft editoren-version.py auf; --ohne-version für Zwischenstände). Datenformat 'mc4y-buchhaltung' version 1, Ablageordner:
                      buchhaltung.json, Belege/<Jahr>/ (Dateinamenmuster ablageCfg.muster wie Peters Liste {NNN}_{DATUM}_{ART}_{PARTNER}_{PERSON}-{TEXT}-{RENR}),
                      Rechnungen/<Jahr>/ (PDF + XRechnung), Berichte/<Jahr>/, _Sicherungen (alle 15 Min., 40 Stück), _Papierkorb. Speichern nach 0,9 s;
                      Zähler `stand` verhindert, dass eine laufende Speicherung neuere Änderungen als gespeichert meldet; speichernSofort vor Ablagewechsel.
                      Festschreiben nur betriebliche Belege (privat bleibt änderbar); gestellte Rechnungen nur noch zahlungen/status/Dateien/Notiz änderbar.
                      Neu erfasster Beleg in angemeldetem/festgeschriebenem Zeitraum → eingang = heute (Vorsteuer im laufenden Zeitraum).
                      Kontenrahmenwechsel stellt eigene Buchungen über Rollen/AW/Belegarten um (kontenrahmenWechseln).
                      Geprüft im Browser: 209 Darstellungen (EÜR/doppelt × SKR03/04 × Ist/Soll × alle Reiter und Berichte, Kleinunternehmer, 375 px ohne
                      Querscrollen) ohne Fehler/NaN, Bilanz ausgeglichen; Abläufe Beleg erfassen, Sperre, Storno, Undo/Redo, Rechnung stellen + PDF/XML +
                      Zahlung + Stornorechnung, E-Rechnung einlesen, mehrere Steuersätze, OPFS-Ordner (Neuladen, Sicherung, Datenpaket-ZIP, Manipulation
                      an Kette UND nur am Datensatz erkannt, keine Fehlalarme nach 478 Protokolleinträgen); PDFs mit PyMuPDF geöffnet und angesehen; XML
                      wohlgeformt, Elementreihenfolge nach CII-Schema – ein KoSIT-Validator lief NICHT (Peter vor dem ersten Versand prüfen lassen).
                      Peters Listen 2025/2026 eingelesen (nur lokal im Vorschau-Browser, danach entfernt): 281 Belege, 1 Rechnung, Q4/2025-Vorsteuer stimmt
                      bis auf 5 Cent Rundung mit dem Jahresblatt überein; sein Blatt „Q4“ 2025 enthält 6 Belege weniger als das Jahresblatt; 16-%-Udemy-,
                      20-%- und 10-%-Belege sind ausländische USt (kein Vorsteuerabzug) – Peter informiert.
                      ELSTER-XML (14.10.2026, Peters Auftrag „Bau den Elster XML-Exporter ein“; Recherche: QuickSteuer/WISO/SteuerSparErklärung/smartsteuer
                      haben KEINE offene Importschnittstelle, Mein ELSTER aber einen offiziellen XML-Import für die UStVA, der nur vorbefüllt): b12_elster.js.
                      elsterXml(spec) = nur Block <Anmeldungssteuern xmlns="http://finkonsens.de/elster/elsteranmeldung/ustva/v<Jahr>" version="<Jahr>">
                      mit Erstellungsdatum, DatenLieferant (Name, Strasse, PLZ, Ort, Telefon, Email), Steuerfall/Umsatzsteuervoranmeldung (Jahr, Zeitraum
                      01–12 bzw. 41–44, Steuernummer 13-stellig, Kz aufsteigend; Bemessungsgrundlagen ganze Euro, Steuer mit Punkt und 2 Stellen; Kz 81/86/87/
                      89/93 ohne Steuerfeld, Kz 10 = berichtigt optional, Kz 39 Sondervorauszahlung, Kz 83 immer; Kz 09 und Kz 500 nie), Bytes ISO-8859-15
                      (iso885915). Steuernummer: firma.stnrElster (13 Ziffern) oder Umrechnung stnrElster(steuernr, firma.bundesland) mit Tabelle BUNDESLAENDER
                      (geprüft gegen die bekannten ELSTER-Testnummern der Länder, z. B. NW 133/8150/8159 → 5133081508159). Knopf „🧾 ELSTER-Datei“ im Bericht
                      Voranmeldung → elsterDialog (Vorschau der Kennzahlen, Fehler/Hinweise, Kz 10 vorbelegt, wenn der Zeitraum schon vermerkt ist) → elsterLos
                      (Download, bei Ordnerablage zusätzlich Berichte/<Jahr>/ELSTER_UStVA_<Zeitraum>.xml). Aufbau nach ELSTER-Hilfe „ustva_upload“ und
                      ELSTER-Anwenderforum (Namensraum-Jahr Pflicht seit 07/2023); offizielle ERiC-Schemadokumentation NICHT eingesehen – Peter testet den
                      ersten Upload selbst in Mein ELSTER (füllt nur vor, sendet nichts). Geprüft: wohlgeformt in allen Zeiträumen, Kz83 = Bericht, Umlaute/€.
                      ÜBERTRAGUNG AN STEUERPROGRAMME (14.10.2026, Peter hat Lexware QuickSteuer DELUXE – nicht „Plus“ –; QuickSteuer ist auf diesem
                      Rechner nicht als Programm registriert): b13_uebertragung.js – uebertragungsSchritte(jahr) = [{g, ort, text, wert („1234,56“, Bemessungs-
                      grundlagen USt-Erklärung volle Euro), art zahl|text|info, hinweis}] für Anlage EÜR (Formular-Reihenfolge, nicht abziehbare Teile vor dem
                      abziehbaren Wert, Summen als info), Umsatzsteuererklärung (nach Bedeutung mit Voranmeldungs-Kz, Hinweis auf andere Nummern in der
                      Jahreserklärung, Summe der vermerkten Vorauszahlungen) und Privat (je Person/Anlage; Spenden und § 35a einzeln mit Empfänger bzw. Art,
                      Betrag, Arbeitskosten; steuer/stb/sonst ausgelassen). Bericht „Übertragung an Steuerprogramme“ (rUebertragung) + Knopf „📤 Datei für den
                      Assistenten“ → *.uebertragung.json (format 'mc4y-uebertragung', bei Ordnerablage auch Berichte/<Jahr>/).
                      uebertragungs-assistent.py + Uebertragungs-Assistent.bat (Projektwurzel, NICHT hochladen; nur Python-Standardbibliothek: tkinter, ctypes;
                      ab 3.8, keine f-Strings): Fenster immer im Vordergrund mit aktuellem Wert und Liste; globale Taste (RegisterHotKey in eigenem Thread mit
                      Nachrichtenschleife, Standard F8, wählbar) tippt den Wert per SendInput (KEYEVENTF_UNICODE) in das aktive Feld eines anderen Programms,
                      optional vorher Strg+A, danach Tab, Dezimalpunkt statt Komma, Modus „einfügen“ über die Zwischenablage; tippt nie in das eigene Fenster,
                      wartet auf losgelassene Umschalt-/Strg-/Alt-Tasten; Protokoll <datei>.protokoll.txt (Zeit, Fenstertitel, Stelle, Wert); Einstellungen in
                      %APPDATA%\MC4Y\uebertragungs-assistent.json; lädt beim Start die neueste *.uebertragung.json aus Downloads (≤ 2 Tage). --selbsttest <Datei>
                      prüft ohne Tastatureingaben (INPUT-Größe 40, Datei) und baut das Fenster unsichtbar auf (Fensterprobe) – fand eine Endlosschleife zwischen
                      Listenauswahl und Anzeige (behoben). Das echte Eintippen in ein fremdes Programm wurde NICHT automatisch getestet (hätte in Peters aktives
                      Fenster tippen können) – Peter soll zuerst im Windows-Editor probieren.
                      ZWEI FASSUNGEN (14.10.2026 abends, Peters Auftrag „Klon … einer mit Quicken nur für mich, einer ohne den Button zum
                      Veröffentlichen“): bauen.py erzeugt aus denselben Quellen buchhaltung-editor.html (ÖFFENTLICH, darf auf die Website) und
                      buchhaltung-privat-editor.html (PRIVAT, NIE veröffentlichen/hochladen; Kopfzeile „🔒 privat“, Titel „(private Fassung)“, NAMES-
                      Eintrag in editoren-version.py). Marken in den Quellen: /*@PRIVAT*/ … /*@ENDE*/ bzw. /*@OEFFENTLICH*/ … /*@ENDE*/ (JS/CSS) und
                      <!--@PRIVAT--> … <!--@ENDE--> bzw. <!--@OEFFENTLICH--> … <!--@ENDE--> (HTML). Nur privat: Knopf „📤 Datei für den Assistenten“,
                      ACT uebDatei, uebertragungsDatei(), Assistent-Texte im Bericht (UEB_UNTER/UEB_BOX) und in der Hilfe, Berichtsname mit „QuickSteuer“.
                      Öffentlich: Bericht „Übertragung an Steuerprogramme“ mit neutralem Hinweis, eigener Hilfe-Abschnitt. bauen.py bricht ab, wenn die
                      öffentliche Fassung noch NUR_PRIVAT-Begriffe (Assistent, QuickSteuer, uebDatei …) oder Marken enthält. Beide lesen/schreiben dieselben
                      Daten (gleiche IndexedDB/Ablage). Nebenbei behoben: Berichtsliste .replist blieb unter 900 px „sticky“ und überlagerte den Bericht.
                      Geprüft: je 24 Berichte/Reiter beider Fassungen ohne Fehler/NaN, Knopf und Assistent-Datei nur privat.
                      KONTOAUSZUG UND DATEV (15.10.2026, Peters Auftrag „Kontoauszug-Import und DATEV-Export umsetzen“): b14_bank.js (in TEILE vor b11).
                      Bankumsätze doc.bank [{id, fp, datum, valuta, betrag (Cent, + Eingang/− Ausgang), name, iban, zweck, text, ref, konto, quelle, eingelesen,
                      status offen|erledigt|ignoriert, zu {art re|beleg|buchung|privat, id, i, neu, vorher}, vorherAm}] – COLL 'bankumsatz' (protokolliert), Reiter
                      „Bank“ (TABS nach Belege, Zähler offener Umsätze). Lesen: CSV über Spaltenüberschriften (BANK_SPALTEN/BANK_TEIL, Kopfzeile bis Zeile 40,
                      Belegliste mit belegart/rechnungsnr wird NICHT als Kontoauszug erkannt; Volksbank-S/H in leerer Spalte nach dem Betrag; zweite Namensspalte
                      nameAlt, wenn die erste der eigene Name ist – eigenerName), Kodierung UTF-8 sonst Windows-1252 (textDekodieren), unbekanntes Format →
                      bankSpaltenDialog (Spalten von Hand); CAMT.052/053/054 per DOMParser (nur Sts BOOK, nur EUR, mehrere TxDtls einzeln, Gegenseite Cdtr/Dbtr
                      inkl. Pty/Nm, RmtInf Ustrd+Strd, EndToEndId ohne NOTPROVIDED), auch als ZIP. Doppelte: Fingerabdruck bankFp + laufende Nummer je Datei.
                      dateienVerarbeiten erkennt Kontoauszüge (istKontoauszug) vor Excel-Listen und E-Rechnungen. Abgleich bankVorschlaege() (eigene Wahl ui.bankWahl
                      zuerst, dann nach Datum, jedes Ziel nur einmal – zielSchluessel): 're' Zahlung zu offener Rechnung (Rechnungsnr. im Zweck 55, Betrag = offen 40,
                      Name 20), 'rz' Zahlung schon erfasst (±10 Tage), 'bo' offener Beleg (Betrag mit Vorzeichen, Re-Nr., Name), 'bb' schon per Bank bezahlter Beleg
                      (±14 Tage) → nur verknüpfen, Zahlungsdatum = Buchungstag (nicht bei festgeschriebenen), 'neu' Beleg mit Belegart (bankKatVorschlag: gleiche
                      IBAN früherer Zuordnungen, gleicher Partner, Finanzamt → UV/UE, Entgelt → BG; eingang = heute im angemeldeten/festgeschriebenen Zeitraum,
                      UV mit fuer = voriger Zeitraum), 'privat' (doppelt: Buchung Entnahme/Einlage ↔ Bank, BA), 'ign'. Vorschlag ab Wert 45, „sicher“ ab 80 (grün).
                      bankUebernehmen = EIN Rückgängig-Schritt, bankLoesen nimmt zurück (neue Belege ohne Datei/nicht fest werden gelöscht). Beleg.bank / Zahlung.bank
                      = Verweis (SPERRE_OK beleg 'bank'; sperrVerstoss erlaubt zahl offen → bezahlt nach fest.bis). belegLoeschen/zahlWeg öffnen den Umsatz wieder.
                      Beispiel hat 8 Umsätze (bankBeispiel). DATEV: datevDialog (📁 Ablage, Reiter Buchungen, Berichte journal/euer/buchungen/susa/konten) →
                      EXTF 700, Kategorie 21, Buchungsstapel Formatversion 13, 31 Kopffelder, 125 Spalten (DATEV_SPALTEN, Namen aus dem Gedächtnis nach der
                      Formatbeschreibung; DATEV-Doku-Seite ist nur per JavaScript lesbar), Windows-1252 (cp1252), CRLF, Belegdatum TTMM, Belegfeld 1 bereinigt
                      (Belege JJJJ-NNN, Rechnungen Nr.), Beleginfo Rechnungs-Nr./Belegdatei/Kunde, nur Sachkonten. Modus 'bu' (Standard): Steuerzeile + Grundbuchung
                      gleicher Quelle/Tag/Gegenkonto mit passendem Satz → Bruttobuchung mit BU 9/8 (VSt 19/7) bzw. 3/2 (USt), Automatikkonten (doc.datev.auto, null =
                      erl19/erl7) ohne Schlüssel; §13b/igE/Bewirtung/mehrere Sätze bleiben einzeln. doc.datev {berater, mandant, modus, auto, infos, fest, exporte[]}
                      (CFG_KEYS), Warnung bei erneutem Export desselben Zeitraums. In DATEV selbst NICHT geprüft – Peter: erste Datei von der Steuerberatung zur
                      Probe einlesen lassen. Geprüft im Browser: Beispiel-Abgleich (8 Vorschläge, ein Schritt, Rückgängig/Wiederholen, lösen), bo/bb/rz/re/neu/
                      privat (EÜR + doppelt), Sparkasse-/Volksbank-CSV, CAMT mit Vormerkung und USD, Spaltendialog, Doppel-Import, Protokollkette ohne Abweichung,
                      EXTF (Kopf 31, Zeilen 125 Felder, Umlaute 1252), alle Reiter/Berichte beider Buchführungsarten ohne Fehler/NaN.
                      STAMMDATEN-ANSCHLUSS (15.10.2026): b9 stammAn()/reAusStammdaten (Knopf „👥 Rechnungsempfänger aus den Stammdaten“ im Rechnungsentwurf; Person mit
                      Firma → Firma + „z. Hd.“, Anschrift der Organisation), kundenAusStammdaten (Mehrfachwahl) und kundenInStammdaten; nur sichtbar, wenn der
                      Anschluss eingeschaltet ist (Ereignis 'mc4y-stammdaten' zeichnet neu).
                      STAMMDATEN FÜHREND (15.10.2026, siehe stammdaten-editor.html): b15_stammdaten.js (in TEILE vor b11) = Adapter NUR für Kunden (Personen bzw.
                      Organisationen, artFrei, ohneRollen; name/email/telefon/strasse/plz/ort/land/ustid/kdnr, land über landCode), bereit erst mit echter Ablage
                      (nicht im Beispiel); Änderungen per aendern(…, {keinUndo:true}) – also protokolliert, ohne Rückgängig-Schritt; Zusammenführen setzt
                      nach.frueher und hängt Rechnungsentwürfe um (kundeIds zählt Summen über beide). zustandSetzen behält doc.stammdaten. Kundenformular bei
                      Verknüpfung nur lesbar (STF, Land als Textfeld) + Knopf 👥; ACT sdZeigen in b11 (sdZeigenKunde in b15 – sonst TDZ).
                      RECHNUNGEN AUS DEM PROJEKTMANAGEMENT + VERSAND (15.10.2026 abends, Build 20:58; Gegenstück zu „RECHNUNGEN NACH LEISTUNGSART“ beim Projekt-
                      management): b16_projekt.js (in TEILE vor b11; Namen pmu…/vs…, NICHT „ueb…Datei“ – NUR_PRIVAT-Prüfung in bauen.py). Übergaben im gemeinsamen
                      Browser-Speicher IndexedDB 'mc4y-uebergabe' (Store kv: 'r:<uid>' vom Projekt, 's:<uid>' Status von hier) bzw. als Datei *.rechnung.json
                      (dateienVerarbeiten → pmuDateien, auch über „Dateien wählen“). ui.pmu = offene Übergaben (pmuLaden: beim Öffnen der Ablage, Fensterfokus,
                      BroadcastChannel 'mc4y-uebergabe'), Banner pmuBanner oben im Reiter Rechnungen + Hinweis in der Übersicht. pmuNehmen: nicht im Beispiel; Kunde
                      gleichen Namens = vorhandener (Buchhaltung führt, fehlende Felder aus dem Projekt, ref = Käuferreferenz je Auftrag), sonst kundeMerken legt an;
                      Rechnungsentwurf mit pos.g/gl (Leistungsart, Text), betreff (neues Feld, im PDF unter der Überschrift), anlage (Modell aus dem Projekt), quelle
                      {werkzeug, uid, projekt, projektNr, abrNr}, notiz2; ein Rückgängig-Schritt. pmuVerwerfen. pmuStatusMelden (entprellt über pmuStatusSpaeter aus
                      geaendert() und ablageOeffnen; nicht im Beispiel): je Rechnung mit quelle.uid entwurf|gestellt|teilbezahlt|bezahlt|storniert + nr, datum, brutto,
                      bezahlt, bezahltAm, versandt; übernommene, hier gelöschte Entwürfe (gleicher Bestand = doc.angelegt bzw. Ablagename) → geloescht. Storno und
                      Kopie übernehmen quelle/versandt NICHT. PDF (b4 rechnungPdf): Positionen mit Zwischenüberschrift und Zwischensumme je gl (wenn ≥ 2 Gruppen),
                      Anlage als Folgeseiten pdfAnlage (Tabellen mit Spaltenbreite nach Inhalt, kurze Spalten ≤ 72 pt brechen nie um), abschaltbar r.ohneAnlage
                      (Häkchen „Anlage beifügen“ im Formular), nicht bei Stornorechnungen; Entwurf zeigt „Rechnungsnummer (Entwurf)“. VERSAND: „✉ Versenden …“ an
                      gestellten Rechnungen → reVersandDialog (An, Betreff, Text aus reVersandText; neutral formuliert): „📤 Teilen“ nur wenn navigator.canShare
                      (Dateien an das E-Mail-Programm, danach automatisch vermerkt), „✉ E-Mail vorbereiten“ = mailto + Download von PDF (aus der Ablage) und XRechnung
                      (Anhänge kann ein Link nicht mitgeben), „⬇ Dateien“, „✓ Als versendet vermerken“ → r.versandt [{am, an, weg}] (SPERRE_OK enthielt versandt schon);
                      Anzeige in Liste (✉, 📁 = aus dem Projekt) und Formular. Geprüft wie beim Projektmanagement beschrieben (echter Ablauf in zwei Tabs, Datei,
                      Storno/verworfen/gelöscht, alle Reiter, XRechnung wohlgeformt, PDFs angesehen). Teilen und mailto NICHT ausgelöst (hätte Programme auf Peters
                      Rechner geöffnet) – mailto-Adresse und Downloads per Skript geprüft.
                      KUNDEN AUS DEM VERTRIEBS-NAVIGATOR (16.10.2026, Build 10:23; Gegenstück zu „ÜBERGABE“ bei vertrieb-editor.html): b17_vertrieb.js (in TEILE vor
                      b11). 'k:<uid>' im Store kv von 'mc4y-uebergabe', format 'mc4y-kundenuebergabe' {uid, erstellt, status neu|uebernommen|verworfen, quelle {werkzeug
                      'vertrieb', bestand, chance, chanceNr, account}, kunde {name, zusatz (z. Hd. Hauptkontakt), strasse, plz, ort, land, email, telefon, ustid, web},
                      notiz} bzw. Datei *.kunde.json (pmuDateien → kuDatei). kuLaden (Ablage öffnen, Fensterfokus, BroadcastChannel art 'kunde') → ui.kuu, kuBanner oben
                      im Reiter Kunden + Hinweis in der Übersicht; kuNehmen (nicht im Beispiel): gleichnamiger Kunde = vorhandener, nur leere Felder werden gefüllt
                      (Buchhaltung führt), sonst neu mit nächster Kundennummer; k.quelle {werkzeug, uid}; Notiz ergänzt; meldet 'k:' status uebernommen + kdnr + kundeId
                      und 'kunde-uebernommen' zurück. kuVerwerfen → status verworfen + 'kunde-verworfen'. Geprüft mit echtem Ablauf in zwei Tabs (Browser-Ablage).
                      KUNDEN-NAVIGATOR (16.10.2026, Build 16:13; Gegenstück siehe kunden-editor.html): b18_kunden.js (in TEILE vor b11) = Kundenstand 'kst:<Bestand>'
                      (format 'mc4y-kundenstand': je Kunde id, kdnr, Anschrift, rechnungen [{nr, datum, faellig, netto, brutto, bezahlt, offen, status offen|bezahlt|
                      storniert|storno, bezahltAm, versandt, betreff, quelleUid}], umsatz {Jahr: netto}, umsatz12, offen, ueberfaellig, letzteRechnung, zahlungsdauer;
                      Rechnungen über kundeIds(k) ↔ r.kunde.id). kstMelden NUR wenn 'kst-wunsch' im Store steht (gesetzt vom Kunden-Navigator bei „Mit der Buchhaltung
                      verbinden“) und nicht im Beispiel; nach jeder Änderung über pmuStatusSpaeter, beim Öffnen der Ablage und auf Nachricht 'kundenstand-anfrage';
                      meldet 'kundenstand'. Knopf „⬇ Kundenstand für den Kunden-Navigator“ im Reiter Kunden (*.kundenstand.json, ACT kstDatei). b16: pmuDbDa() –
                      pmuAlle liest nur, wenn 'mc4y-uebergabe' schon existiert (legt beim Lesen nichts an); Rechnungsübergaben mit quelle.werkzeug 'kunden'
                      (Kunde über kunde.bhId → kdnr → Name; betreff = titel; r.quelle {werkzeug 'kunden', uid, kunde, kundeName}), Beschriftungen über
                      reHerkunftText (NICHT „quelleText“ – den Namen gibt es in b6 schon; die doppelte Deklaration legte die Buchhaltung lahm) und in Banner/Übersicht.
                      b17: Kundenübergaben 'k:' auch mit quelle.werkzeug 'kunden' (kdnr wird übernommen, wenn frei). Hilfe-Absatz „Kunden-Navigator“.
                      EINGANGSRECHNUNGEN AUS DEM KUNDEN-NAVIGATOR (16.10.2026 abends, Build 17:47): b19_belegeingang.js (TEILE nach b18). 'b:<uid>' format
                      'mc4y-beleguebergabe' (Datei als Blob) bzw. *.beleg.json (Base64; pmuDateien → pbDatei). pbLaden (Ablage öffnen, Fokus, Kanal 'beleg') →
                      ui.pbu, pbBanner oben im Reiter Belege (VIEWS.belege umhüllt) + Hinweis in der Übersicht. pbNehmen (nicht im Beispiel): E-Rechnung (XML
                      oder PDF mit Anhang) → gewohnter E-Rechnungs-Dialog, ui.eRech {herkunft, zahl} → eUebernehmen übernimmt herkunft/zahl; sonst belegNeu mit
                      Datei, Belegart/Steuerfall wie letzter Beleg desselben Rechnungsstellers, zahl offen bzw. bezahlt am. Beleg-Feld b.herkunft {werkzeug
                      'kunden', uid, partner, vertrag} (NICHT b.quelle – das ist bei Bankbelegen ein Text 'bank'). Erst belegSpeichern → pbGespeichert: 'b:'
                      status uebernommen + belegNr (pbNr = „JJJJ-NNN“), Datei aus dem gemeinsamen Speicher entfernt, Kanal 'beleg-uebernommen'. pbVerwerfen →
                      'beleg-verworfen'. Kundenstand (b18) zusätzlich lieferanten = lieferantenStand(): betriebliche Ausgaben-Belege der letzten ~3 Jahre je
                      Rechnungssteller {name, belege [{nr JJJJ-NNN, datum, renr, brutto, netto, offen, bezahltAm, status, text, quelleUid}], ausgaben {Jahr},
                      ausgaben12, offen}; private Belege nie. Hilfe-Absatz „Eingangsrechnungen aus dem Kunden-Navigator“. Geprüft echt in zwei Tabs (Browser-
                      Ablage): PDF per Maus übernommen + Belegart + Speichern → Beleg mit Datei, Status zurück; Zahlung → „bezahlt“ im Kunden-Navigator;
                      XRechnung (vom eigenen xrechnungXml erzeugt) über den E-Rechnungs-Dialog; *.beleg.json; verwerfen; Banner/Hinweis; Beispiel gesperrt;
                      26 Darstellungen (Reiter, 16 Berichte) ohne Fehler, private Fassung geladen. Testdaten entfernt.
                      TAB-FOLGE (21.10.2026, Volltest, Build 00:14, Sicherung editor-backups/2026-10-21_*_buchhaltung-tabfolge): Nach Eingabe in einem Textfeld und
                      Tab fiel der Fokus auf die Seite zurück (Beleg: Rechnungssteller; Rechnung: jedes Feld; Einstellungen) – „change“ kommt beim Tab, BEVOR der Fokus
                      weiterwandert, und bfEingabe/rfEingabe/aendern riefen render() mitten darin auf. b11_start.js: Textfelder (input außer checkbox/radio/file/Knöpfe,
                      textarea) werden per setTimeout(0) erst nach dem Fokuswechsel verarbeitet (aenderungVerarbeiten; übersprungen, wenn ui.draft bzw. ui.reDraft inzwischen
                      ein anderer Entwurf ist), render() stellt den Fokus dann über fokusSelektor auf das NEUE Feld wieder her; Selects/Häkchen sofort. Geprüft mit der Maus:
                      Tab-Folge Beleg/Rechnung/Einstellungen, Belegart-Vorschlag aus früheren Belegen bleibt, Betrag tippen + sofort „Speichern“ klicken (gespeichert, danach
                      nicht wieder „geändert“). TESTPLAN BH-10.
                      Offene Ideen: Mahnwesen, Vorsteuer-Vergütungsverfahren, Anlage EÜR-Zeilen für 2026 bei Erscheinen prüfen, DATEV mit Personenkonten.
kanban-editor.html    (seit 14.10.2026, Peters Auftrag „Kanban-Board: Kartenfarben wählbar, Bewertung finanziell oder Story Points, Entwickler aus Liste oder neu,
                      Userstory-Satz, Abhängigkeiten – nur die zuerst zu bearbeitende Karte darf gezogen werden, bis sie mindestens In Review ist –, Ready-/Done-
                      Kriterien je Karte, Tasks je Userstory, mindestens Ideen-Log/Backlog/To Do/In Arbeit/In Review/Zurückgestellt/Done, Schritte an beliebiger
                      Stelle einfügen/löschen, Auswertetools“). Eigenständiges Werkzeug (NICHT Teil der Website, nie eigenmächtig hochladen). QUELLEN in
                      kanban-quellen/ (nicht hochladen): seite.html (Gerüst + Hilfe; Schließen oben data-act helpX, unten closeHelp für den Stempel), stil.css,
                      k1_grund.js (Hilfen, Geld in Cent, Datum, Farben, IndexedDB 'mc4y-kanban' nur für den Dateihandle), k2_daten.js (Modell, Regeln, Rückgängig,
                      Speichern), k3_board.js (Leiste, Board, Ziehen, Schritte, Linien, Menüs/Auswahllisten/Dialoge), k4_panel.js (Eigenschaften, Liste, Team,
                      Einstellungen), k5_auswertung.js, k6_start.js (ACT, Tastatur, Ereignisse, Beispiel, Start). ÄNDERUNGEN NUR IN kanban-quellen/, dann
                      `py kanban-quellen/bauen.py` (ruft editoren-version.py auf; --ohne-version für Zwischenstände). Format 'mc4y-kanban' version 1,
                      *.kanban.json: {name, einst {bewertung beide|sp|eur, spSkala, wipModus warnen|sperren, dorModus/dodModus warnen|sperren|aus, archivTage,
                      swim keine|entw|eltern|prio|typ, linien aus|auswahl|alle, farbLegende {Farbe: Bedeutung}, tasksZeigen}, spalten [{id, name, art ARTEN
                      idee|backlog|todo|arbeit|review|pause|done|sonst, farbe, wip, sperrt, erfuellt, dor, dod, zu}], alteSpalten {id: {name, art}} (gelöschte
                      Schritte, damit der Verlauf auswertbar bleibt), entwickler [{id, name, kuerzel, rolle, farbe, kap = SP/Woche}], vorlagen {ready, done},
                      karten [{id, nr, typ epic|story|task|bug|idee, titel, farbe (FARBEN, 12), spalte, pos, rolle, funktion, ziel, akzeptanz, beschr, notiz,
                      sp, eur (Cent), prio MoSCoW, entw, eltern (task/bug → story, story → epic), abh [Vorgänger-ids], ready/done [{id, t, ok}], faellig,
                      erstellt, verlauf [{t, s}], archiv}], zaehler}. REGEL (pruefeZug): Schritt mit sperrt (Standard In Arbeit, In Review, Done) nur, wenn alle
                      Vorgänger in einem Schritt mit erfuellt (Standard In Review, Done) stehen – immer Sperre; WIP (Epics zählen nicht, wipN), Ready (dor) und
                      Done (dod) je nach Modus Hinweis-Dialog „Trotzdem verschieben“ oder Sperre; Hinweis bei offenen zugehörigen Karten und beim Zurückziehen
                      eines Vorgängers. Kreise in Abhängigkeiten verhindert (wuerdeKreis). Ziehen = HTML5-DnD, Zielzellen grün/gelb/rot schon beim Aufnehmen
                      (zugKlassen), Einfügelinie .einl; Spaltenköpfe ziehbar; „＋“ zwischen Spaltenköpfen (spEinf) fügt ein und startet Umbenennen; Menü ⋯ bzw.
                      Rechtsklick (spMenue): umbenennen, davor/danach, links/rechts, WIP, Farbe, Art, vier Regeln, einklappen, löschen (Zielschritt wählen, wenn
                      Karten darin). Karten: Klick = Eigenschaften rechts (renderPanel, Fokus bleibt über fokusInfo/fokusZurueck; Tab nach Neuzeichnen über
                      tabWeiter), Doppelklick = Titel, Rechtsklick kartenMenue, Doppelklick in leere Zelle = neue Karte (mit Bahn-Wert). Entwickler-Feld: Name
                      wählen/eintippen (eindeutiger Anfang genügt), Unbekanntes wird angelegt (entwSetzen); Auswahllisten mit Suchfeld (auswahl(), Enter, „neu
                      anlegen“). Vorgänger per Auswahl oder Klick-Modus auf dem Board (vorgWahl). Bahnen nach Entwickler/Userstory-Epic/Priorität/Art – Ablegen
                      ändert das Merkmal. Abhängigkeitslinien als SVG hinter den Karten (rot gestrichelt = Vorgänger noch nicht weit genug). Rückgängig 150
                      Schritte mit großen Pfeilen + Verlauf ▾; Textfelder über feldSnap/feldFertig ein Schritt je Feld. Zwischenstand localStorage
                      'mc4y-kanban-entwurf' (still wiederhergestellt), Datei per showSaveFilePicker (Handle in IndexedDB, Strg+S schreibt dorthin).
                      AUSWERTUNGEN (Maß Anzahl/SP/€, Zeitraum, Entwickler): Kennzahlen, CFD, Burn-up/-down mit Prognose (Tempo der letzten 4 Wochen),
                      Durchsatz/Velocity je KW, Durchlauf-/Bearbeitungszeit-Streuung mit 85 %-Linien, Bestand je Schritt mit WIP-Marke, Verweildauer +
                      Flusseffizienz, Auslastung je Entwickler (Kapazität), Alter laufender Karten, Fortschritt Stories/Epics, Abhängigkeiten/Engpass-Karten,
                      Verteilung nach Farbe/Art/Priorität; Drucken/PDF (iframe, A4 quer), CSV (Karten mit Zeiten + Wochenwerte), Liste als CSV.
                      Fokus nach dem Neuzeichnen SOFORT setzen, nicht per setTimeout (im Vorschau-Browser laufen Zeitgeber verzögert – Umbenennen ging verloren).
                      Beispiel „Online-Terminbuchung (fiktiv)“ mit Verlauf über ~6 Wochen relativ zu heute. Geprüft im Browser: 405 Board-Darstellungen
                      (Bahnen × Linien × Auswahl jeder Karte) + alle Ansichten/Auswertungen × Bewertung × Maß × Zeitraum + leeres Board ohne Fehler/NaN,
                      normalize idempotent, Datei-Rundlauf, Wiederherstellen nach Neuladen, Druck-HTML; mit der Maus: gesperrte Karte ziehen (rot + Hinweis),
                      erlaubte mit „Trotzdem verschieben“, Rückgängig-Pfeil, Eigenschaften, neuer Entwickler per Eintippen + Tab, Vorgänger-Auswahl mit Suche,
                      Schritt per „＋“ einfügen und benennen, Spaltenmenü → löschen.
                      Seit 15.10.2026: Team-Ansicht „👥 aus den Stammdaten …“ (ACT entwStamm, Mehrfachwahl, nur bei eingeschaltetem Stammdaten-Anschluss).
                      STAMMDATEN FÜHREND (15.10.2026, siehe stammdaten-editor.html): k7_stammdaten.js = Adapter (Entwickler = Personen name/kuerzel, Rollen = die
                      Texte entwickler.rolle als lids, Option eineRolle), sdAnwenden (Rollen-Umbenennung zieht Zuordnungsschlüssel mit, Zusammenführen hängt Karten
                      um), sdGesperrt, ACT.sdZeigen; start() am ENDE von k7. Team-Tabelle bei Verknüpfung nur lesbar; Ereignis 'mc4y-stammdaten' zeichnet sie neu.
stammdaten-editor.html (seit 15.10.2026, Peters Auftrag „Personenstammdatenverwaltung: Anschrift, E-Mail, Rolle …, Rollen, Kostenstellen und Organisationseinheiten
                      anlegen, grafisch und tabellarisch, auswertbar; Anschluss für alle jetzigen und zukünftigen Systeme wahlweise, Module laufen weiter stand-alone“).
                      Eigenständiges Werkzeug (NICHT eigenmächtig hochladen). QUELLEN in stammdaten-quellen/ (nicht hochladen): seite.html, stil.css, s1_grund.js
                      (Hilfen, IndexedDB 'mc4y-stammdaten' Store kv: 'daten' = gemeinsamer Datenbestand, 'datei' = Dateihandle, 'sicherungen' (max. 12, alle 30 Min.),
                      'werkzeuge' = welche Werkzeuge den Anschluss nutzten; BroadcastChannel + localStorage 'mc4y-stammdaten-stand' melden Änderungen, Menü, Dialog,
                      CSV), s2_daten.js (Modell, normalize, aendern/Rückgängig 150, speichern, Beziehungen, Anlegen/Löschen mit Aufräumen, Import CSV/vCard/
                      Buchhaltung-/Kanban-JSON, Export CSV/vCard/JSON), s3_tabellen.js (ui, render-Teile, Tabellen, Eigenschaften), s4_grafik.js (Organigramm,
                      Beziehungsnetz), s5_auswertung.js, s6_start.js (Anschluss-Seite, Beispiel, ACT, Ereignisse, Start), anschluss.js (siehe unten), bauen.py
                      (`py stammdaten-quellen/bauen.py`, ruft editoren-version.py auf). Format 'mc4y-stammdaten' version 1: personen [Personen UND Organisationen,
                      art person|organisation, anrede, titel, vorname, nachname, name (Org.), kuerzel, funktion, typ (TYPEN_P intern|extern|dienstleister|kunde|
                      lieferant|partner|sonst bzw. TYPEN_O … behoerde|eigen), firma (Org.-id), oe, rollen [ids], kst, vorgesetzt, email, telefon, mobil, strasse,
                      plz, ort, land, ustid, kdnr, web, eintritt, austritt, aktiv, notiz], rollen [{name, kuerzel, oe, beschreibung}], einheiten [{name, kuerzel,
                      art OE_ARTEN, parent, leitung, kst, notiz}], kostenstellen [{nr, name, art, parent, verantwortlich, budget (Text)}], layout.netz {id: {x, y}},
                      einst. normalize räumt Verweise und Kreise auf. vorgesetzteVon = ausdrücklich oder Leitung der (übergeordneten) Einheit.
                      Tabellen: Felder data-f, Textfelder ändern „leise“ (aendernLeise/teilRender: der Bereich mit dem Fokus wird nicht neu gezeichnet – Tab geht
                      weiter), rechte Leiste in den Tabellen immer sichtbar (sonst sprang die Tabelle beim ersten Klick); unter 900 px Angaben über ☰. Grafik:
                      Organigramm (Baum, Mitglieder mit ★ Leitung, „Ohne Einheit“, wahlweise Organisationen mit Ansprechpersonen; Person/Einheit ziehen = zuordnen/
                      unterordnen, Knöpfe 🧩/👤 an der Einheit, Rechtsklick elementMenue) und Beziehungsnetz (Lage gespeichert, netzStandard = Spalten je Art, Griff ↗
                      = verbinden nach Artenpaar (verbinden), Alt = Leitung, Linie wählen + Entf = kanteLoesen, Beschriftung wahlweise). Klick wählt, Doppelklick/
                      Enter öffnet die Eigenschaften (ui.panelAuf), F2 = Umbenennen im Bild (#gedit); eigene Doppelklick-Erkennung (gLetzt). Auswertungen
                      (auswertungDaten, pruefhinweise; Druck-iframe, CSV). #beispiel[&ansicht=…&grafik=…&tab=…] = Beispiel nur ansehen (ohne Browser-Speicher –
                      Edge headless hing sonst an IndexedDB). Geprüft: alle Ansichten × Tabs × Sortierung × Grafik-Schalter, Eigenschaften aller 42 Beispiel-
                      Elemente in Tabelle und Grafik, leeres Modell, alle Verbindungsarten inkl. Kreis-Sperren, Lösen, Löschen mit Umhängen + Rückgängig,
                      normalize idempotent, Importe (CSV mit Firma/Einheit/Rollen/KST/Vorgesetzten, vCard, Buchhaltung, Kanban), Exporte; mit der Maus: Person im
                      Organigramm umgehängt, Netz-Verbindung per Griff, Element verschoben, Doppelklick → Eigenschaften, Tippen + Tab in Leiste und Tabelle.
                      BEZIEHUNGEN ZWISCHEN ORGANISATIONEN UND EINHEITEN (26.10.2026, Peters Hinweis „Verbindungen zwischen Organisation und Einheiten sowie
                      zwischen Organisationen, z. B. Steuerberater unterstützt MC4Y und deren Einheiten Finanz und Buchführung – derzeit nicht abbildbar, die
                      Organisation eher nutzlos“; Build 12:41, Sicherung der Quellen editor-backups/2026-10-26_12-24-44_stammdaten-beziehungen-vorher):
                      Format bleibt version 1, nur ergänzt: einheiten[].org (Organisation der Einheit, '' = keine Angabe; Untereinheiten ERBEN sie – oeOrg(id)
                      = eigene Angabe oder die der nächsten übergeordneten Einheit) und beziehungen [{id 'b…', von, nach, art, text, notiz}] (von/nach =
                      Organisation ODER Einheit, nie Person; art aus BEZ_ARTEN [Schlüssel, Satz A→B, Gegenrichtung]: unterstuetzt, leistet, beraet, liefert,
                      kunde, beauftragt, partner, tochter, mitglied, sonst – text nur bei sonst = freie Bezeichnung). normalize: org nur auf vorhandene
                      Organisation, Beziehungen nur zwischen vorhandenen Org./Einheiten, kein Selbstbezug, Doppel (von|nach|art|text) entfernt, unbekannte art →
                      sonst, von/nach über verweise aufgelöst (Zusammenführung aus anderem Fenster). zusammenmischen mischt beziehungen je id. Löschen einer
                      Organisation leert einheit.org und entfernt ihre Beziehungen; Löschen einer Einheit entfernt ihre Beziehungen, Untereinheiten behalten
                      deren Organisation; zfAusfuehren hängt org und Beziehungen um. ARTEN.beziehung (nur für Reiter/Leiste – element() kennt Beziehungen NICHT;
                      BZ(id) schlägt nach, renderPanel/auswahlPruefen/Entf/ACT.loeschen unterscheiden). OBERFLÄCHE: Tabellen-Reiter „🔗 Beziehungen“ (TAB_LISTE
                      = TABS + beziehung; Felder data-bt, in Leiste/Dialog data-bf → bezSetzen), Spalte „Organisation“ bei Einheiten (↑ = geerbt), Spalte
                      „Beziehungen“ bei Organisationen (bezKurz, eine Zeile mit …). Eigenschaften: Organisation mit Abschnitten „Organisationseinheiten“
                      (zuordnen/lösen/neu) und „Beziehungen“ (bezAbschnitt: abgehend mit Satz, eingehend mit Gegenrichtung; ✎ = bzBearbDialog, × löschen);
                      Einheit mit Feld „Organisation“ (leer = „wie übergeordnete Einheit: …“) und „Beziehungen“. „＋ Beziehung …“ = bzNeuDialog: Von, Art,
                      freie Bezeichnung (nur sonst), MEHRERE Ziele als Häkchenliste je Organisation mit ihren Einheiten (Filterfeld), Notiz für alle →
                      bezAnlegen = ein Rückgängig-Schritt. Beziehungsnetz: Kanten oeorg (Einheit → Organisation, braun gestrichelt) und bez (rot, Pfeil,
                      immer beschriftet; Schlüssel bez|von|nach|id; mehrere Linien zwischen demselben Paar nebeneinander versetzt); Griff Organisation ↔
                      Organisation/Einheit → bezMenue (Art wählen; bei Einheit/Organisation zuerst „gehört zu“; „Sonstige oder mehrere Ziele …“ = Dialog);
                      Rechtsklick auf Beziehung = Bearbeiten/Umkehren/Löschen, Doppelklick = Bearbeiten; elementMenue „Beziehung anlegen …“. Organigramm:
                      Etikett „🏢 Organisation“ über der Einheit, wo die Organisation wechselt bzw. ganz oben. Auswertung: Block „Beziehungen zwischen
                      Organisationen und Einheiten“ (Balken nach Art, Tabelle), Spalte „Einheiten“ bei Organisationen, Prüfhinweise „Oberste Einheiten ohne
                      Organisation“ (nur wenn andere zugeordnet sind) und „Inaktive Organisationen mit Beziehungen“. Export CSV „Beziehungen“, Einheiten-CSV
                      mit Spalte „Organisation“. Beispiel: Geschäftsführung gehört zum eigenen Unternehmen; Steuerkanzlei unterstützt das Unternehmen und
                      „Verwaltung und IT“ usw. Englisch: 66 Texte in sprache/texte/0108.json; zusammengesetzte Sätze übersetzen das Verb über spr() =
                      MC4YSprache.t (bezSatz, bezKurz, Balkenbeschriftung VOR dem Kürzen). anschluss.js unverändert (meta() behält unbekannte Schlüssel –
                      geprüft: Anlegen aus dem Kanban-Board lässt Beziehungen und org stehen). GEPRÜFT im Vorschau-Browser (nextcloud-root, Port 8323):
                      Tabelle/Leiste/Dialog mit der Maus (Steuerkanzlei „berät“ Organisation + 2 Einheiten in einem Schritt), Netz-Griff Organisation →
                      Einheit mit Auswahlmenü, Bearbeiten-Dialog (Art → sonst blendet Bezeichnung ein), Logik per Skript (Erben, Löschen Organisation/
                      Einheit + Rückgängig, Zusammenführen, Umkehren, normalize mit Doppel/Selbstbezug/ungültig/verweise, Drei-Wege-Mischen, idempotent),
                      Speichern + Neuladen, EN mit MC4YSprache.offen() (nur Datennamen übrig), 375 px ohne Querscrollen; Testdaten der Vorschau gelöscht.
                      Danach mc4y-suite/bauen.py (Kopie in public/werkzeuge). NICHT geprüft: echte Nutzung über file:// in Chrome/Edge, Suite mit Server-
                      Bestand; paket_bauen.py nicht gelaufen. Die 8EM-/ITSM-/Kanban-Adapter nutzen org/beziehungen (noch) nicht.
                      ANSCHLUSS (stammdaten-quellen/anschluss.js): editoren-version.py fügt ihn als Block MC4Y-STAMMDATEN vor </body> in ALLE gestempelten Werkzeuge
                      ein (nicht in stammdaten-editor.html, SD_OHNE; Block zählt nicht zum Inhalt). Standard AUS (localStorage 'mc4y-stammdaten-anschluss' {datei:
                      {an, tippen}}), dann nur Knopf „👥“ hinter #mc4yVer. Eingeschaltet: Strg+Leertaste bzw. Knopf am Feld = Auswahl (Suche, Zusatzknöpfe E-Mail/
                      Telefon/Anschrift/Firma, „＋ … als Person/Organisation“ legt im gemeinsamen Speicher an), Vorschläge beim Tippen (nur einzeilig, ohne eigene
                      datalist, nicht type=search; Enter/Tab nur, wenn mit ↑↓ gewählt). feldArt() aus type/name/id/placeholder/Label/data-* (email, telefon, mobil,
                      plz, ort, strasse/anschrift, ustid, kostenstelle, rolle, einheit, organisation, name); Einsetzen per nativem value-Setter + input/change.
                      Elemente landen im offenen <dialog>, wenn das Feld darin liegt (Top-Layer). Schnittstelle window.MC4YStammdaten {an, einschalten, laden, daten,
                      eintraege(arten), suche, waehlen({arten, titel, mehrere}), anlegen, adresse, beiAenderung, oeffnen}, Ereignis 'mc4y-stammdaten' am document.
                      Daten gleicher Herkunft (file:// bzw. dieselbe Website); sonst „Stammdaten-Datei laden“. Geprüft: Kanban (Team-Übernahme, Vorschlag beim Tippen
                      ohne Kanban-Enter), Buchhaltung (Empfänger mit Firma, Strg+Leertaste im E-Mail-Feld), Anlegen aus dem Werkzeug → Stammdaten-Fenster lädt neu,
                      aus = keine Änderung; 8EM-Modeller, Mindmap, Kompass, 3D-Systembrett, Ikigai laden ohne Konsolenfehler.
                      SPEICHER NUR BEI BEDARF (16.10.2026, Anlass: Speichertabelle der Datenschutzerklärung, § 25 Abs. 2 Nr. 2 TDDDG): Vorher legte go()
                      (`if (an || adapter) laden()`) in JEDEM Werkzeug mit Adapter beim bloßen Öffnen eine leere Datenbank 'mc4y-stammdaten' an, auch bei
                      ausgeschaltetem Anschluss. Jetzt: Lesen (laden, Abgleich, fremd/BroadcastChannel, „Neu laden“) öffnet sie nur, wenn es sie schon gibt –
                      vorhanden() über indexedDB.databases(); ohne diese Funktion (ältere Browser) nur bei an oder verknüpftem Datenbestand. kv(mode, fn, neu):
                      neu = true darf anlegen (anmelden beim Einschalten, „Stammdaten-Datei laden“); ohne Datenbank liefert kv null, ohne fn aufzurufen.
                      masterAendern(fn) fragt bei fehlender Datenbank erst fn(null) und legt nur an, wenn fn schreiben will (Erstverknüpfung, anlegen) – fn
                      läuft dann in der Transaktion ein zweites Mal (fn darf deshalb nur äußere Ergebnisvariablen setzen). Abgleich eines verknüpften Werkzeugs
                      ohne Stammdaten im Browser → Status „fehlt“, nichts angelegt. Geprüft im Vorschau-Browser (eigener Ursprung, leerer Speicher): alter Block
                      legte die Datenbank in Kanban an, neuer nicht; ohne Anschluss weder durch laden/abgleichen/lokalGeaendert/BroadcastChannel/storage/„Neu
                      laden“ noch durch einen verknüpften Datenbestand (Status „fehlt“); Einschalten per Maus → Datenbank mit „werkzeuge“; Erstverknüpfung per
                      Dialog (Kanban mit, Systembrett ohne vorhandene Datenbank und bei ausgeschaltetem Anschluss); Änderung in den Stammdaten kommt an und
                      sperrt das Feld; Neuladen eines verknüpften Werkzeugs bei ausgeschaltetem Anschluss gleicht ab; anlegen() ohne Datenbank legt sie an;
                      Feldhilfe (Strg+Leertaste); alle 45 Werkzeuge/Fragebögen mit Anschluss (dazu Projekt-Auswertung ohne) nacheinander geladen – keine
                      Konsolenfehler, keine 'mc4y-stammdaten'. Andere Werkzeuge legen beim Öffnen weiter eigene Datenbanken an (8EM-Modeller 'mc4y-8em' und
                      'mc4y-uebergabe', Bodenanker, Buchhaltung …) – nicht Teil dieser Änderung.
                      STAMMDATEN FÜHREND (15.10.2026 nachmittags, Peters Auftrag „Stammdaten-Editor auf Knopfdruck in allen Systemen, die mit den Stammdaten
                      arbeiten; bei der Erstverknüpfung alle vorhandenen Personen und Rollen übernehmen und händisch konsolidieren – Person X in Person Y integrieren,
                      Verbindung zum nutzenden Modul bleibt; danach ist das Stammdatenmodul führend, Neuanlagen in den anderen Systemen gehen dorthin“; Stempelläufe
                      14:22 und 14:40, Sicherungen editor-backups/2026-10-15_14-22-01_editoren, *_14-24-06_editoren, *_14-40-02_editoren).
                      KNOPF „👥 Stammdaten“ in jedem gestempelten Werkzeug: Klick = Stammdaten-Editor als Überlagerung (.mc4ysd-ov, iframe
                      stammdaten-editor.html#eingebettet[&ansicht=…|&zeige=<id>], Kopfleiste mit Status, Abgleichen, ↗ eigenes Fenster, ⚙, Schließen, Esc);
                      Rechtsklick = Einstellungen (Feldhilfe, Datei laden, Neu laden, Verknüpfen/Verknüpfung lösen). Beschriftung ✓ = verknüpft, „· n zu prüfen“.
                      Eingebettet meldet der Editor Änderungen per window.parent.postMessage({mc4ysd:'geaendert'|'zu'}, '*'); der Anschluss prüft e.source.
                      Popups haben max-height + overflow (in kleinen Fenstern waren die Knöpfe des Verknüpfen-Dialogs abgeschnitten); .mitte = senkrecht zentriert.
                      ADAPTER: Werkzeuge mit eigenen Personen/Rollen setzen VOR dem Anschluss-Block window.MC4Y_SD_ADAPTER = {titel, bereit(), bestandName(),
                      namePersonen, felder {person, organisation?, rolle} (Feldnamen der Stammdaten, F_PERSON/F_ROLLE im Anschluss), lesen() → {personen [{lid, art,
                      name, …Felder, rollen [Rollen-lids]}], rollen [{lid, name, …}]}, verknuepfung() (= doc.stammdaten), anwenden(a) mit a = {verknuepfung,
                      personen {lid: Felder + rollen}, rollen {lid: Felder}, zusammen [{von, nach}] (lids), getrennt [lid], grund}; Optionen eineRolle, artFrei,
                      ohneRollen, wandeln {Feld: fn}}. Adapter bisher: ITSM (i7_stammdaten.js), Kanban (k7_stammdaten.js), Buchhaltung (b15_stammdaten.js, nur
                      Kunden) und 8EM-Modeller (Block STAMMDATEN-ANSCHLUSS vor „Start“) – Einzelheiten bei den Werkzeugen. Werkzeuge ohne Adapter haben nur Knopf
                      und Feldhilfe. ZUORDNUNG im Dokument des Werkzeugs: doc.stammdaten = {kennung (Stammdatenbestand), bestand (Kennung dieses Datenbestands),
                      seit, name, p {lid: {sd, s, weg?}}, r {…}} – s = zuletzt abgeglichene Werte (Drei-Wege-Vergleich), sd '-' = getrennt, weg:1 = im Werkzeug
                      entfernt, Zuordnung bleibt (Rückgängig/Wiederholen legt sonst Doppel an). doc.stammdaten gehört NICHT zum Rückgängig-Verlauf (alle Werkzeuge
                      behalten sie beim Undo/Redo); Werkzeuge rufen nach jeder eigenen Änderung MC4YStammdaten.lokalGeaendert() (entprellt 700 ms).
                      In den Stammdaten zusätzlich: kennung, rev, verweise {alte id: neue id}, geloescht {id: Zeit}, verbindungen {bestand: {w, n, datei, seit,
                      zuletzt, ids}}, je Person/Rolle pruefen, herkunft {b, w, n, am, bearbeitet}, zusammengefuehrt [{id, name, am, herkunft}].
                      REGELN (planen im Anschluss): Erstverknüpfung (erstDialog → erstverknuepfen(gleichnamig)) übernimmt ALLE Personen und Rollen als „zu prüfen“
                      (Häkchen: gleichnamige vorhandene gleich verbinden) und öffnet den Abgleich. Danach Stammdaten führend: abgeglichene Felder und Rollen-
                      zuordnungen kommen von dort und sind im Werkzeug nur lesbar (MC4YStammdaten.gesperrt(art, lid); Knopf 👥 daneben = zeigen(sd-id)); lokale
                      Änderungen springen beim nächsten Abgleich zurück (Hinweis). Ausnahme „eigen“: Eintrag noch „zu prüfen“, herkunft.b = dieser Bestand und nicht
                      herkunft.bearbeitet → das Werkzeug darf ihn noch ändern (Drei-Wege mit s). Neue Einträge im Werkzeug: genau ein gleichnamiger, mit diesem
                      Bestand noch nicht verbundener Stammdateneintrag → verbinden, sonst neu anlegen („zu prüfen“). Rollenzuordnung nur für abgeglichene Rollen;
                      Stammdaten führend außer bei eigen-Einträgen und im selben Lauf neu angelegten Rollen. Zusammengeführt (zwei lids → gleiche sd) → zusammen:
                      das Werkzeug legt lokal zusammen und hängt Verweise um. In den Stammdaten gelöscht → getrennt ('-'), Eintrag bleibt im Werkzeug. Änderungen aus
                      den Stammdaten übernehmen die Werkzeuge still (kein Rückgängig-Schritt; Buchhaltung protokolliert mit keinUndo).
                      STAMMDATEN-EDITOR dazu: Ansicht „🔗 Abgleich“ (s7_abgleich.js; Zähler im Reiter): zu prüfende Einträge mit Herkunft, ähnliche Namen
                      (aehnliche/woerter), gleichnamige Gruppen („alle gleichnamigen zusammenführen“, gnLos), „⇢ Zusammenführen …“ (zfDialog: je Feld den bleibenden
                      Wert wählen, ⇄ tauschen; zusammenfuehren → zfAusfuehren hängt vorgesetzt/firma/leitung/verantwortlich/rollen um, verweise[von] = nach,
                      verbindungen.ids, Tombstone), „✓ geprüft“, „✓ alle als geprüft markieren …“, Verbindung vergessen. aendern() setzt herkunft.bearbeitet.
                      Speichern = Drei-Wege-Zusammenführung je Eintrag (basis/doc/gespeichert; mischListe/mischObjekt/zusammenmischen), Rückgängig-Stände werden
                      nachgezogen; neuerBestand (Datei öffnen, Beispiel, leeren, Sicherung) ersetzt statt zu mischen. Löschen nennt die nutzenden Werkzeuge.
                      Hilfe-Abschnitt „Werkzeuge verknüpfen – die Stammdaten werden führend“.
                      GEPRÜFT im Vorschau-Browser (http, gleiche Herkunft; zum Testen ungestempelter Builds Anschluss per eval aus anschluss.js nachladen bzw.
                      Testkopie mit ersetztem Block): je Werkzeug Erstverknüpfung (Dialog mit der Maus, Zählung, „zu prüfen“, Abgleich öffnet), Änderungen in den
                      Stammdaten kommen an (Name, Kürzel, Telefon, Rollen), gesperrte Felder springen zurück, Neuanlage geht als „zu prüfen“ in die Stammdaten,
                      Zusammenführen → Werkzeug legt zusammen (ITSM-Tickets, Kanban-Karten, Buchhaltung frueher + Rechnungsentwürfe, 8EM-Verknüpfungen in allen
                      Bildern), Rückgängig/Wiederholen ohne Doppel, Kanban-Rollenumbenennung, Buchhaltungs-Protokollkette, Sperre wechselt sofort nach „✓ geprüft“,
                      alle gestempelten Werkzeuge laden ohne Konsolenfehler. NICHT geprüft: echte Nutzung über file:// in Chrome/Edge (die Vorschau zeigt file://
                      nur als Abbild) – beim ersten Einsatz von Peter prüfen lassen.
                      NACHTRAG 15.10.2026 abends (Peters Hinweis „im 3D-Systembrett und ähnlichen Editoren habe ich Zugriff, aber keine Verknüpfung“ – dort fehlte
                      ein Adapter; Build 15:54/15:55): ALLE Werkzeuge mit eigenen Personen/Rollen haben jetzt einen Adapter. Anschluss: Optionen mehrfach (mehrere
                      lokale Einträge = ein Stammdaten-Eintrag, kein „zusammen“), gleicherName (gleicher Name im SELBEN Datenbestand = dieselbe Person, auch neu im
                      selben Lauf; Kandidaten nur Einträge, die dieser Bestand schon nutzt – eine „Mutter“ aus einem anderen Brett wird nie automatisch verbunden;
                      Erstverknüpfung mit Häkchen „gleichnamige verbinden“ = alle), nameAbgleich false (nach der Erstverknüpfung nie über den Namen verbinden –
                      Genogramm), erklaerung/hinweis im Verknüpfen-Dialog (Datenschutz-Hinweis bei Coaching-Werkzeugen), inaktive Stammdaten nie per Name.
                      EINFACHER ADAPTER window.MC4Y_SD_EINFACH = {titel, namePersonen, erklaerung, hinweis, typ ('sonst'), rollen, mitgliedschaft, eineRolle,
                      gleicherName, nameAbgleich, bereit(), bestandName(), lesen() → {personen [{lid, name, art?}], rollen}, setzen(art, lid, {name}) → false = nichts
                      geändert, zuordnung(), zuordnungSetzen(v), fertig({inhalt, zuordnung, nutzer})} (bzw. MC4YStammdaten.verbindenEinfach(c)); der Anschluss baut
                      daraus den Adapter (einfachAdapter). Ohne Adapter erklären Knopf-Titel und Fensterkopf, dass das Werkzeug keine Personenliste führt (Feldhilfe).
                      FIGUREN-REGELN (Systembrett 2D/3D, Soziales Atom 2D/3D, Genogramm, Kern-Werkzeuge): Name = ERSTE ZEILE der Beschriftung (weitere Zeilen frei;
                      sdLabelSchutz/sdTextSchutz setzen bei verknüpften Elementen die erste Zeile zurück), je Element n.sdArt p|r|- (Auswahl „In den Stammdaten“ in den
                      Eigenschaften; fehlt = Standard: Figuren/Menschen bzw. Personen-Symbole als Person, alles andere nicht; „Ich“ nie automatisch), gleiche id in
                      mehreren Bildern = ein Eintrag (setzen ändert alle Bilder), Knopf „in den Stammdaten öffnen“ (act sdZeigen), doc.stammdaten nicht im Verlauf
                      (restore/restoreFrom behalten sie) und in normalize/Speichern enthalten, markDirty/setDirty/loadDoc melden lokalGeaendert, fertig zeichnet bei
                      Fokus in den Eigenschaften nur das Bild neu. ADAPTER JE WERKZEUG: systembrett-editor.html und 3d-systembrett-editor.html (Block vor „Start“;
                      sdArt läuft im 2D⇄3D-Austausch mit), soziales-atom-editor.html (mann/frau/kind/kreis), genogramm-editor.html (Personen m/w/d/u, nur der Name,
                      nameAbgleich false, Namensfeld readonly), lebensrad-editor.html (Teamuhr-Marker; „Person 3“ ausgenommen; Namensfeld readonly), 3D-Kern
                      (k3_c1–c4: generisch, aktiv bei M.sd {person(n), namePersonen, erklaerung, hinweis} – gesetzt nur in mod3_atom.js; 3D-Inneres-Team bewusst
                      nicht: Stimmen sind keine Personen), 2D-Board-Kern (kern_b/kern_c: generisch bei M.sd {relevant(it), person(it), organisation(it)?, …} – gesetzt
                      in mod_sh.js (Stakeholder, Gruppen als Organisation), mod_rt.js (Personen-Markierungen; Selbst-/Fremdbild gleichen Namens = eine Person),
                      mod_change.js (Personen/Gruppen auf der Veränderungskurve)). Keine Personenliste (nur Feldhilfe): Ikigai, Zielbild, Kartenarbeit, Analyse,
                      Lebenslinie, Bodenanker, TMK, Logische Ebenen, U-Prozess, Geschäftsmodell, Wirkungsgefüge, Inneres Team, Entscheidung, Ressourcengarten/-baum,
                      Mindmap (Ressourcen können Personen ODER Rollen sein), alle Fragebögen. Geprüft im Vorschau-Browser (Testkopien mit eingesetztem Anschluss):
                      je Adapter Erstverknüpfung (Dialog mit Erklärung/Hinweis), Zählung, Status ok, Auswahl „In den Stammdaten“; 3D-Systembrett ausführlich (Name aus
                      den Stammdaten in beiden Bildern, Tippen in die erste Zeile mit der Maus → springt zurück, zweite Zeile frei, gleicher Name → gleicher Eintrag,
                      neuer Name → neu, „nicht übernehmen“, Rückgängig ohne Doppel, Speichern mit Zuordnung), 2D „als Rolle“, Genogramm (gleichnamige Großväter
                      getrennt, neue „Vater“-Person nicht mit fremdem verbunden), Riemann-Thomann (4 × „Anna“ = 1 Eintrag); danach alle 44 Werkzeuge/Fragebögen
                      gestempelt geladen, Skripte syntaktisch geprüft, ITSM/Kanban/8EM-Erstverknüpfung unverändert. Testdaten der Vorschau entfernt.
                      FELDHILFE NUR NOCH IN PERSONEN-/KONTAKTFELDERN (15.10.2026 abends, Peters Fund: im Projektmanagement stand „Bea Muster“ als Name von
                      Arbeitspaket 1.1.1 – die Feldhilfe hatte beim Tippen Personen angeboten, weil feldArt() JEDES Textfeld ohne Hinweis als 'name' einstufte;
                      betraf alle Werkzeuge: Kartentitel, Ticket-Kurzbeschreibungen, 8EM-Elementnamen …). anschluss.js jetzt: (1) data-sd am Feld oder einem
                      Vorfahren hat Vorrang ('aus'/'nein' = keine Hilfe, Strg+Leertaste gesperrt; sonst die Art, 'person' = 'name'); (2) Beschriftung ohne
                      Feldinhalt (Label geklont, input/textarea/select entfernt – vorher las der Text einer Textarea mit); (3) OBJ-Wörter (Titel, Bezeichnung,
                      Aufgabe, Vorgang, Paket, Phase, Sprint, Story, Ticket, Projekt, Beschreibung, Notiz, Ziel, Diagramm, Begründung, Szenario, Einleitung,
                      Referenz, Rechnungsposition [^a-z]pos[^a-z] …) → keine Hilfe, außer PERS-Wörter (vorname, nachname, person(?!al), zuständig,
                      verantwortlich, ansprech, bearbeiter, mitarbeiter, entwickler, empfänger, teilnehmer, autor, inhaber, (?<!ein)leitung, leiter, owner,
                      klient, coach, beteiligt, gemeldet, melder, antragsteller, ersteller, eigentümer, benutzer, lead); (4) Kundennummer → Art 'kdnr' (wert = kdnr);
                      (5) ein bloßes „Name“ ohne Personenbezug → KEINE Hilfe mehr (Ausnahme nur über data-sd); (6) Knopf 👥 am Feld nur, wenn eine Art erkannt
                      ist; (7) Vorschläge beim Tippen in Namensfeldern nur Personen/Organisationen (keine Rollen). Werkzeuge kennzeichnen Felder per data-sd –
                      Projektmanagement: <body data-sd="aus">, Personen-Tabelle name/email/firma und Projekt-Auftraggeber/Anschrift mit data-sd. Geprüft mit dem
                      Code des Anschlusses an den echten Feldern von Projektmanagement, Kanban, ITSM (Gemeldet von = Person), Buchhaltung (Kunde, Anschrift,
                      USt-IdNr., Kundennummer; Einleitung/Einheit/Leitweg-ID ohne Hilfe) und 8EM-Modeller (Elementnamen ohne Hilfe); Praxistest mit
                      eingeschaltetem Anschluss und Beispiel-Stammdaten: „Bea“ im Arbeitspaket-Namen → nichts, im Personennamen → „Bea Muster“. Danach
                      editoren-version.py → Anschluss in allen Werkzeugen und passenden Kopien (Sicherungen editor-backups/*_19-56-37_editoren, *_19-57-48_editoren).
                      Folge: Buchhaltung-Kundenformular „Name“ und Kanban-Entwicklerfeld haben keine automatische Feldhilfe mehr (Strg+Leertaste geht; bei Bedarf
                      dort data-sd="name" setzen).
                      FIRMA EINER PERSON (16.10.2026, Auftrag aus der Vertriebs-Navigator-Sitzung; Stempellauf 11:01/11:05, Sicherung editor-backups/
                      2026-10-16_11-00-58_editoren): anschluss.js überträgt wahlweise die Zugehörigkeit Person → Organisation (Stammdaten-Feld personen[].firma).
                      Adapter liefern in lesen() je Person firmaLid = lid einer Organisation desselben lesen()-Ergebnisses ('' = keine); FEHLT das Feld, bleibt die
                      Firma unberührt (alle älteren Adapter und der einfache Adapter arbeiten unverändert). planen() rechnet die Firma in einem ZWEITEN Durchgang nach
                      Personen und Organisationen (in lesen() darf die Organisation nach der Person stehen): oSd (lokale Organisation → sd), oLid (sd → erste lokale
                      Organisation, pLid); lokale Firma lf = '' | sd der Organisation | null, wenn die Organisation (noch) nicht verknüpft ist (dann nichts entscheiden).
                      Drei-Wege über s.firma (sd der zuletzt abgeglichenen Firma) wie bei den Feldern: Werkzeug gilt bei eigenen „zu prüfen“-Einträgen (schreibbar),
                      wenn die Stammdaten seit dem letzten Abgleich unverändert sind; sonst Stammdaten führend → a.personen[lid].firmaLid (lokale lid bzw. ''). Fehlt
                      s.firma (erster Abgleich der Firma, auch bei vorher verknüpften Beständen), wird eine LEERE Firma in den Stammdaten aus dem Werkzeug ergänzt statt
                      die Firma im Werkzeug zu löschen; eine gesetzte, abweichende Firma in den Stammdaten setzt sich durch (außer bei eigenen Einträgen). Gibt es die
                      Organisation der Stammdaten im Werkzeug nicht: mit Adapter-Option firmaAnlegen a.personen[lid].firmaNeu = {sd, s (wie planen sie vergleicht, also
                      nach wandeln), name, …Felder der Organisation}; das Werkzeug legt sie EINMAL je sd an und ruft a.zuordnen(neueLid, firmaNeu) (schreibt in
                      a.verknuepfung.p, bevor das Werkzeug sie speichert) – ohne firmaAnlegen bleibt die Firma dann still unberührt. Zusammengeführte Organisationen
                      laufen über aufloesen/verweise (der Stammdaten-Editor schreibt firma beim Zusammenführen ohnehin um). Meldung „Aus den Stammdaten übernommen …“
                      nennt dann „Firmenzugehörigkeit“ (plan.firma). Nutzer bisher: vertrieb-editor.html (siehe dort „FIRMA“). Geprüft im Vorschau-Browser zusammen mit
                      dem Vertriebs-Navigator (alle Richtungen, Zusammenführen, Umstieg ohne s.firma) und Kanban als Werkzeug ohne firmaLid.
itsm-editor.html      (seit 15.10.2026, Peters Auftrag „ITSM-Tool nach ITIL4: Incidents, Problems, Changes erfassen und auswerten; gleichartige Incidents zu
                      einem Problem bündeln; Tickets an Personen und Rollen zuweisen; Problems Changes zuweisen“). Eigenständiges Werkzeug (NICHT Teil der Website,
                      nie eigenmächtig hochladen). „In Anlehnung an ITIL® 4“, eigene Formulierungen, kein zertifiziertes Produkt; Markenhinweis „ITIL® ist eine
                      eingetragene Marke der PeopleCert-Gruppe“ (Hilfe). QUELLEN in itsm-quellen/ (nicht hochladen): seite.html (Gerüst + Hilfe; Schließen oben
                      data-act helpX, unten closeHelp), stil.css, i1_grund.js (Hilfen, Zeiten, ZEIT-Überschreibung nur fürs Beispiel, IndexedDB 'mc4y-itsm' nur
                      für den Dateihandle, dialog/menue, Diagramme balken/saeulen), i2_daten.js (Modell, Priorität, SLA, Ähnlichkeit/Bündeln, Änderungen,
                      Rückgängig 150, Speichern), i3_ansichten.js (Kopf, Listen, Boards, Bündel-Vorschläge, Known Errors, Change-Kalender, Übersicht),
                      i4_panel.js (Eigenschaften rechts, Statusabläufe, Dialoge Lösen/Entscheiden/Abschluss/Bündeln/Sammel-Zuweisung, feldAendern),
                      i5_auswertung.js (Kennzahlen, Grafiken, Druck/PDF, CSV), i6_start.js (Team und Einstellungen, Beispiel, ACT, Ereignisse, Start).
                      ÄNDERUNGEN NUR IN itsm-quellen/, dann `py itsm-quellen/bauen.py` (ruft editoren-version.py auf; --ohne-version für Zwischenstände –
                      ACHTUNG: dann fehlen Logo/Build/Stammdaten-Anschluss, also zum Schluss immer mit Stempel bauen). Format 'mc4y-itsm' version 1,
                      *.itsm.json: {einst {name, ich, kategorien, sla {1–5: {r, l} Minuten}, zeit {modus immer|werktage, von, bis, tage}, buendel {tage, min,
                      schwelle}}, zaehler {inc, prb, chg}, personen [{id, name, kuerzel, email, telefon, rollen [ids], aktiv}], rollen [{id, name, beschreibung}]
                      (Standard ROLLEN_STD: Service Desk, 2nd Level Arbeitsplatz/Netzwerk und Server, Anwendungsbetreuung, Problem Manager, Change Manager, CAB),
                      services [{id, name, beschreibung, verantwortlich, kritisch}], cis [{id, name, typ, service}], incidents/problems/changes [Ticket mit id,
                      art, nr INC-/PRB-/CHG-0001, verlauf [{t, art status|zuweisung|verknuepfung|notiz|feld|system, text, wer, von, nach}] …]} – Felder je Art in
                      VORGABE (i2). Incident: Auswirkung × Dringlichkeit → P1–P5 (PRIO_M, prioManuell übersteuert), major, rolle/bearbeiter, problem, change
                      (= ausgelöst durch Change), workaround, loesung + lcode (LCODE), reagiert/geloest/geschlossen, zuweisungen (Weiterleitungen), wieder.
                      Problem: prio, ursache (+ursacheAm), methode (METHODE), workaround, known/knownSeit (Known Error), changes [ids] (n:m mit Changes).
                      Change: typ standard|normal|notfall, Risiko = Auswirkung × Wahrscheinlichkeit (RISIKO_M), plan/test/rueckfall, von/bis, authority (Rolle),
                      entscheidung/entschiedenVon/-Am/-Grund, umgesetzt, ergebnis (ERGEBNIS), review. ZUWEISEN (zuweisen): Person außerhalb der Rolle → Rolle der
                      Person; neue Rolle ohne Personenangabe → Person außerhalb der Rolle wird frei. SLA (sla): Reaktion/Lösung je Priorität, geschMin rechnet
                      wahlweise nur Servicezeit (ohne Feiertage), Status „wartet“ pausiert die Lösungszeit; zustand ok|knapp (< 25 % Rest)|verletzt|erfuellt.
                      BÜNDELN: aehnlich() = gleicher Service 0,3 + gleiches CI 0,25 + gleiche Kategorie 0,1 + 0,6 · Jaccard der Wörter (≥ 4 Zeichen, ohne STOPP);
                      buendelVorschlaege = Union-Find über offene Incidents ohne Problem im Zeitraum, ab einst.buendel.min, mit passendem offenem Problem („→ zu …
                      hinzufügen“); buendeln(ids, pid, titel) legt ggf. das Problem an. bekannteFehler(i) schlägt Known Errors mit Workaround vor. Ein Change mit
                      Ergebnis „erfolgreich“ kann beim Abschluss seine Problems und deren offene Incidents mitlösen. konflikte(c) = Überschneidung am gleichen
                      Service/CI. Board-Ziehen = statusWechsel (Lösen/Entscheiden/Abschluss über Dialoge; ohne Zuständige → Hinweis + Fokus auf Rolle).
                      Kalender: Namen links fest (svg.kal-l), Zeitachse scrollt (ui.kalLinks), zeigt bei Filter „alle offenen“ auch Abgeschlossene (blass).
                      Team und Einstellungen: Personen (Rollen als Chips, Löschen mit Tickets = inaktiv), Rollen, Services, CIs, Kategorien, Service-Level/Servicezeiten,
                      Bündel-Regeln; „👥 aus den Stammdaten …“ (nur bei eingeschaltetem Anschluss) übernimmt Personen samt Rollen. Auswertungen (Zeitraum): Kennzahlen,
                      Incidents je Woche, SLA je Priorität, Verteilungen, Arbeitsstand je Person/Rolle, Alter, Problems mit den meisten Incidents, was fehlt noch,
                      Changes nach Art/Ergebnis/Status, Changes mit ausgelösten Incidents, Überschneidungen; Druck/PDF und CSV; Listen-CSV je Art.
                      Beispiel „Muster GmbH – IT (Beispiel)“ (#beispiel, fiktiv, relativ zu heute, Arbeitsschritte mit ZEIT nachgestellt, Nummern danach chronologisch
                      neu vergeben): WLAN-Problem mit Firmware-Change, ERP-Gruppe als Bündel-Vorschlag, Drucker-Known-Error mit Change in Bewertung, Major Incident nach
                      Notfall-Change (fehlgeschlagen), zwei ERP-Changes mit Überschneidung. #beispiel&ansicht=…&modus=…&sel=<Nr> für Bilder. Geprüft im Browser:
                      274 Darstellungen (alle Ansichten × Modi × Statusfilter, Panels aller Tickets, leeres Modell) ohne Fehler/NaN, normalize idempotent, Datei-
                      Rundlauf, Zwischenstand nach Neuladen; Abläufe Bündeln (Vorschlag, Markieren, Dialog), Zuweisen, Lösen, Change von Antrag bis Abschluss mit
                      Mitlösen, Rückgängig/Wiederholen, Team-Einstellungen, Stammdaten-Übernahme (mit Platzhalter-Auswahl); mit der Maus: Karte auf dem Board
                      gezogen, Zeile gewählt, Titel getippt + Tab, Strg+Z, Notiz mit Strg+Enter. Ansicht 1400 px per Edge headless angesehen.
                      STAMMDATEN FÜHREND (15.10.2026, siehe stammdaten-editor.html): i7_stammdaten.js = Adapter (Personen name/kuerzel/email/telefon mit Rollen,
                      Rollen name/beschreibung), sdAnwenden, sdZusammen (Rollenlisten vereinigen, id per JSON-Ersetzung in Tickets/Verlauf umhängen), sdGesperrt/
                      sdKnopf, ACT.sdZeigen; start() steht am ENDE von i7 (vorher TDZ-Gefahr). Team-Tabelle bei Verknüpfung nur lesbar (Chips ohne ×, kein „+ Rolle“).
                      CMDB-ANBINDUNG (18.10.2026, Peters Auftrag „Jedem Incident, Problem oder Change sollen ein oder mehrere CIs aus der CMDB zugeordnet werden
                      können … verschiedene Reports, die sich auch auf die CMDB beziehen“; Build 17:58, Sicherung editor-backups/2026-10-18_17-29-48_itsm-cmdb).
                      FORMAT version 2: Tickets haben t.cis [CI-ids] statt t.ci (normalize übernimmt ci → cis, entfernt unbekannte; doppelte CMDB-Verknüpfungen werden
                      zusammengeführt, Tickets zeigen auf den ersten Eintrag – ciAlias). CI {id, name, typ (= Kategorie), service, cmdb (uid), cmdbNr, hersteller, modell,
                      standort, adresse, os, status CI_STATUS (wie CMDB), cmdbStand, cmdbWeg ''|papierkorb|fehlt} (CI_FELDER). Konstanten CMDB_KATEGORIEN/CI_SYMBOL in
                      i2 (i6 nutzt sie beim Laden – TDZ!), CI_TYPEN = CMDB-Kategorien + Anwendung/Datenbank/Cloud-Dienst. Helfer ciAnzeige/cName (Name › Hersteller+Modell
                      › Adresse), ciNamen, ciGemeinsam (Ähnlichkeit 0,25 bei gemeinsamem System, Konflikte im Change-Kalender), ciHaeufig (Bündeln übernimmt die
                      Systeme der Incidents), ticketsMitCi, ciWarnung. neuTicket klont o (sonst teilten Tickets aus dem Beispiel eine cis-Liste). NEUE QUELLE
                      i8_cmdb.js (in TEILE VOR i7 – start() steht am Ende von i7): SUITE()/cmdbRecht()/cmdbQuelle() 'suite' | 'datei' | '', cmdbKatalog (eingelesener
                      Export, NUR im Arbeitsspeicher), kurzAusExport, ciWerteAus/ciAktualisieren (CMDB führend für Name, typ, Hersteller, Modell, Standort, Adresse,
                      OS, Status), ciAusCmdb (vorhandenes verknüpftes bzw. GENAU EIN gleichnamiges eigenes CI wird genutzt), ciVerknuepfen (führt mit einem schon
                      verknüpften zusammen), ciZuTicket/ciVomTicket (Verlauf; Service wird vom CI übernommen, wenn leer). FELD im Ticket ciFeld (Chips mit ⚠, ↗ in
                      die CMDB, ×; Eingabe [data-cisuche] mit schwebender Vorschlagsliste #civorschlag – eigene Systeme, CMDB-Treffer (live entprellt 220 ms bzw.
                      Datei), gleichnamige als EIN Vorschlag „wird verknüpft“, „＋ „…“ in der CMDB anlegen“ (Recht ≥ 2) bzw. „als neues System anlegen“; ↑↓ Enter Esc;
                      Enter ohne offene Liste sucht und nimmt den ersten), „🔍 Auswählen …“ = Dialog ciDialog(modus ticket|liste|verknuepfen) mit Suche, Kategorie,
                      Status, Mehrfachauswahl (CD.sel), Herkunft je Zeile. ciKontext(t) im Incident/Change: Hinweise aus der CMDB, Changes in Umsetzung oder ≤ 14 Tage
                      umgesetzt (mögliche Ursache), geplante Changes, weitere offene Incidents, bekannte Fehler an denselben Systemen. TABELLE „Systeme und Geräte“
                      (ciTabelleHtml): verknüpfte Felder readonly, „🔗 verknüpfen …“, „⇪ in die CMDB“ (ciInCmdb: fragt bei gleichnamiger Komponente, Kategorie außerhalb
                      der CMDB → „Sonstiges“, Adresse als ipv4/mac/dns), „lösen“, „🔍 Aus der CMDB übernehmen …“, „↻ Mit der CMDB abgleichen“, „📂 CMDB-Export
                      einlesen …“ (Dateifeld OHNE accept, damit die Suite es nicht abfängt; Dialog: gleichnamige verknüpfen, alle übernehmen). ABGLEICH cmdbAbgleich
                      = EIN Rückgängig-Schritt „Mit der CMDB abgeglichen“ (gelöscht → cmdbWeg papierkorb, fehlend → fehlt; aus einer Datei gilt Fehlendes nicht als
                      gelöscht); automatisch beim Start und nach dem Öffnen in der Suite (cmdbNachLaden), bei Fensterfokus höchstens minütlich. 'mc4y-suite-zeige'
                      zeigt das Ticket. Liste/Board/Known Errors/Suche/Filter (data-fl ci: einzelnes System, 'k:<Kategorie>', '-' = ohne) mit Systemen; Übersicht
                      „Systeme und Geräte mit offenen Störungen“. BERICHTE (i5, BERICHTE + AW[...], Auswahl in der Leiste, ui.aw {tage, bericht, ci}): Überblick (wie
                      bisher; Verteilung je System zählt Mehrfachzuordnungen), Störanfällige Systeme (ciStat: Incidents, offen, P1/P2, Major, Störungsdauer, Lösungszeit
                      Median, Ø Abstand, Problems, Changes; wiederkehrend ohne Problem), Kategorie/Hersteller/Modell/Standort/Betriebssystem (ciGruppen, „je System“),
                      Changes und Folgen (folgeIncidents = ausdrücklich verknüpft oder ≤ FOLGE_TAGE 7 nach Umsetzung am selben System; Erfolgsquote je System/Kategorie,
                      Wartungsfenster 30 Tage), Problems/bekannte Fehler je System, Akte eines Systems (ACT.awAkte, Steckbrief, Incidents/Changes je Monat, alle Tickets),
                      Datenqualität (ohne System, nur hier, Hinweise, ohne Tickets, letzter Abgleich). Druck/PDF und CSV je Bericht; Listen-CSV mit Systemen, CMDB-Kennungen,
                      Folge-Incidents. Beispiel: 9 Systeme mit Hersteller/Modell/Standort/Adresse (erfunden), Drucker „defekt“, Mehrfachzuordnungen. Hilfe-Abschnitt
                      „Systeme und Geräte (CIs) und die CMDB“ + Berichte. GEPRÜFT: 108 Darstellungen (alle Ansichten, 7 Berichte × 4 Zeiträume, Akten, alle Panels,
                      Team-Reiter, Filter) ohne Fehler/NaN, normalize idempotent, Fassung-1-Datei umgewandelt, CSV/Druck aller Berichte; mit Maus und Tastatur ohne
                      Suite (Vorschlag per ↓ Enter, Neuanlage per Enter, × , Dialog mit zwei Häkchen, CMDB-Export einlesen → gleichnamige verknüpft, Vorschlag aus der Datei)
                      und in einer Wegwerf-Suite (Port 8098, Datenordner im Scratchpad): Live-Vorschlag über die IP, gleichnamiges verknüpft statt doppelt, Neuanlage in
                      der CMDB, ⇪ in die CMDB, 🔗 verknüpfen, Speichern in die Ablage, Änderung/Papierkorb/endgültig in der CMDB → Abgleich (⚠, „fehlt“, auch automatisch
                      nach Neuladen), CMDB-Akte zeigt 5 Tickets, Link öffnet die Datei beim Ticket, Rechte nur lesen/ohne CMDB, 375 px ohne Querscrollen; Rauchtest
                      und DATEI-itsm grün.
                      LÖSUNGSDATENBANK, SERVICE REQUESTS, MASKEN, CI VON HAND (18.–20.10.2026, Peters Auftrag „Lösungsdatenbank … Word, PDF, Powerpoints, GIF,
                      JPG und LibreOffice importier- und exportierbar … nach passenden Lösungen suchen … per Häkchen als dauerhafte Lösungsvorschläge … überarbeitbar
                      … ähnlich wie ein WIKI; CIs händisch erfassen und in die CMDB überführen, Herkunft und Datum je CI; Service Requests erfassen und filtern; statt
                      Seitenleiste eine Maske je SR/Incident/Problem/Change/Lösung“; die 8EM-Anbindung der CMDB bewusst NICHT – Peter plant dort größere Ergänzungen;
                      Build 2026-10-20 10:29; Sicherungen editor-backups/2026-10-18_18-33-48_itsm-loesungen, *_datenschutz-itsm-loesungen, *_blog-itsm-loesungen).
                      FORMAT 3. bauen.py setzt zusätzlich @MINDMAP ein: DOKIMP (Import-Teil) und CRC_T/crc32/makeZip aus mindmap-editor.html (Marken „/* =====================
                      Import: gemeinsame Hilfen“ … „/* ---------- Import-Ablauf“ und „/* ===================== ZIP (ohne Kompression“ … „/* ===================== Bilder in
                      Notizen“ – dort NICHT umbenennen, bauen.py bricht sonst ab); TEILE …, i8, @MINDMAP, i9_loesungen.js, i10_dokumente.js, i7 (start() am Ende).
                      MASKE: #maske statt #panel (renderMaske für ui.sel bzw. ui.kb; Bildlauf .mkrumpf/.mkspalte bleibt; Alt+←/→ maskeBlaettern; Esc bzw. Klick auf den
                      Rand). Seit 21.10.2026 (Peters Wunsch, ITSM Build 14:09) liefert maskeListe() immer die Liste der Ticket-Art (liste(t.art), Filter wie dort),
                      also auch aus Übersicht/Board/Kalender; fällt das Ticket aus dem Filter, alle Tickets dieser Art (IT-21). DIALOGE: Enter in einzeiligen Feldern bestätigt den Hauptknopf (type=search nie; Felder mit datalist erst beim keyup – dlgEnter, sonst wählte
                      Enter nur den Vorschlag), Strg+Enter überall. SERVICE REQUESTS = incidents mit typ 'anfrage' (istSr, AI, SR-####, Art der Anfrage, gewünscht bis,
                      Genehmigung), Taste R, Filter Typ, Bericht 'anfragen'; stoerungen() in allen Störungs-Auswertungen; listeCsv mit Typ-Spalten.
                      LÖSUNGSDATENBANK (i9): doc.loesungen [{id, nr KB-####, titel, kategorie, status entwurf|freigegeben|veraltet, symptom, ursache, inhalt (HTML über
                      kbSauber), workaround, schlagworte, systeme, service, ticketKat, pruefenBis, anhaenge, quelle {art, nr, titel, datei}, versionen (lokal 20), genutzt,
                      hilfreich, nichtHilfreich, geloescht}], doc.kbKategorien. Anhänge lokal: KBF (Arbeitsspeicher) + IndexedDB 'mc4y-itsm' v2 Store 'dateien' (erst beim
                      Schreiben) + beim Speichern als kbDateien (Base64) in der Datei (kbMitDateien/kbDateienAus); kbSpeicherAufraeumen entfernt nicht mehr gebrauchte NUR
                      beim Start (nicht beim Öffnen einer Datei – dort gibt es den Rückgängig-Schritt „Stand vor dem Öffnen“ –, nicht im Beispiel). Adapter KBS (lokal bzw.
                      window.MC4YSuite.loesungen; kbSuite(), kbSuiteAbwarten nach dem Start, sonst lieferte „passend“ anfangs die lokale Datenbank). BEWERTUNG kbBewerten =
                      ls_bewerten auf dem Server – IMMER beide gleich ändern: Titel/Schlagworte 3, Fehlerbild 2, Ursache/Inhalt/Workaround 1, Anhang 0,5, Präfixtreffer ab
                      5 Zeichen, text = p/(3·Wörter)·1,6; System +0,2, gleicher Service +0,1 und dessen Wörter („Mail“, „ERP“) zählen höchstens 1, Ticket-Kategorie +0,05,
                      hilfreich ≤ 0,05; kein gemeinsames Wort → 0; ein einziges bei ≥ 3 Wörtern ×0,6; veraltet ×0,6, Entwurf ×0,9; KB_SCHWELLE 0,25; KB_STOPP = STOPP +
                      Wörter ohne Aussage (= LS_STOPP; STOPP selbst = Bündeln, unverändert) + seit 21.10.2026 KB_STOPP_EN (englische Füllwörter „the“, „with“, „very“ …,
                      gleiche Liste am Ende von LS_STOPP – vorher drückten sie jede englische Bewertung). Im Beispiel: INC-0017 (verdächtige E-Mail) ohne Vorschlag,
                      INC-0015 → KB-0005 – auf Deutsch (0,25) und im englischen Beispiel (KB-0004/0005 haben seitdem eigene englische Fassungen).
                      ENGLISCHE WORTFORMEN (21.10.2026 nachmittags, Peters „Bau die englischen Wortformen ein“; Sicherung editor-backups/2026-10-21_16-01-53_englische-
                      wortformen): kbRoh/ls_roh = Wörter (fold, ab 3 Zeichen, ohne Füllwörter; Bindestrich-Ketten aus Teilen mit höchstens zwei BUCHSTABEN werden ein Wort:
                      „Wi-Fi“ → wifi, „E-Mail“ bleibt mail, Zahlen „10-15“ nicht); kbStamm/ls_stamm = deutsche Form wie bisher; kbStammEn/ls_stamm_en = englische Form
                      (-ies → y, -(ss|sh|ch|x|z)es, -s außer ss/us/is ab 4 Zeichen, dann -ing ab 6/-ed ab 5 (-ied → y) bzw. -ly ab 6, verdoppelter Mitlaut wird einfach;
                      Rest nach -ing/-ed auf Mitlaut–Selbstlaut–Mitlaut → zusätzlich Form mit e: closing → clos/close). kbFormen/ls_formen = [deutsche Form, englische Formen
                      ab 3 Zeichen NUR wenn wirklich eine englische Endung wegfiel] – sonst landete das ungekürzte Wort dazu („papier“ neben „papi“ passte als Anfang von
                      „Papiereinzug“; im ersten Versuch drei deutsche Bewertungen geändert). Wortlisten der Lösungen (kbWoerter/ls_woerter) = alle Formen; Suchanfrage
                      (kbSuchwoerter/ls_suchwoerter_anfrage) = je Wort (Schlüssel deutsche Form, zählt wie bisher einmal) die Liste seiner Formen; kbBewerten/ls_bewerten nehmen
                      je Suchwort die beste Form → deutsche Bewertungen im Beispiel alle 48 unverändert. Server zusätzlich: LS_SCHEMA 3 baut beim ersten Aufruf die
                      gespeicherten Wortlisten neu (vorher automatische Kopie …_vor-loesungen-3), ls_fts_anfrage nimmt die englische Stammform als weiteren Wortanfang
                      („dropping“ findet „drops“), die LIKE-Rückfallsuche den gemeinsamen Anfang (nur reine Buchstabenwörter), Auszug im Vorschlag aus dem Anfragetext.
                      BEISPIEL: Im englischen Beispiel fehlten Vorschläge vor allem, weil Service-/Systemnamen der Tickets übersetzt waren, die der Lösungen nicht – Haken
                      „auch“ in sprache/werkzeuge/itsm-editor.html.json (loesungen.service, loesungen.systeme.name, loesungen.ticketKat; neu in sprache.js daten()). Seitdem
                      englisch 23 Vorschläge (deutsch 22): WLAN-Tickets → KB-0002 wie deutsch, zusätzlich INC-0006 (WLAN) → KB-0002 und CHG-0004 (ERP-Update) → KB-0005,
                      PRB-0003 → KB-0003 (Outlook-Absturz → Abwesenheitsnotiz, schwach) fällt auf Englisch weg; INC-0017 weiter ohne Vorschlag. GRENZE: keine Synonyme
                      (WLAN ≠ Wi-Fi – im Beispiel stehen beide als Schlagworte), unregelmäßige Formen (lost, ran) nicht.
                      Im Ticket: „Passende Lösungen“ (kbPassendZeigen; „anwenden“ = kbAnwenden füllt LEERE Felder, verknüpft t.kb, zählt Nutzung), Häkchen „In die
                      Lösungsdatenbank übernehmen“ (kbAusTicket → Entwurf mit Quelle). Ansicht 'kb' (Kategorienbaum, Karten, Suche, Status, Sortierung), Maske Lesen/
                      Bearbeiten (Editor kbEditorLeiste, Bilder einfügen/ziehen, Verweise [[KB-0001]] bzw. [[Titel|Text]]), Versionen mit Vergleich (kbDiff) und
                      Wiederherstellen, Papierkorb, CSV. DOKUMENTE (i10): Import kbDokHtml – docx/odt/pdf über DOKIMP, pptx (kbLiesPptx) und odp (kbLiesOdp) selbst, Bilder,
                      txt/csv/html; je Datei eine Lösung oder in den Editor, Original als Anhang (Text für die Suche). Export über das Blockmodell kbBloecke/kbExportBloecke
                      (kbZellen normiert Tabellenzellen – ohne cs/rs öffnete Word die Datei nicht) → kbDocx, kbOdt (Spaltenstil je Tabelle), kbPptx/kbOdp (kbFolien), HTML,
                      PDF (druckFenster), ZIP mit Anhängen; „Alle exportieren“ = Word oder PDF der aktuellen Liste. CI VON HAND (i8): „＋ Neu erfassen …“ (ciErfassenDialog/
                      ciErfassenOk; der Vorschlag „neu“ öffnet ihn mit dem Namen), cmdbKategorie (Kurzform „Client“ → „Client / Endgerät“, sonst Sonstiges), Häkchen #ceCmdb
                      → SUITE().cmdb.anlegen(felder, adressen, {art: 'itsm', info: Ticketnummer · Datei}) – der Schlüssel heißt ART (bis 20.10. stand fälschlich herkunft
                      → Herkunft „von Hand“); sonst lokales CI mit herkunft 'ticket'. CI {herkunft, herkunftInfo, erstellt, geaendert}; Tabelle „Systeme und Geräte“ mit Spalte
                      Herkunft. NUR LÖSUNGSDATENBANK: #nur=loesungen[&eingebettet][&loesung=…] (leeres Dokument ohne Tickets/Zwischenstand, body.nurkb, window.__mc4yKb
                      {geaendert, oeffnen}) – so bettet das Suite-Modul 'loesungen' das Werkzeug ein. Beispiel mit SRs, KB-0001 … 0005 und Verknüpfungen, CIs mit Herkunft
                      'beispiel'. GEPRÜFT: 81 Darstellungen ohne Fehler/NaN, Rauchtest, DATEI-itsm, Datei-Rundlauf MIT Anhängen, Importe in Word/PowerPoint erzeugter Dateien
                      (Scratchpad), Exporte in Word/PowerPoint geöffnet, Maus/Tastatur lokal und in einer Wegwerf-Suite (Übernahme per Häkchen auf den Server, CI per
                      Tastatur in die CMDB mit Herkunft itsm, Export aus der Server-Lösungsdatenbank mit Bild-Anhang, Modul im Portal). NICHT geprüft: LibreOffice selbst,
                      sehr große bzw. ungewöhnliche Dokumente; die installierten Suiten (PC, <server>) haben noch den ITSM-Stand vom 15.10.
projektmanagement-editor.html + projektauswertung-editor.html (seit 15.10.2026, Peters Auftrag „Projektmanagement-Werkzeug, tabellarisch und grafisch
                      bedienbar, Kosten, Personen/Stammdaten, Wasserfall und RUP, Beispielprojekt, Kanban-Board integrierbar mit Datenübernahme, Abrechnung für Scrum,
                      RUP und Wasserfall, weitere Vorgehensmodelle erlaubt, Auswerte-Werkzeug, Blog-Einträge“). Eigenständige Werkzeuge (NICHT Teil der Website, nie
                      eigenmächtig hochladen). Die Anforderungs-Mindmap zum Web-PM-Tool (Geschäftsplanung\Mindmaps, siehe Gedächtnis) wurde nur gelesen, nicht
                      geändert; dieses Werkzeug ist die lokale Browser-Fassung wie die übrigen – der Server-Technikstack des geplanten Web-Tools bleibt offen.
                      QUELLEN in projekt-quellen/ (nicht hochladen): seite.html (PM, Hilfe mit Markenhinweis „Rational“ = IBM, nur beschreibend; V-Modell „im
                      allgemeinen Sinn, nicht V-Modell XT“; Scrum nach Scrum Guide), seite-auswertung.html, stil.css, gemeinsam p1_grund (Hilfen, Geld in Cent, Datum
                      als Tagesnummer dn, Feiertage je Bundesland LAENDER/feiertage, kalender() = Arbeitstag-Index wi/wiNach, Dialog/Menü, Diagramme balken/saeulen/
                      linien/risikoMatrix, druckFenster, kvGet/kvSet in IndexedDB IDB_NAME), p2_modell (FORMAT 'mc4y-projekt' version 1, MODELLE wasserfall|vmodell|rup|
                      scrum|kanban|hybrid mit Standard-Abrechnungsart, PTYP phase|iteration|sprint|paket|meilenstein, BTYP epic|story|task|bug|anf, BSTATUS, DISZIPLINEN
                      (9 RUP-Disziplinen), KOSTENARTEN, ROLLEN_STD, ABR, Gate-Kriterien KRIT, V_STUFEN, vorlagePlan(modell), leeresDoc, normalize, Nachschlage-
                      funktionen), p3_rechnen (rechne(d) zwischengespeichert über WeakMap + REV; Terminplanung vorwärts/rückwärts auf Arbeitstagen mit EA/AA/EE/AE +
                      Versatz, Sammelvorgänge, kritischer Pfad, Prognose aus Fortschritt (soll/prog), Basisplan, Earned Value PV/EV/AC/CPI/SPI/EAC, Timeboxen/Velocity/
                      Burndown, fluss (Durchsatz, Durchlaufzeit, CFD), auslastung, abrSet/abrVorschlag/abrStand, Ampel, Hinweise), p4_aendern (aendern(label, fn,
                      leise) = einziger Änderungsweg, Rückgängig 150, Zwischenstand localStorage 'mc4y-projekt-entwurf', Datei *.projekt.json per showSaveFilePicker),
                      p5_oberflaeche (ui, render/teilRender(quelle) – Textfelder zeichnen nur Teile neu, damit Tab/Fokus bleiben –, Eigenschaften-Panel, feldAendern),
                      p6_plan (Tabelle + Gantt: Ziehen = verschieben, rechter Rand = Dauer, Griff = Abhängigkeit, Zoom Tage/Wochen/Monate/Quartale, Struktur = PSP-
                      Baum, Druck), p7_agil (Backlog, Board, Kanban-Austausch), p8_phasen (je Modell: Phasen-Chevrons + Quality Gates, V-Sicht, RUP-Sicht mit
                      Meilensteinen LCO/LCA/IOC/PR und Disziplinen-Kurven je Iteration, Sprint-Burndown/Burn-up, Fluss/CFD/WIP), p9_ressourcen (Auslastungs-Heatmap,
                      Zeiterfassung Woche/Liste, Kosten), pa_abrechnung, pb_risiken (Risiken mit Matrix, Änderungsanträge → Nachtrag/Einplanen), pc_berichte (8 BERICHTE,
                      Druck/PDF, CSV, Statusbericht festhalten), dazu nur PM: pd_team (Projekt, Personen, Rollen, Kalender, Regeln, Ampel, Basisplan/Austausch, Vorlagen,
                      Stammdaten-Adapter MC4Y_SD_ADAPTER mit sdAnwenden/sdZusammen/sdGesperrt), pe_beispiel (auch in der Auswertung), pf_start (ACT, Tastatur, Start mit
                      #beispiel&modell=&ansicht=&bericht=&zoom=&modus=); nur Auswertung: pz_portfolio. ÄNDERUNGEN NUR IN projekt-quellen/, dann `py projekt-quellen/bauen.py`
                      (baut BEIDE Dateien, ruft editoren-version.py auf; --ohne-version für Zwischenstände – dann fehlen Logo/Build/Anschluss).
                      DATEN *.projekt.json: {projekt {name, nr, kunde, ziel, start, ende, modell, budget, …}, einst (Kalender/Bundesland, Stunden je SP, WIP, Ampel-
                      Grenzen …), rollen [{name, beschreibung, satz}], personen [{id, name, kuerzel, rollen, art intern|extern, kostensatz, satz (= Verrechnungssatz),
                      stdWoche, anteil, abwesend [{von, bis, text}], aktiv}], plan (Phasen/Iterationen/Sprints/Pakete/Meilensteine mit eltern, pos, start, dauer (Arbeits-
                      tage), aufwand, res (Personen), rolle, abh [{v, art EA|AA|EE|AE, lag}], fortschritt, status, gate/kriterien/freigabe, disziplin, vstufe/vseite,
                      rup (Phase 1–4), abr {art ''|aufwand|pauschal|keine, preis}, mta = Meilensteintrend), backlog [{typ, titel, als/moechte/damit, akzeptanz, sp, aufwand,
                      rest, eur, prio, person, timebox, eltern, abh, disziplin, dod, status, verlauf, abgenommen, kanban {karte, board}}], zeiten [{datum, person, ref,
                      stunden, abrechenbar}], kosten (Plan/Ist, Kostenart, weiterberechenbar mit aufschlag), risiken, changes, abrechnung {art, raten, nurAbgenommen …},
                      rechnungen [{pos mit Herkunft q {a, ids}, status entwurf|gestellt|bezahlt|storniert}], basis, berichte, zaehler, stammdaten}.
                      ABRECHNUNG (ABR): aufwand (Stunden × Verrechnungssatz + weiterberechenbare Kosten, Obergrenze), festpreis (Raten an Meilensteinen, Nachträge aus
                      genehmigten Änderungen), timebox (je Sprint/Iteration), sp (je abgenommenem Story Point), phasen (je Element eigene Art – Hybrid). abrVorschlag
                      rechnet nur noch nicht abgerechnete Leistung (abrSet über die Herkunft der Positionen), Leistungsnachweis drucken, Storno; erloesAnteilig =
                      Festpreis mindestens nach Fortschritt (sonst wäre der Deckungsbeitrag zwischen zwei Raten negativ), Ergebnisprognose = Planerlös − Kostenprognose.
                      KANBAN-AUSTAUSCH: „Backlog als Kanban-Board ausgeben“ (kanbanDoc: format 'mc4y-kanban', Spalten 's'+Art, Entwickler 'e'+Person, Karten-id = vorhandene
                      Karte bzw. 'pm'+id, Verlauf/DoD/Abhängigkeiten) und „Aus einem Kanban-Board übernehmen“ (Datei oder Zwischenstand 'mc4y-kanban-entwurf' im selben
                      Browser; Dialog mit Zeitrahmen für neue Einträge, Umgang mit „Done“, Zuordnung Entwickler → Personen oder neu; b.kanban.karte verhindert Doppel,
                      Status aus der Spaltenart, Verlauf übernommen, fehlende Karten wahlweise „Verworfen“; doc.kanbanQuelle). Zeitrahmen/Stunden/Disziplin bleiben im PM.
                      PROJEKT-AUSWERTUNG (pz_portfolio.js, IndexedDB 'mc4y-projekt-auswertung' merkt den Ordner): lädt mehrere *.projekt.json (Dateien, Ordner, Zwischenstand
                      des PM im selben Browser, „Beispiele“ = alle sechs), mitDoc(d, fn) rechnet je Projekt mit dem gemeinsamen Rechenkern; Ansichten PZ_ANSICHTEN
                      Portfolio (Ampeln, Fortschritt, Prognose, EV, Abrechnung, CPI/SPI-Bild, Meilensteine, Hinweise), Termine, Auslastung über alle Projekte (gleiche
                      Person über doc.stammdaten-Verknüpfung oder gleichen Namen – pzPersonKey), Abrechnung (je Projekt/Art/Monat, offen, jetzt abrechenbar), Kosten,
                      Agil und Fluss, Einzelprojekt (alle Berichte); Druck/Gesamtbericht, CSV. Liest nur; kein Stammdaten-Anschluss (SD_OHNE in editoren-version.py).
                      BEISPIELE (pe_beispiel.js, fiktiv, relativ zu heute simuliert: Pläne, gebuchte Zeiten höchstens Wochenstunden × Anteil, Backlog-Verlauf, Rechnungen,
                      Statusberichte, Basisplan): Wasserfall „Einführung Lagerverwaltung“, V-Modell „Steuerungssoftware Prüfstand“, RUP „Kundenportal“, Scrum „Termin-App“,
                      Kanban „Website-Betreuung“, Hybrid „Einführung CRM-System“; gemeinsames Team BSP_TEAM (erfundene Namen) mit Anteilen je Projekt – Überbuchungen
                      über Projekte hinweg sind gewollt sichtbar. Geprüft im Browser: alle Ansichten, Berichte und Abrechnungsarten der sechs Beispiele, leeres Projekt und
                      Projekt mit einem Eintrag ohne Fehler/NaN, normalize idempotent (alle sechs Beispiele), Kanban: Beispiel-Board des Kanban-Werkzeugs (27 Karten,
                      4 Entwickler → Personen, Eltern/Abhängigkeiten/Status) in ein leeres Projekt und erneut ohne Doppel, Export → Import ohne Abweichung; Stammdaten-
                      Adapter per Skript (lesen, anwenden, zusammenführen mit Umhängen von Zeiten/Zuständigkeiten) – die Erstverknüpfung über den Dialog NICHT eigens;
                      mit der Maus: Gantt ziehen/Dauer/Abhängigkeit, Tabelle + Tab, Board-Ziehen, Abrechnung anlegen/stellen/bezahlen, Auswertung mit sechs Projekten.
                      Bilder per Edge headless (#beispiel&…). Blog-Entwürfe 2026-10-15-tool-projektmanagement.md und -tool-projektauswertung.md (Bilder tool-projekt*.jpg).
                      GLIEDERUNG BELIEBIG TIEF + TASKS IM BAUM (15.10.2026 abends, Peters Frage „Warum kann ich unter der Iteration keine weiteren Teilschritte
                      einbauen … bis zu einzelnen Tasks“; Build 18:28). Ursache: Das Datenmodell erlaubte Tiefe (eltern), aber „＋ Unterpunkt“ gab es nur in den
                      Eigenschaften, der Baum hatte keine Anlegen-Befehle, und ein Unterpunkt machte eine Iteration zum Sammelvorgang (sie schrumpfte auf die
                      Dauer des Unterpunkts). Jetzt: RECHNEN (p3) – m.rahmen(e) = Sprint/Iteration mit Unterpunkten bleibt Zeitrahmen fester Länge: planen()
                      nimmt Zeitrahmen als eigene Einheit auf (su = Blätter + Zeitrahmen; Nachfolger warten auf Rahmen UND Inhalt), Sammel-S hat rahmen {s, f}
                      und ueber (AT über dem Rahmenende → Hinweis + Schraffur im Gantt). m.traeger(e) = Blatt oder Sammelpunkt mit direkt zugeordneten
                      Einträgen – Auslastung, Planerlös, RUP-Disziplinen, Personalkosten, Ressourcenliste zählen solche Einträge mit (anteile: e.aufwand nur bei
                      Blättern); Fortschritt/EV eines Sammelpunkts gewichten eigene Einträge (fItems) mit; PV nimmt Sammelpunkte mit eigenen Kosten (eigenK)
                      auf; m.timeboxes.items umfassen Einträge der Unterpunkte. OBERFLÄCHE: Tabelle „↳ Unterpunkt“ + Einfg, Rechtsklick-Menüs planMenue/
                      itemMenue (Zeile, Balken, Kasten), Dauer-Spalte/Eigenschaft „Länge des Zeitrahmens“ bleibt bei Zeitrahmen mit Unterpunkten editierbar,
                      Gantt zeigt den Rahmen als helle Fläche mit Griff; erster Unterpunkt eines Arbeitspakets übernimmt Zuständige/Rolle/Disziplin (Hinweis
                      „ist jetzt Sammelvorgang“). STRUKTURPLAN (p6, Block „Projektstruktur … beliebig tief“): ui.struktur {ms, bl} (gespeichert in
                      'mc4y-projekt-ansicht'), pspKinder = Plan-Kinder + (bei bl) topItems, Einträge darunter bkids gleichen Zeitrahmens, Spalte „Noch nicht
                      eingeplant“ (pspOhneZeitrahmen; Epics nur mit nicht eingeplanten Unterpunkten), Kästen data-psp / data-pspb / data-psppb, Klappen ▾/▸ über zu
                      (pspAuf alle/zuklappen), Griffe am gewählten Kasten (↳ kind, ＋ danach, 📘 item → Menü Userstory/Task/Fehler/Anforderung; pspNeu), Ziehen:
                      Vorgang → Vorgang, Eintrag → Vorgang (timebox), → Eintrag (eltern nach BELTERN, timebox = erben), → „Noch nicht eingeplant“; schmale Fenster
                      (≤ 900 px): erster Klick wählt (ui.pspSchmal unterdrückt die Eigenschaften), zweiter öffnet sie. itemNeu({…, hier:true}) bleibt in der Ansicht.
                      Geprüft im Browser: Iteration + Unterpunkt (Rahmen 15 AT bleibt, Kind darin, Nachfolger wartet), 4 Ebenen, Tasks/Userstory auf Ebene 3–4,
                      Überlauf-Hinweis + Schraffur, Ziehen aller vier Arten (per Skript), Rechtsklick, Einfg in Baum und Tabelle, Rückgängig/Wiederholen, mit der
                      Maus: Kasten wählen, ↳ Unterpunkt + Namen tippen, 📘 → Task, Rechtsklick-Menü; 156 Ansichten + 48 Berichte der sechs Beispiele, leeres
                      Projekt, Projekt-Auswertung ohne Fehler/NaN; jeder Eintrag erscheint im Baum; Kennzahlen der Beispiele unverändert (Blogbilder gültig).
                      MEHRERE ZUSTÄNDIGE MIT LEAD (15.10.2026 abends, Peters Wunsch „mehrere Personen je Aufgabe, oberste = Lead fest gekennzeichnet,
                      Reihenfolge verschiebbar“; Build 19:16): Arbeitspaket e.res (Reihenfolge, [0] = Lead – gab es schon als Liste), Backlog-Eintrag person (= Lead)
                      + NEU mit [weitere] (normalize: nur gültige, ohne Doppel, rückt nach, wenn person leer). p2: bPers(b), zustListe/zustSetzen(art, o, L),
                      zustText (CSV „Name (Lead), …“). p5: zustHtml(art, o) = Liste ol.zliste (li draggable data-zust="art|id|pid", Lead gelb + „★ Lead“, je
                      Zeile ★ zum Lead / ▲ / ▼ / ×, Auswahl „＋ Person ergänzen“ data-act-sel zustPlus) in den Eigenschaften von Vorgängen und Einträgen;
                      zustAendern/zustSchieb (ein Rückgängig-Schritt je Aktion); avaLead/avasZ (goldener Ring um das Lead-Kürzel bei > 1 Person), kuerzelZ
                      („★MM, EE“ im Gantt und Strukturplan). Item-Feld person im Backlog-Auswahlfeld = Lead: schon Zuständige rücken nach oben, neue ersetzen den
                      Lead, „+n“ zeigt weitere. Tabellen-Kürzelmenü (zustMenue) mit „… zum Lead machen“. pf_start: ACT zustLead/zustSchieb/zustWeg, eigene
                      HTML5-DnD-Listener für [data-zust] (Markierung li.vor/li.nach). Rechnen: Aufwand und Plankosten eines Eintrags gleichmäßig auf alle
                      Zuständigen (wie bei e.res); Board-Filter, Ressourcen-Listen, Personen-Löschprüfung, Suche berücksichtigen alle. Kanban-Austausch: nur
                      der Lead (eine Person je Karte); beim Import bleiben weitere erhalten. Geprüft: ★, ▲▼, ×, Ziehen (Skript und echte Maus), Lead per
                      Tabellen-Auswahl und Kürzelmenü, Rückgängig, Board/Backlog/Struktur/CSV-Anzeige, Aufwandsverteilung (Summe = Plan), alle sechs Beispiele
                      mit zusätzlichen Personen (78 Ansichten, 48 Berichte, Panels) und Projekt-Auswertung ohne Fehler/NaN; Beispielkennzahlen unverändert.
                      NACHBESSERUNG (Build 19:35, Peters Kritik „Ich kann keine weiteren Personen zu einer Task, Aufgabenpaket usw. anlegen“): Die erste Fassung bot
                      nur ein Auswahlfeld mit vorhandenen Team-Personen – in einem neuen Projekt leer, Anlegen nur über „Projekt und Team“, in Board und
                      Backlog-Tabelle gar nicht. Jetzt: Fenster zustDlg(art, id) (ZD, zustDlgRender nach jeder Änderung) aus Plantabelle (Kürzel/„＋“, zustMenue),
                      Backlog-Tabelle (Kürzel/„＋ zuordnen“ statt Lead-Auswahl), Board-Karte (Kürzel), Rechtsklick „👥 Zuständige …“ (planMenue/itemMenue) und Link
                      „großes Fenster“ im Feld; Feld zustHtml = zustListeHtml + zustEingabeHtml: Eingabe [data-zust-neu="art|id"] mit datalist (Enter = zustNeuName:
                      personFinden = Name/Kürzel genau oder eindeutiger Anfang ab 2 Zeichen, sonst NEUE Person {name, kuerzel initialen, intern} – ein Rückgängig-
                      Schritt mit der Zuordnung; inaktive gleichen Namens werden reaktiviert; Datalist-Auswahl (inputType insertReplacementText) ordnet sofort zu),
                      Chips „Aus dem Team“ (zustPlusP; im Feld max. 8 + „… alle“), „👥 aus den Stammdaten …“ (zustStamm, nur bei eingeschaltetem Anschluss).
                      Enter im Feld wird VOR der Dialog-Tastenbehandlung abgefangen. Geprüft mit der Maus im neuen leeren Projekt: Fenster aus der Tabelle,
                      zwei neue Namen getippt + Enter, ★ zum Lead, Team-Chip im nächsten Arbeitspaket; per Skript: Backlog-Zelle, Board-Karte, Eigenschaften
                      (Fokus bleibt im Feld), Anfangsbuchstaben, Datalist, Doppel abgewiesen, Rückgängig; 78 Ansichten, 48 Berichte, 46 Fenster/Panels ohne Fehler.
                      LEAD-WECHSEL (Build 19:43, Peters Wunsch): Wird jemand Lead (★, ▲ in die oberste Zeile, Ziehen an die Spitze, Item-Feld person), steht er
                      vorn und die BISHERIGE Lead-Person rückt ans ENDE (zustSchieb mit ziel 0: [neu, übrige in alter Reihenfolge, alter Lead]); Verschieben ohne
                      Lead-Wechsel bleibt positionsgenau. Hinweis im Fenster und in der Hilfe. Geprüft per Skript (★, ▲, Ziehen) und mit der Maus (★ im Fenster),
                      Tabelle „CN★, AB, …“ und Gantt-Beschriftung „★CN, AB, DE, BM“.
                      RECHNUNGEN NACH LEISTUNGSART + ÜBERGABE AN DIE BUCHHALTUNG (15.10.2026 abends, Build 20:58; Peters Auftrag „Auswertungen, in denen ich
                      Rechnungen generieren kann, die als Rechnungen in das Buchhaltungssystem überführbar und versendbar sind, gezielt zu einzelnen Leistungszeit-
                      punkten, Anlage mit Arbeitsstunden, Materialien, zugekauften Fremdleistungen, Lizenzen/Wartung/Betrieb für eigens für den Kunden angeschaffte
                      und ggf. angepasste Software; das Programm soll auch Nicht-IT-Projekte können; alle drei Projektwerkzeuge anpassen“ – gelesen als PM, Projekt-
                      Auswertung, dazu die Buchhaltung als Ziel; Kanban unverändert, seine Beträge kommen über den Austausch als pauschale Leistungen).
                      p2: KOSTENARTEN + material, wartung; LART (std Arbeitsstunden, pausch Pauschale Projektleistungen, mat, fremd, lwb, reise, sonst) = Leistungsart,
                      LART_POS (Reihenfolge), LART_FARBE (auch stil.css .lg-…), KA_LART (Kostenart → Leistungsart), lartVon(k) (k.lart übersteuert). Kosten neu: menge,
                      einheit, vk (Verkaufspreis je Einheit statt Aufschlag), lart, von/bis (Leistungszeitraum). abrechnung neu: email, ustid, kdnr, ref, land (Empfänger)
                      und Vorgaben std (ABR_STD person|rolle|bezug|satz), kost (ABR_KOST einzeln|art), anlage (ABR_ANLAGE einzeln|summen|keine), lief. Rechnung: pos.g
                      (alte Abrechnungen: aus q.a/Kostenart abgeleitet), anlage, lief, buha {uid, am, status uebergeben|entwurf|gestellt|teilbezahlt|bezahlt|storniert|
                      verworfen|geloescht, nr, datum, brutto, bezahlt, bezahltAm, versandt}. p3: abrQuellen(d, m, {von, bis = Stichtag, umfang = Plan-id mit Unterpunkten
                      (abrUmfang)}) = Einzelleistungen {k, a, id, g, datum, text, menge, einheit, preis, betrag, …}; abrPositionen(d, m, Q, {std, kost}) bildet die Posi-
                      tionen (gleicher Text mit verschiedenen Sätzen bekommt „– x € je Std.“), sortiert nach LART; abrVorschlag = beides; kostenVkEp(k), zeitraumText,
                      summeJeArt; abrStand.offenArt/abgerechnetArt. pa_abrechnung neu geschrieben: Ansicht (Kennzahlen, „Jetzt abrechenbar“ gegliedert mit lartChips/
                      posTabelleG, Liste mit Spalte Buchhaltung, Abrechnungsart, „Rechnungsempfänger und Vorgaben“ mit data-sd), Dialog abrDlg (AD: von/bis/umfang/
                      datum/titel/std/kost/anlage/lief, Einzelposten je Leistungsart an-/abwählbar, adNeu/adListe/adVorschau/adFeld/adWahl; „vom“ = früheste offene
                      Leistung, wenn früher als Projektbeginn bzw. letzte Abrechnung – sonst fehlten heute gebuchte Stunden in einem Projekt mit Beginn nächste Woche),
                      abrAnlegen(uebergeben), panelRechnung (Gruppen, Leistungsart freier Positionen, Anlage, Buchhaltungs-Kasten, gesperrt solange buhaAktiv),
                      anlageDaten(r) = EIN Modell für Druck (anlageHtml) und Buchhaltungs-PDF: {titel, projekt, abrechnung, zeitraum, zus, abschnitte [{g, h, kopf,
                      rechts, zeilen (fertige Texte), summe, hinweis}]}; Stundenzeilen über verteile() centgenau zur Position; bei Lizenzen Hinweis „Einrichtung und
                      Anpassung dazu: „Paket“ x Std.“; leistungsZeitraum(r) (Stunden + Lizenz-/Wartungszeitraum). ÜBERGABE: IndexedDB 'mc4y-uebergabe' Store kv,
                      'r:<uid>' = Übergabe (format 'mc4y-rechnungsuebergabe' version 1, quelle, kunde aus abrEmpfaenger() – Anschrift-Textfeld wird in Straße/PLZ/Ort/Land
                      zerlegt –, pos mit g/gl, anlage, status neu|uebernommen|verworfen), 's:<uid>' = Status der Buchhaltung; BroadcastChannel 'mc4y-uebergabe' (neu,
                      zurueck, status); Datei *.rechnung.json (gleiches Objekt) für andere Browser. abrUebergeben (nicht in der Beispielansicht), abrUebDatei,
                      abrUebZurueck (nur solange status neu), abrSync (Start, Fensterfokus, Nachricht; aendernStill in p4 = ohne Rückgängig-Schritt; gestellt/bezahlt/
                      storniert setzen den Status der Abrechnung, storniert gibt die Leistungen frei). p9: Kosten-Tabelle mit Menge/Einheit/„auf der Rechnung“,
                      Zeilen wählbar → panelKosten (Leistungsart, Aufschlag, fester VK, Leistungszeitraum; abgerechnet gesperrt; #pcalc = kostenCalcHtml), Block
                      „An den Auftraggeber weiterberechnet“ je Leistungsart, Filter „noch nicht abgerechnet“, CSV erweitert; „Zeit buchen“: Person eintippen = vorhandene
                      oder NEU (personFinden). pc: Bericht „Abrechnung, Rechnungen und Deckungsbeitrag“ mit Balken je Leistungsart, Spalte Buchhaltung, Aktionsleiste
                      .awaktion „🧾 Rechnung erstellen …“ über dem Bericht. pz: Tabelle „Nach Leistungsart“ (abgerechnet/abrechenbar je Projekt), offene Rechnungen mit
                      Buchhaltungsstand (liest 's:' im selben Browser, PZ.buha). Beispiel Hybrid (CRM, im Blogbild nicht sichtbar): weiterberechnete Lizenzen 10 Nutzer,
                      Wartung je Monat, Datenübernahme-Dienstleister, Tablets 4 Stück (fester VK), gedruckte Kurzanleitungen 25 Stück; alle Beispiele mit E-Mail/Kundennummer
                      (erfunden, .invalid). Kennzahlen der vier im Blogbild sichtbaren Beispiele unverändert. Geprüft: leeres neues Projekt mit der Maus (Kanban-Vorlage,
                      Lizenz in den Eigenschaften mit Tab-Folge, Zeit mit NEUER Person, Verrechnungssatz, Empfänger mehrzeilig, Rechnung erstellen + übergeben), echte
                      Übergabe an die Buchhaltung im zweiten Tab (Banner per BroadcastChannel, übernehmen, stellen, Versand vermerkt, Zahlung) → Projekt zeigt „bezahlt ·
                      versandt“; per Skript Storno (Leistungen wieder frei), verwerfen + erneut übergeben, übernommen + Entwurf gelöscht, zurücknehmen, Datei-Übergabe
                      (Kunde wiedererkannt); 194 Prüfungen (6 Beispiele × alle Ansichten, Berichte, Panels, Anlagen, Übergabe-Objekte, 8 Gliederungs-Kombinationen mit
                      Summenprüfung) ohne Fehler/NaN, normalize idempotent, Projekt-Auswertung alle Ansichten; 17 Übergaben → PDFs (bis 5 Seiten) mit PyMuPDF angesehen.
                      Testdaten der Vorschau entfernt.
                      ANSCHLUSS AN DEN 8EM-MODELLER (15.10.2026 abends, Build 21:41; Gegenstück zu „PROJEKTMANAGEMENT-ANSCHLUSS“ beim 8EM-Modeller). p2 normalize:
                      doc.kennung (stabile Projektkennung, sonst 'pj'+Zeit+Zufall; Beispiele 'bsp-<modell>' in pe_beispiel), doc.bezug8em {modell, element, name, am}
                      (aus einem übernommenen Projektauftrag), doc.projektstand wird beim Öffnen entfernt. p4: dateiInhalt() = Datei MIT „projektstand“ (für Abgleich
                      über die Datei), entwurfSichern() ruft standBereitstellen(); dateiOeffnen erkennt format 'mc4y-projektauftrag' → paDialog. pg_8em.js (nur PM, in
                      bauen.py vor pf_start.js): projektstand8em(d) = berechneter Stand (format 'mc4y-projektstand', uid = kennung; Ampeln + warum, fortschritt, start/
                      ziel/soll/prog/verzug, spi/cpi, kosten {budget, bac, ac, eac, ev, arten [{art, plan, ist, prog}]} – Sachkosten je Kostenart aus d.kosten, Personal =
                      bac − Sachplan bzw. max(Ist, eac − Sachprognose), intern/extern nach geplanten Kosten getrennt –, erloes, ms aus dem Plan (plan = Basisplan, sonst
                      Soll; prog, ist, st, gate, ph, ziel, wer, mta), ph (Gliederungsebene 0–1), risiken (Top 8), team mit Rollen/sd, hinweise), standBereitstellen
                      (1,2 s verzögert, nur bei Inhalt, nicht in der Beispielansicht, nur bei Änderung → 'ps:'+kennung + BroadcastChannel 'projektstand'). Projektauftrag:
                      paLaden (Start, Fokus, Nachricht 'auftrag'; nur status neu), paBanner in der Übersicht (auch Start ohne Projekt), paDialog (Vorgehensmodell und
                      „Struktur als Vorlage“ wählen; Hinweis, dass ein geöffnetes Projekt ERSETZT wird), auftragDoc(o, modell, vorlage) = leeresDoc + Projektdaten (Budget
                      → budgetPlan, Status), Personen mit Rollen (Auftraggeber/in, Projektleitung, Steuerkreis …), Meilensteine mit Termin als „frühestens am“ (start;
                      gleichnamige der Vorlage werden genutzt), verknüpfte Ziele als „Zahlt ein auf: …“ in der Beschreibung, kennung + bezug8em; paAnlegen (Rückfrage nur
                      bei ungespeicherten Änderungen, ein Rückgängig-Schritt) setzt 'pa:' status uebernommen (+ Nachricht 'auftrag-uebernommen'), paVerwerfen, paDatei.
                      „Projekt und Team › Basisplan und Austausch“: bezug8emHtml (Erklärung, Kennung, Herkunft, offene Aufträge, „📂 Projektauftrag aus einer Datei …“,
                      „↻ Nach Projektaufträgen sehen“). Hilfe „Zusammenarbeit mit dem 8EM-Modeller“; stil.css .pa-band. Geprüft zusammen mit dem Modeller (siehe dort)
                      sowie alle sechs Beispiele/Ansichten ohne Fehler.
                      PROJEKTAUFTRAG AUS DEM VERTRIEBS-NAVIGATOR (16.10.2026, Build 10:23): gleiches Format 'mc4y-projektauftrag' ('pa:<uid>'), aber quelle.werkzeug
                      'vertrieb' (quelle.chance, account, bestand) und zusätzlich o.kunde (wie bei der Kundenübergabe) sowie projekt.auftraggeberExtern. pg_8em.js:
                      paVertrieb/paVon/paHerkunft beschriften Banner, Dialog, Toasts und Rückfragen nach der Herkunft (vorher fest „aus dem 8EM-Modeller“);
                      auftragDoc setzt bei Vertrieb d.bezugVertrieb {chance, account, bestand, am} statt bezug8em (p2 normalize behält es; Anzeige in bezug8emHtml),
                      Kunde → P.kunde, P.kundeAnschrift und Rechnungsempfänger (abrechnung kunde/anschrift/email/ustid/land), Auftraggeber/in als externe Person.
vertrieb-editor.html  (seit 16.10.2026, Peters Frage „Kennst Du den Sales-Navigator von LinkedIn? Wenn ja, kannst Du mir so ein Tool nachbauen.“) „Vertriebs-Navigator“
                      (Arbeitstitel): Akquise-Werkzeug nach dem Vorbild des LinkedIn Sales Navigator, aber OHNE Zugriff auf LinkedIn. Mit Peter abgestimmt (AskUserQuestion):
                      kein Scraping, keine Automatisierung von LinkedIn, keine Browser-Erweiterung (Nutzervereinbarung, DSGVO Art. 14); Daten nur aus eigener Erfassung,
                      eigenem LinkedIn-Datenexport (Connections.csv), CSV/Excel, Stammdaten; Recherche-Links öffnen nur auf Klick eine Suche im eigenen Tab. Zweck: eigene
                      Akquise UND neutral für Kunden/Website (fiktives Beispiel, GPL). Alle vier Zusatzbausteine und alle drei Anbindungen gewählt. Markenhinweis „LinkedIn
                      und Sales Navigator sind Marken der LinkedIn Corporation … nur beschreibend“ (Hilfe) – nie im Namen. Eigenständiges Werkzeug, NICHT eigenmächtig
                      hochladen. QUELLEN in vertrieb-quellen/ (nicht hochladen): seite.html (Gerüst, Hilfe; Schließen oben helpX, unten closeHelp), stil.css (aus
                      itsm-quellen abgeleitet), v1_grund (Hilfen, Datum inkl. datumLesen für „15 Oct 2026“/„15. Okt. 2026“/Excel-Zahl, Geld in Cent ct/eur/eurK,
                      IndexedDB 'mc4y-vertrieb' nur Dateihandle, Übergabe-Speicher uebSetzen/uebAlle/uebMelden, csvLesen mit Trennzeichen aus den ersten 25 Zeilen –
                      LinkedIn beginnt mit „Notes:“ –, xlsxLesen aus der Buchhaltung, Dialog/Menü/kopieren, Diagramme balken/saeulen/trichter, druckFenster),
                      v2_daten (Konstanten, normalize, Passung, Kontaktwege, Art. 14, Löschfrist, aendern/Rückgängig 150, Speichern), v3_ansichten (ui, render, Übersicht,
                      Leads/Accounts mit erweiterter Suche, Listen und gespeicherte Suchen, Pipeline, Signale/Wiedervorlagen/Kontaktverlauf, Datenschutz), v4_panel
                      (Eigenschaften Lead/Account/Chance, Zuordnen mit Neuanlage, Tags, Kontaktwege, Herkunft/Art. 14, Kontaktverlauf, Nachrichten aus Vorlagen,
                      Recherche-Menü, Auskunft Art. 15), v5_gremium (Einkaufsgremium-Karte), v6_import (Import aus Datei oder eingefügter Tabelle, vCard, Stammdaten-
                      Übernahme, CSV), v7_auswertung, v8_einstellungen (Kundenprofil, Personas, Einstellungen), v9_uebergabe (Buchhaltung/Projektmanagement),
                      va_beispiel, vb_start (ACT, Ereignisse, Start), vd_recherche (Ansicht „🔎 Recherche“, siehe RECHERCHE unten), vc_stammdaten (Adapter; start() am ENDE). ÄNDERUNGEN NUR IN vertrieb-quellen/, dann `py vertrieb-quellen/bauen.py` (ruft editoren-version.py
                      auf; --ohne-version für Zwischenstände – dann fehlen Logo/Build/Stammdaten-Anschluss). NAMES und eigener PRIVACY-Satz in editoren-version.py.
                      FORMAT 'mc4y-vertrieb' version 1, *.vertrieb.json: einst {name, absender {name, firma, email, telefon, web, anschrift}, loeschMonate 24, art14Tage 30,
                      kontaktTage 90, branchen, verlust (Verlustgründe), phasen [{id, name, p %, art offen|gewonnen|verloren}] (je Art mindestens eine), vorlagenStd,
                      zuordnungen {Kennung der Kopfzeile (zuoSig): [Feld je Spalte]} = gemerkte Spaltenzuordnungen beim Import, höchstens 30},
                      icp {branche|groesse|region|funktion|hierarchie: {w 0–5, werte}, schwelleA 70, schwelleB 40, beschreibung}, personas [{name, beschreibung,
                      funktionen, hierarchie, ziele, schmerzen, einwaende, ansprache}], accounts [{name, branche, groesse 1–5 (GROESSE), ma, strasse, plz, ort, region,
                      land, web, linkedin, telefon, email, ustid, status ACC_STATUS, tags, notiz, herkunft {q, am, d}, karte {leadId: {x, y}}, bez [{id, a, b, art BEZ}]}],
                      leads [{anrede, titel, vorname, nachname, position, account, funktion/hierarchie ('' = automatisch aus der Position: autoFunktion FUNK_KW,
                      autoHier HIER_KW – Reihenfolge der Regeln beachten, „Assistenz der Geschäftsführung“ = Assistenz), persona ('' automatisch, '-' keine), email,
                      telefon, mobil, linkedin, ort, verbunden, status LEAD_STATUS, tags, notiz, gremium GREMIUM, haltung, einfluss 0–5, herkunft {q HERKUNFT, am, d},
                      art14 {am, weg, ausnahme}, kw {email|telefon|linkedin|post: {st, am, n}}, gesperrt (Datum des Widerspruchs), behalten {bis, grund}, imp {firma,
                      position, am} = Stand des letzten Imports}], chancen [{nr VC-001, titel, account, lead, phase, wert (Cent), p (null = aus der Phase), abschluss,
                      naechster, quelle, notiz, grund, verlauf, gewonnenAm, verlorenAm, ueb {bh|pm: {uid, am, st neu|uebernommen|verworfen, info, kennung}}}],
                      aktivitaeten [{t, art AKT, lead, account, chance, text}], aufgaben (Wiedervorlagen) [{faellig, text, lead, account, chance, erledigt}], signale
                      [{t, art SIGNAL, lead, account, text, quelle manuell|import, st neu|gesehen|erledigt}], listen [{name, art lead|account, ids}], suchen [{name, art,
                      f (Filter wie LF0/AF0), gesehen [ids], am}], vorlagen [{name, kanal email|linkedin|brief|telefon|info14, betreff, text}], sperrliste [{name, firma,
                      email, telefon, linkedin, am, umfang werbung|alles, notiz}], zaehler, stammdaten. normalize idempotent (geprüft).
                      PASSUNG (score): je ICP-Merkmal mit Gewicht und Werten 1 = passt, 0,5 = Nachbarstufe (Größe, Hierarchie), 0 = passt nicht, fehlende Angabe = 0 mit
                      „?“; Region über Bundesland oder PLZ-Präfix; Account nur Firmenmerkmale; Klassen A/B/C nach Schwellen; personaVorschlag nach Funktion (2) +
                      Hierarchie (1,5). KONTAKTWEGE (kontaktweg, nur Hinweise, keine Rechtsberatung): E-Mail nur mit Einwilligung (§ 7 Abs. 2 UWG, Bestandskunden-Hinweis
                      § 7 Abs. 3), Telefon B2B mutmaßlich, LinkedIn „vorsichtshalber wie E-Mail“, Brief bis zum Widerspruch. ART. 14 (art14): Herkunft direkt (Art. 13) |
                      nicht direkt → Frist art14Tage ab herkunft.am | offen → „pruefen“. LÖSCHFRIST (loeschPruefen): kein Kontakt seit loeschMonate, keine offene Chance,
                      kein Kunde, nicht „behalten bis“. WIDERSPRUCH: Sperrlisten-Eintrag (nur Wiedererkennungsdaten), Lead gesperrt bzw. auf Wunsch gelöscht; Import
                      überspringt Treffer (gesperrtTreffer: E-Mail, Profil-Adresse, Telefon, Name + Firma).
                      IMPORT: Kopfzeile = Zeile mit den meisten erkannten Spalten (IMP_SYN, deutsch/englisch), LinkedIn erkannt → Herkunft linkedin; Spalten zuordenbar;
                      Vorschau aus importPlanen (neu, ergänzt, unverändert, Sperrliste, doppelt, ohne Namen, nur Firma); Abgleich leadFinden (Profil-Adresse liUrl, E-Mail,
                      Name + firmaKey ohne Rechtsform); Accounts über accountFinden/firmaKey; bei vorhandenen Leads Signale neue_firma (Firma geändert: Lead wechselt den
                      Account, Gremium-Rolle wird zurückgesetzt), befoerderung (höhere Hierarchiestufe) bzw. position, neuer_kontakt (verbunden ≥ letzter LinkedIn-Import);
                      ein Rückgängig-Schritt; Tag „LinkedIn TT.MM.JJJJ“ bzw. „Import …“, Liste danach gefiltert. Seit 16.10.2026: Accounts auch über die Website-
                      Domain erkannt (domainVon), Vorschau mit Häkchen je Zeile (ui.imp.aus, IMP_WAEHLBAR), Spalten quelle (Fundstelle → herkunft.d) und begruendung
                      (an die Account-Notiz angehängt „Begründung (<Herkunft>): …“), Outlook-Spaltennamen in IMP_SYN, vCard (.vcf bzw. Inhalt BEGIN:VCARD; vcardZeilen:
                      Faltung, quoted-printable, N/FN/ORG/TITLE/EMAIL/TEL/ADR/URL/NOTE), Spaltenzuordnung je Kopfzeile gemerkt (einst.zuordnungen), Import-Dialog mit
                      „📂 Datei wählen“ und „📋 Tabelle einfügen“. Zuordnungsfelder (data-zu account|lead|branche|persona|
                      gremPerson, Tags data-tagadd, Kundenprofil data-icpadd): Auswahl aus der Vorschlagsliste (input inputType insertReplacementText) sofort, Enter erst
                      beim Loslassen (keyup), sonst beim Verlassen; unbekannte Namen legen Account/Lead/Branche/Persona/Tag an. Textfelder im Panel zeichnen das Panel NICHT
                      neu (feldTippen/feldFertig, nur Kopf über panelKopfAuffrischen) – Tab-Folge bleibt; „↗“-Links haben tabindex -1 (lagen sonst in der Tab-Folge).
                      EINKAUFSGREMIUM: SVG-Karte je Account, Knoten 180 × 64 (Rolle = Farbe, Haltung = Punkt, Einfluss = Balken), automatische Lage nach Hierarchiestufe
                      (gremLagen; beim ersten Verschieben werden alle Lagen gespeichert), Ziehen per Zeiger-Ereignissen, Griff ↗ ziehen oder anklicken + Ziel = Verbindung
                      der in der Leiste gewählten Art, mehrere Linien zwischen denselben Personen nebeneinander, Linie anklicken → Leiste (Art, ⇄, entfernen, Entf),
                      gremHinweise (kein Entscheider/Champion, Blocker mit Einfluss ≥ 4, lange kein Kontakt, nur eine Ansprechperson), Druck und SVG.
                      PIPELINE: Board mit Zeiger-Ziehen (kein HTML5-DnD – ließ sich im Vorschau-Browser nicht auslösen; Geist-Karte .karte.geist, ui.klickSperre), Liste,
                      phaseWechsel → gewonnen: Account/Lead „Kunde“, gewonnenDialog (Datum, Häkchen Buchhaltung/Projektmanagement) | verloren: verlorenDialog (Grund).
                      ÜBERGABE (v9): anBuchhaltung → 'k:<uid>' format 'mc4y-kundenuebergabe' (siehe Buchhaltung „KUNDEN AUS DEM VERTRIEBS-NAVIGATOR“), anProjekt →
                      'pa:<uid>' format 'mc4y-projektauftrag' mit quelle.werkzeug 'vertrieb', kunde, personen = Gremium (Hauptkontakt als Auftraggeber/in, extern),
                      leitung = Absender; ohne gemeinsamen Speicher bzw. „⬇ Datei“ als *.kunde.json/*.projektauftrag.json; uebStatusLaden (Start, Fokus, Nachrichten
                      kunde-/auftrag-uebernommen|verworfen) übernimmt Stand und Kundennummer still (aendernLeise). Nicht im Beispiel.
                      KUNDEN-NAVIGATOR (16.10.2026, Build 16:13): anKundenNav → 'kb:<uid>' format 'mc4y-kundenbetreuung' (kbObjekt: kunde = kundeObjekt + branche,
                      kontakte = Leads des Accounts OHNE gesperrte {vid, Anrede…Mobil, gremium, haupt, kw}, verlauf = letzte 40 Aktivitäten von Account/Leads/Chance,
                      chance {nr, titel, wert, gewonnenAm, notiz}) bzw. *.kundenbetreuung.json; c.ueb.kn (normalize kennt 'kn'), dritte Zeile im Übergabe-Abschnitt,
                      Häkchen im Gewonnen-Dialog; uebStatusLaden liest auch 'kb:'. ZUSATZGESCHÄFT ve_kunden.js (vor vc_stammdaten): 'vc:<uid>' format 'mc4y-chancenuebergabe'
                      bzw. *.zusatzgeschaeft.json (dateiOeffnen erkennt es) → vcBanner in Übersicht und Pipeline (VIEWS.pipeline umhüllt), vcNehmen: Account per
                      accountFinden (Status Kunde), Lead per leadFinden (nie gesperrt; neu mit kw aus dem Kunden-Navigator), Herkunft HERKUNFT 'kundennav' („Bestehende
                      Kundenbeziehung“, Art. 13), Chance mit c.vc {uid, kunde} (normalize behält vc), quelle „Bestandskunde“. vcStatusMelden schreibt 'vcs:<uid>'
                      {chanceNr, phase, art offen|gewonnen|verloren, wert, abschluss} – entprellt aus geaendert() (vcStatusSpaeter). uebDbDa: vcLaden liest nur, wenn
                      'mc4y-uebergabe' existiert. eur() zeigt Cent jetzt immer zweistellig (vorher „1.570,8 €“). Hilfe-Absätze ergänzt.
                      RECHERCHE (16.10.2026, Peters Auftrag über die Hauptsitzung „Baue Option 1 und 2 ein …“ und „eine eigene Recherche, die ich mit M365, Claude oder Vibe
                      anstelle und die ich händisch übernehmen kann“; vd_recherche.js, Ansicht 'recherche' nach Accounts, Reiter RE_TABS links|offen|eigen, Zustand ui.re
                      wird nicht gespeichert). STUFE 1 Suchlinks (reLinksHtml) aus dem Kundenprofil: Websuche (DuckDuckGo), OpenStreetMap-Suche, Gelbe Seiten, LinkedIn
                      Unternehmen/Personen (nur eigenes Konto), North Data (Startseite, Suchbegriff wird kopiert); öffnen nur einen Tab, lesen nichts aus.
                      STUFE 2 Firmensuche in offenen Daten (nur Firmen, keine Personen): OpenStreetMap über Overpass (OVERPASS: overpass-api.de, Ausweich
                      overpass.private.coffee; POST data=…; nwr je Kategorie OSM_KAT (18, Kategorie → Branche) im Gebiet area[boundary=administrative][name]
                      [admin_level] oder im Umkreis um einen place-Knoten; out center tags 600). „🔍 Gebiet prüfen“ sucht erst den genauen Namen, sonst per Regex
                      (25–60 s). Einzelpraxen (healthcare doctor/physiotherapist) bewusst nicht – meist Personennamen. Wikidata: Region per wbsearchentities
                      (origin=*), Firmen per SPARQL (Sitz P159 in P131* der Region, P31/P279* Q4830453, P856, P1128, P281, P452; aufgelöste P576 werden mitgeliefert
                      und im Werkzeug aussortiert – FILTER NOT EXISTS machte die Abfrage mehr als doppelt so langsam und lief in Zeitüberschreitung; LIMIT 500;
                      wdRegion leitet das Bundesland aus Name/Beschreibung ab, „Sachsen“ nicht in „Niedersachsen“). Enter im Gebietsfeld = Region bzw. Firmen suchen.
                      Ergebnis mit Häkchen, Passung eines vorläufigen Accounts, Status neu/vorhanden (accountFinden: Name ohne Rechtsform oder Website-Domain),
                      „passende wählen (A/B)“; reUebernehmen = ein Rückgängig-Schritt, herkunft q osm|wikidata, d = Datensatz + Adresse + Lizenz (ODbL bzw. CC0),
                      Tag „Firmensuche <Datum>“. FEHLERBILDER: abgelehnte/überlastete Anfragen kommen im Browser ohne CORS-Kopfzeile an (sehen aus wie „keine
                      Verbindung“) → Meldungen nach Art und Dauer des Fehlers; Wikidata 429 = Anfragebudget je Minute aufgebraucht (beim Testen schnell erreicht),
                      Zeitüberschreitung ebenfalls ohne CORS (TypeError nach > 30 s). Die allgemeine CSP der Website (default-src 'self') würde die Abfragen
                      blockieren; seit 16.10.2026 (Peters „JA. erweitere das“) setzt build.py für Dateien namens vertrieb-editor.html (jeder Ordner) eine eigene CSP
                      mit connect-src 'self' + genau den vier Servern (csp_verbindungen, siehe „Gestaltung“) – auf der Website geht die Suche also, sobald das
                      Werkzeug in downloads/ liegt und die neue .htaccess hochgeladen ist. Der Hinweis im Werkzeug („von einer Website mit strengen Sicherheits-
                      regeln“) bleibt für fremde Websites. PRIVACY-Satz in editoren-version.py nennt die Übertragung
                      der Suchbegriffe. STUFE 3 Eigene Recherche – KEINE anbieterspezifischen Vorlagen (Peters Korrektur: kein Vibe-/Dynamics-/Outlook-Sonderformat):
                      rechercheAuftrag() = neutraler Auftrag (Profil, Regionen, Personas, optional Ausschlussliste der vorhandenen Firmen/Domains, Regeln: nur
                      öffentliche Quellen mit Fundstelle, nichts erfinden, keine E-Mail-Adressen sammeln; Tabelle „Firma | Website | Ort | Branche | Größe |
                      Ansprechperson | Funktion | Quelle (URL) | Begründung“; Platzhalter bei leerem Profil), „📋 Auftrag kopieren“/„⬇ Als Textdatei“ nehmen den
                      bearbeiteten Text; „📋 Tabelle einfügen“ (einfuegenDialog, tabelleAusText: Markdown inkl. **fett** und [Text](URL) → „Text ⟨URL⟩“, Tabulator,
                      Semikolon/Komma) → dieselbe Vorschau wie der Import, Herkunft recherche mit Knöpfen IMP_WERKZEUGE (d „Eigene Recherche: <Werkzeug>, <Datum>“),
                      Tag „Recherche <Datum>“; „⚡ Schnellerfassung“ (schnellDialog; Speichern bzw. Speichern und nächste; vorhandene Firma/Person wird ergänzt).
                      Hinweise Art. 14 DSGVO und § 7 UWG bei Auftrag, Vorschau und Suche.
                      STAMMDATEN (vc_stammdaten): MC4Y_SD_ADAPTER ohneRollen, Leads = Personen (name, email, telefon, mobil, funktion = Position), Accounts = Organisationen
                      (name, email, telefon, strasse, plz, ort, land, ustid); gesperrte Leads werden nicht übergeben; sdZusammen (Lead/Account zusammengeführt: Tags, leere
                      Felder, Karte/Beziehungen, dann id-Ersetzung); gesperrte Felder readonly (sdFest). Übernahme „👥 aus den Stammdaten …“ (Personen + Organisationen,
                      Herkunft stammdaten).
                      FIRMA (16.10.2026, Build 11:05; siehe stammdaten-editor.html „FIRMA EINER PERSON“): der Account eines Leads = Stammdaten-Feld personen[].firma.
                      lesen() liefert firmaLid = l.account, Option firmaAnlegen; sdAnwenden setzt u.firmaLid über sdFirma(l, id) (Lead verlässt Karte/Beziehungen des
                      alten Accounts; bei Wechsel zwischen zwei Firmen werden gremium/haltung/einfluss zurückgesetzt – wie beim Import-Signal „neue Firma“) bzw. legt bei
                      u.firmaNeu EINEN Account je Stammdaten-Organisation an (Herkunft stammdaten, „Firma von …“) und verknüpft ihn sofort per a.zuordnen.
                      wandeln { land: v => v || 'DE' } – normalize setzt bei Accounts ohne Land „DE“; ohne diese Umrechnung meldete jeder Abgleich Organisationen ohne
                      Land erneut („Aus den Stammdaten übernommen …“). Gesperrte Leads (abgeglichen, nicht mehr „eigen“): Account-Feld readonly, Hinweis nennt „Firma
                      (Account)“, zuordnen() lehnt Account-Wechsel ab, gremPerson (Person einem anderen Account zuordnen) bricht mit Hinweis ab, Hauptkontakt einer Chance
                      überträgt den Account nicht mehr auf einen gesperrten Lead. Import-Signal „neue Firma“ ändert den Account weiter direkt – bei gesperrten Leads
                      setzt der nächste Abgleich ihn zurück (wie bei den übrigen gesperrten Feldern). Erklärung im Verknüpfen-Dialog nennt die Firma.
                      Geprüft im Vorschau-Browser (eigener Ursprung): Erstverknüpfung über den Dialog (24 Leads mit der Firma ihres Accounts, 2 ohne), Firma im
                      eingebetteten Stammdaten-Editor per Auswahl geändert → Account gewechselt, Gremium-Rolle zurückgesetzt, Feld gesperrt; Organisation, die es hier
                      nicht gab → Account angelegt und verknüpft, folgender Abgleich ohne Schreiben/Meldung; bei „zu prüfen“-Leads Account per Tastatur geändert und
                      neu eingetippt → Firma bzw. neue Organisation in den Stammdaten; Zusammenführen zweier Organisationen; Umstieg alter Zuordnungen ohne s.firma;
                      Kanban (ohne firmaLid) unverändert verknüpft. Testdaten danach entfernt.
                      Beispiel (#beispiel[&ansicht=…&tab=…&modus=…&sel=Name|Firmenanfang|VC-Nr.]): fiktive „Muster Beratung (Beispiel)“, 12 Accounts, 25 Leads mit
                      Platzhalter-Namen (Musterfrau, Beispiel, Probe …), 9 Chancen mit Verlauf, Gremien mit Beziehungen, Signale, Wiedervorlagen, Listen, 3 gespeicherte
                      Suchen mit „neu“, Sperrliste, Art.-14-Fälle (offen, überfällig, erledigt, Art. 13), ein Löschfrist-Fall; Termine relativ zu heute, Adressen .example.
                      GEPRÜFT im Vorschau-Browser: alle Ansichten × Reiter × Modi, alle Panels, alle Gremium-Karten, gespeicherte Suchen, 91 Filter-Kombinationen, leeres
                      Modell (über 200 Darstellungen) ohne Fehler/NaN, normalize idempotent, 375 px ohne Querscrollen; mit der Maus: Gremium (Person wählen, verschieben,
                      Griff auf andere Person ziehen, Entf, großer Rückgängig-Pfeil, „＋ Person“ mit Neuanlage), Pipeline-Karte in andere Phase gezogen, im leeren
                      neuen Bestand Lead per Tastatur mit neuem Account (Tab-Folge), Tags per Enter, Chance mit neuem Hauptkontakt und Betrag, „Gewonnen“ → Übergabe;
                      echte Übergabe in drei Tabs: Buchhaltung (Browser-Ablage) übernimmt Kunden mit der Maus → Kundennummer zurück, Projektmanagement legt Projekt mit
                      Kunde/Rechnungsempfänger/externer Auftraggeberin an → Stand zurück; Import einer erfundenen Connections.csv (Notes-Zeilen, Sperrliste, Doppel,
                      vorhandener Lead) und eines zweiten Exports (neue Firma, Beförderung, neue Position, neue Verbindung) sowie einer selbst erzeugten .xlsx;
                      Nachricht aus Vorlage + Vermerk, Information Art. 14, Widerspruch (mit und ohne Löschen), behalten, Mehrfachauswahl (Liste, Tag, Status,
                      Wiedervorlage), Signal → Wiedervorlage, verloren mit Grund; Stammdaten (gestempelt): Erstverknüpfung über den Dialog (17 Einträge „zu prüfen“,
                      gesperrter Lead nicht), Änderung in den Stammdaten kommt an (Name, E-Mail, Position, Anschrift), Felder danach schreibgeschützt, Neuanlage geht
                      dorthin, Zusammenführen hängt Kontaktverlauf um. RECHERCHE (16.10.2026): Suchlinks; OpenStreetMap <ort> (Gebiet prüfen und suchen mit der Maus,
                      15 Firmen) und Wikidata Landkreis Leer (Region per Enter, 25 Firmen, 6 mit der Maus übernommen); Auftrag; Markdown-Tabelle mit Link und Fettdruck
                      eingefügt (Zeile per Maus abgewählt, vorhandene Firma ergänzt, Rückgängig/Wiederholen), Tabulator-Tabelle mit geänderter Spaltenzuordnung →
                      beim nächsten Einfügen gemerkt; Schnellerfassung (vorhandene Firma, Person beim zweiten Mal ergänzt statt doppelt); vCard 2.1/3.0 mit
                      quoted-printable; 69 + 18 Darstellungen (Beispiel/leer) ohne Fehler/NaN, 375 px. Testdaten danach entfernt. NICHT geprüft: Speichern/Öffnen über den Dateidialog und Nutzung über
                      file:// in Chrome/Edge (gemeinsamer Speicher der Werkzeuge im selben Ordner), Teilen/mailto (nicht ausgelöst), Recherche-Links (nicht geöffnet).
kunden-editor.html    (seit 16.10.2026, Peters Auftrag „Baue mir ein CRM-Modul, das mit dem Buchhaltungsmodul, dem Stammdatenmodul und dem Akquise-Navigator
                      zusammenarbeitet“; mit Peter abgestimmt (AskUserQuestion): Abgrenzung KUNDENBETREUUNG – Neukunden bleiben im Vertriebs-Navigator, gewonnene Kunden
                      kommen per Übergabe, Zusatzgeschäft geht als Verkaufschance zurück; alle vier Bausteine (Anliegen/Beschwerden, Verträge/Fristen, Einwilligungen/
                      Kontaktwege, Kundenwert/Zufriedenheit); alle drei Buchhaltungs-Abläufe (Umsätze/offene Posten, Leistungen zur Abrechnung, Zahlungserinnerungen);
                      Name „Kunden-Navigator“). Eigenständiges Werkzeug, NICHT eigenmächtig hochladen; NAMES-Eintrag in editoren-version.py (Standard-PRIVACY-Satz: überträgt
                      nichts ins Internet). QUELLEN in kunden-quellen/ (nicht hochladen): seite.html (Gerüst, Hilfe; helpX/closeHelp), stil.css (Kopie aus vertrieb-quellen +
                      Ergänzungen am Ende), k1_grund (aus v1_grund: Hilfen, Datum, Geld, IndexedDB 'mc4y-kunden' nur Dateihandle, Übergabe-Speicher; NEU dbVorhanden/uebDa –
                      uebHolen/uebAlle lesen nur, wenn 'mc4y-uebergabe' existiert), k2_daten (Modell, normalize, Nachschlagen, Vertragsfristen, Kennzahlen, ABC, Zufriedenheit,
                      stille Kunden, Ampel, Kontaktwege, aendern/Rückgängig 150, Speichern), k3_ansichten (ui, render, Übersicht, Kunden, Anliegen-Board/Liste, Verträge,
                      Wiedervorlagen/Verlauf, Einwilligungen und Datenschutz), k4_akte (Panels Kunde/Ansprechperson/Anliegen/Vertrag/Leistung, Felder, Zuordnen mit Neuanlage,
                      Nachrichten aus Vorlagen, Auskunft Art. 15), k5_buchhaltung, k6_vertrieb (+ anderenLaden), k7_auswertung, k8_einstellungen (+ Import), k9_beispiel,
                      ka_start (ACT, Ereignisse, Start), kb_stammdaten (Adapter; start() am ENDE). ÄNDERUNGEN NUR IN kunden-quellen/, dann `py kunden-quellen/bauen.py`
                      (ruft editoren-version.py auf; --ohne-version für Zwischenstände – dann fehlen Logo/Build/Stammdaten-Anschluss).
                      FORMAT 'mc4y-kunden' version 1, *.kunden.json: einst {name, absender, stillTage 180, vorlaufTage 60, ehemaligMonate 36, abcA 80, abcB 95, fristTage
                      {hoch 1, mittel 3, niedrig 7 Werktage}, branchen, bh {verbunden, bestand}, vorlagenStd}, kunden [{name, kdnr, art firma|person, status aktiv|ruhend|
                      gekuendigt|ehemalig, branche, Anschrift, web, telefon, email, ustid, betreuer, seit, potenzial, wichtig, tags, notiz, herkunft {q, am, d}, quelle {werkzeug
                      'vertrieb', uid, chance, chanceNr, vertriebAccount}, bh {id, kdnr} (Verknüpfung zur Buchhaltung), bhUeb {uid, am, st}}], kontakte [{kunde, anrede, titel,
                      vorname, nachname, position, rolle ROLLEN (haupt, entscheidung, fachlich, buchhaltung, einkauf, technik, sonst), email, telefon, mobil, bevorzugt, aktiv,
                      widerspruch (Datum, Art. 21), herkunft, quelle {vid}}], aktivitaeten [{t, art AKT, kunde, kontakt, anliegen, vertrag, text}], aufgaben (Wiedervorlagen),
                      anliegen [{nr AN-001, kunde, kontakt, art ANL_ART (anfrage, beschwerde, reklamation, aenderung, stoerung, lob, sonst), titel, beschreibung, prio, status
                      ANL_ST neu|bearbeitung|wartet|geloest|geschlossen, kanal, eingang, frist (+ fristGesetzt), zustaendig, loesung, geloestAm, zufrieden 0–5, verlauf}],
                      vertraege [{nr VT-001, kunde, ansprech, titel, art, status entwurf|aktiv|gekuendigt|beendet, wert (Cent je Rhythmus), rhythmus monat|quartal|jahr|einmalig,
                      beginn, ende ('' = unbefristet), frist + fristEh, verlaengerung (Monate, 0 = endet), gekuendigtAm, kuendigungZum, abrechnen, letzteAbrechnung}],
                      einwilligungen [{kunde, kontakt, zweck EW_ZWECK, status erteilt|abgelehnt|widerrufen, form EW_FORM, am, widerrufAm, nachweis}], feedback [{am, wert 1–5,
                      anlass, kontakt, anliegen, kommentar}], leistungen [{kunde, vertrag, datum, text, menge, einheit, preis, satz, ueb {uid, am, st neu|uebernommen|verworfen,
                      status entwurf|gestellt|teilbezahlt|bezahlt|storniert|geloescht|verworfen, nr, datum, brutto, bezahlt, bezahltAm, versandt}}], erinnerungen [{kunde, nr,
                      weg, am, faellig (neue Frist), betrag, stufe}], vcUeb [{uid, kunde, kontakt, titel, notiz, am, wert, abschluss, st, chanceNr, phase, art}], vorlagen
                      [{name, kanal email|brief|telefon, zweck service|werbung, betreff, text}], zaehler. normalize idempotent (geprüft).
                      REGELN: vertragsLage (Laufzeitende bei Verlängerung fortgeschrieben, kündbar bis = Ende − Frist), vertragFristBald (Vorlauf), jahresWert; kennzahlen(k) aus
                      dem Kundenstand der Buchhaltung (bhKunde: k.bh.id → kdnr → Name ohne Rechtsform, je Buchhaltungskunde nur einmal; bhVerknuepfen setzt k.bh still);
                      ABC nach Umsatz 12 Monate (ohne Buchhaltung nach Vertragswert/Jahr), Grenzen abcA/abcB; zufriedenheit = Rückmeldungen + gelöste Anliegen (Schnitt der
                      letzten 3); stillSeit (aktiver Kunde ohne Kontakt/Rechnung/Anliegen/Rückmeldung seit stillTage); ampelKunde rot (gekündigt, offene Beschwerde/Reklamation,
                      Rechnung > 30 Tage überfällig, zuletzt ≤ 2) / gelb (Anliegen über Frist, überfällige Rechnung, 3 von 5, Frist naht, still). kontaktweg(p, email|telefon|brief)
                      für WERBUNG nach § 7 UWG (Einwilligung, Bestandskunden-Ausnahme als Prüfhinweis, B2B-Telefon mutmaßlich, Brief bis Widerspruch) – nur Hinweise, keine
                      Rechtsberatung; Vorlagen „Service“ prüfen nichts. Anliegen: Frist nach Priorität in Werktagen (fristVorschlag), Board mit Zeiger-Ziehen (.karte[data-anid]
                      → .spalte[data-st]), „Gelöst“ fragt Lösung/Zufriedenheit/Nachfass-Wiedervorlage (geloestDialog), Antwort-Vorlage übernimmt {loesung}.
                      BUCHHALTUNG (k5): „Mit der Buchhaltung verbinden“ = doc.einst.bh.verbunden + 'kst-wunsch' im Store + Nachricht 'kundenstand-anfrage' (wartet 0,9 s, sonst
                      Erklärdialog, der sich beim Eintreffen schließt); bhLaden liest 'kst:*' (mehrere Bestände wählbar). Leistungen → lsDlg/lsUebergeben = 'r:<uid>' format
                      'mc4y-rechnungsuebergabe' {quelle {werkzeug 'kunden', bestand, kunde, kundeName}, titel = Betreff, lvon/lbis, kunde = kundeAdresse (z. Hd. Rechnungskontakt,
                      bhId, kdnr), pos (g/gl leer), notiz}; lsStatusLaden liest 'r:'/'s:' und 'k:' (kuUebergeben = Kunde ohne Gegenstück → 'k:' format
                      'mc4y-kundenuebergabe', Kundennummer kommt zurück). lsFrei: nicht übergeben bzw. verworfen/gelöscht/storniert → wieder abzurechnen. lsZuruecknehmen
                      nur solange 'r:' status neu ('zurueckgenommen'). Vertragsabrechnung vtNaechsteAbrechnung/vtAbrechnen (Zeitraum ab letzteAbrechnung bzw. Beginn,
                      Leistung mit Zeitraum im Text), „alle fälligen anlegen“. Zahlungserinnerung erinnernDlg → nachrichtDialog mit {r, frist, stufe} (1. bzw. zweite
                      Vorlage), Vermerk = erinnerungen + Aktivität + Wiedervorlage „Zahlungseingang prüfen“. Kunden aus der Buchhaltung übernehmen (bhKundenDlg; „z. Hd. …“ →
                      Ansprechperson). Kundenstand auch per Datei (kstDatei/kstAblegen, dateiOeffnen erkennt 'mc4y-kundenstand'). Beispiel: bestand 'beispiel' = fiktive
                      Buchhaltung beispielKst() (bhLaden), nurBeispiel() sperrt Übergaben auch im geladenen Beispiel.
                      VERTRIEB (k6): kbLaden/kbBanner/kbNehmen ('kb:' format 'mc4y-kundenbetreuung'; Kunde per kundeFinden ergänzt oder neu, Kontakte per quelle.vid/
                      kontaktFinden, Gremium → Rolle, kw → Einwilligungen form 'vertrieb' (ja → erteilt, widerrufen, nein → abgelehnt; Telefon „mutmaßlich“ bewusst nicht),
                      Verlauf als Aktivitäten „(Vertrieb)“, Aktivität „Auftrag gewonnen“, Wiedervorlage in 14 Tagen; Stand zurück 'kb:' status uebernommen +
                      'kundenbetreuung-uebernommen'). vcDlg/vcUebergeben ('vc:'; Person mit Widerspruch geht nicht mit; kwVon), vcLaden ('vc:' verworfen, 'vcs:' Phase/
                      Ergebnis → Aktivität bei gewonnen/verloren), vcStText. anderenLaden (Start, Fensterfokus, BroadcastChannel kundenstand|status|kunde-*|
                      kundenbetreuung|chance-*) ruft bhLaden, lsStatusLaden, kbLaden, vcLaden – nicht im Beispiel.
                      STAMMDATEN (kb): MC4Y_SD_ADAPTER ohneRollen, Kunden = Organisationen (typ kunde; name, email, telefon, Anschrift, ustid, kdnr; Privatkunden ebenso),
                      Ansprechpersonen (nur aktive) = Personen (name, email, telefon, mobil, funktion = Position) mit firmaLid = Kunde, firmaAnlegen, wandeln land;
                      sdZusammen; gesperrte Felder readonly (sdFest); ausStammdaten. Datenschutz-Hinweis im Verknüpfen-Dialog.
                      Beispiel (#beispiel[&ansicht=…&tab=…&modus=…&sel=Name|Firmenanfang|AN-/VT-Nr.]): „Kunden der Muster Beratung (Beispiel)“, 11 Kunden (A/B/C,
                      Schlüsselkunden, ruhend, gekündigt, ehemalig, einer aus dem Vertrieb), 17 Ansprechpersonen (eine mit Widerspruch), 9 Anliegen, 7 Verträge (zwei mit
                      fälliger Abrechnung, Fristen in < 60 Tagen), Einwilligungen, Rückmeldungen, Leistungen (offen, gestellt, bezahlt), Erinnerung, Zusatzgeschäft beim Vertrieb;
                      fiktive Buchhaltung mit ~170 Rechnungen (zwei überfällig) und einem Buchhaltungskunden ohne Gegenstück. Alles erfunden, Adressen .example.
                      GEPRÜFT im Vorschau-Browser: alle Ansichten × Reiter × Modi, Panels aller Beispiel-Objekte, Auswertungen 1/2/3/5 Jahre, Filter, leeres Modell (229 + 22
                      Darstellungen) ohne Fehler/NaN, normalize idempotent, 375 px ohne Querscrollen; ECHTER ABLAUF in drei Tabs (gleiche Herkunft): Vertriebs-Navigator
                      übergibt VC-002 mit der Maus → Kunden-Navigator übernimmt per Klick (2 Personen, 2 Einwilligungen, Verlauf, Wiedervorlage) → Vertrieb zeigt
                      „übernommen“; Zusatzgeschäft per Dialog (Maus + Tastatur) → Vertrieb übernimmt (vorhandener Account/Lead, keine Doppel) → Phase „Angebot“ kommt zurück;
                      Buchhaltung (Browser-Ablage) verbunden → Kundenstand kommt; Leistung per Tastatur erfasst (Tab-Folge) und übergeben → Buchhaltung übernimmt mit der Maus
                      (Kunde neu, Kd.-Nr. kommt zurück), stellt die Rechnung → Nummer/Status/Umsatz/offen im Kunden-Navigator; Storno, Zurücknehmen, Verwerfen → Leistung wieder
                      frei; Kunde ohne Gegenstück übergeben → Kd.-Nr. zurück; Buchhaltungskunde mit „z. Hd.“ übernommen; Zahlungserinnerung im Beispiel mit der Maus (Vorlage,
                      Vermerk, Wiedervorlage); Anliegen-Karte mit der Maus gezogen, Gelöst-Dialog; Import CSV (Firma/Person/Privatkunde, zweiter Import ohne Doppel); Zuordnen
                      mit Neuanlage (Kunde und Person eingetippt, Tab-Folge); Verträge (Frist mit Verlängerung, Abrechnung, Kündigung, Rückgängig/Wiederholen); Stammdaten
                      (gestempelt): Erstverknüpfung über den Dialog (9 Einträge, Firma der Personen), Änderung in den Stammdaten kommt an und sperrt Felder, neue Person geht
                      „zu prüfen“ dorthin, kein Dauer-Abgleich. Frisch geöffnet legen Kunden- und Vertriebs-Navigator KEINEN Browser-Speicher an. Testdaten danach entfernt.
                      Druck-HTML (Auswertung, Auskunft Art. 15) und alle CSV-Ausgaben per Skript erzeugt (Druck/Download abgefangen) – ohne NaN/undefined.
                      NICHT geprüft: Speichern/Öffnen über den Dateidialog, Nutzung über file:// in Chrome/Edge, mailto/Kopieren (nicht ausgelöst), Druckdialog selbst.
                      DIENSTLEISTER, DOKUMENTE, E-MAIL (16.10.2026 abends, Build 17:51; Peters Fund „Mail-Anbindung tot, keine Dateien als Anlage bei
                      Verträgen“ + Wunsch „mehrere Dokumente je Vertrag, Verträge auch mit eigenen Dienstleistern – Kontakte, Rechnungen an/von und Verträge
                      für Kunden bzw. mit Dienstleistern unterscheiden, in die Buchhaltung überführen/verknüpfen; E-Mails übernehmen und mit Verträgen usw.
                      verknüpfen, Outlook/Thunderbird; Mails im Navigator so aufbereiten, dass die Mail-Programme sie zum Senden nutzen können“). URSACHE „tot“:
                      die Meldung erschien hinter dem modalen Dialog (toast hängt #toast jetzt in den obersten offenen dialog), und mailto kann keine Anhänge.
                      FORMAT version 2: Partner k.bez kunde|dienstleister|beides (BEZ; istKunde/istDL/nurDL/partnerWort), k.unsereNr (Kunden-/Vertragsnr. beim
                      Dienstleister), k.bhPartner (Name als Rechnungssteller in der Buchhaltung); Vertrag v.seite verkauf|einkauf (VT_SEITE; einkauf: kein
                      „abrechnen“, Kosten statt Wert), v.fremdNr; VT_ART erweitert (software, hosting, telekom, versicherung, dienstleistung, lieferung …).
                      doc.dokumente [{id, name, typ, groesse, art DOK_ART (vertrag, nachtrag, angebot, rechnung, kuendigung, agb, protokoll, email, anhang,
                      sonst), datum, titel, notiz, kunde, kontakt, vertrag, anliegen, mail, ort intern|ordner|beispiel, pfad, hash SHA-256, bh {uid, am,
                      st neu|uebernommen|verworfen, belegNr, status offen|bezahlt|storniert, renr, betrag, datum}}], doc.mails [{id, richtung ein|aus, von,
                      an, cc ({name, adr}), betreff, datum 'JJJJ-MM-TTTHH:MM', text, msgId, inReplyTo, dok (Originaldatei), anhaenge [Dokument-ids], kunde,
                      kontakt, vertrag, anliegen, quelle}], Aktivität art 'email' mit mail. einst.dokOrdner, einst.eigeneAdressen. NEUE QUELLEN kc_dokumente.js
                      und kd_mail.js (TEILE nach k6). IndexedDB 'mc4y-kunden' version 2: Store kv (datei, ordner = Handle Dokumentenordner) + Store dateien
                      (Blob je Dokument-id). Ablage: ort 'intern' = Store dateien, beim Speichern als anlagen {id: {typ, data Base64}} in *.kunden.json
                      (dateiInhalt; dateiOeffnen → anlagenAblegen); ort 'ordner' = File System Access, Pfad „Partner/[VT-Nr Titel/]Datum Name“ (ordnerPfad,
                      ordnerFrei), ordnerWaehlen/ordnerLoesen/dokUmziehen; Beispiel: ort 'beispiel' (miniPdf bzw. emlBauen erzeugt den Inhalt) und TEMP_BLOBS.
                      speicherAufraeumen behält nur intern-Dokumente + Rückgängig-Bezüge der Sitzung. Öffnen nur PDF/Bilder/Text im Browser (SICHER_ZEIGEN),
                      sonst Download. dokAbschnitt (Drop-Zone data-dropziel, „＋“) in Kunde/Dienstleister/Vertrag/Anliegen/E-Mail, panelDokument, Ansicht
                      „📨 E-Mails, Dokumente“ (VIEWS.post, ui.postTab mails|doks). E-MAIL LESEN: eigener MIME-Parser (RFC 5322/2045/2047/2231; multipart,
                      quoted-printable, base64, Zeichensätze über textAus, message/rfc822 als Anhang, inline nur Bilder mit Content-ID) emlLesen; eigener
                      CFB-Leser (MS-CFB, Mini-Stream, DIFAT) + msgLesen (MS-OXMSG: __substg1.0_* 001F/001E/0102, __properties_version1.0 Kopf 32/8,
                      Empfänger 0C15 Typ 1/2, Anhänge 3707/3704/3701/370E/3712, Absender 0C1A/5D01/0C1F, Zeit 0039/0E06, PR_TRANSPORT_MESSAGE_HEADERS);
                      Zwischenablage (kopiertLesen: Von/Gesendet/An/Betreff). mailImportDialog = Karten je E-Mail (#miListe, .mikarte) mit Vorschlag
                      (mailZuordnung: Adresse einer Ansprechperson, E-Mail bzw. Website-Domain des Partners ohne Freemail, VT-/AN-Nummern, fremdNr/unsereNr im
                      Betreff/Text; eigene Adressen = ausgehend), Doppel über msgId bzw. Kopf (mailDoppel), „als Person anlegen“; mailImportLos = EIN
                      Rückgängig-Schritt (E-Mails, Originaldatei, Anhänge als Dokumente artVon, Aktivitäten, neue Personen; Anliegen „wartet“ → „in
                      Bearbeitung“). E-MAIL SCHREIBEN: nachrichtDialog (Vorlagen, ANTWORT mit Zitat und In-Reply-To, Dokument „per E-Mail senden“) mit An/Cc,
                      Anhängen aus den Dokumenten des Bezugs + eigenen Dateien (nvAnhZeigen); „📧 E-Mail-Datei (Outlook, Thunderbird)“ = nvEml → emlBauen (.eml,
                      X-Unsent: 1, nur ASCII, CRLF, RFC 2047 Betreff, filename + filename*, base64-Anhänge, In-Reply-To/References); #nvMailto = echter
                      mailto-Link (CRLF-kodiert, ohne Anhänge); nvVermerk speichert die gesendete E-Mail (richtung aus, Anhänge) + Aktivität. Einstellungen
                      Reiter „Dokumente und E-Mail“ (einstPost: Ablage, eigene Adressen, Anleitung Outlook/Thunderbird). Thunderbird-Menünamen NICHT zitieren
                      (nicht geprüft) – beschreibend „als neue Nachricht bearbeiten“. BUCHHALTUNG: Dokument → „🧾 an die Buchhaltung“ (beDlg/beUebergeben):
                      'b:<uid>' format 'mc4y-beleguebergabe' {quelle {werkzeug 'kunden', partner, partnerName, vertrag, dok}, beleg {datum, partner, renr,
                      betrag, satz, text, bezahlt, bezahltAm}, datei {name, typ, blob | data Base64}} bzw. *.beleg.json; beStatusLaden (in anderenLaden) liest
                      'b:' und kundenstand.lieferanten[].belege[].quelleUid → x.bh.belegNr/status. Dienstleister-Kennzahlen aus kst.lieferanten (bhLieferant über
                      bhPartner bzw. Namen; Reiter „Rechnungen › Eingangsrechnungen“ eingangSeite, Akte eingangAbschnitt). bhName zeigt bei fehlendem
                      Firmennamen „angelegt am …“. Stammdaten: Organisation typ dienstleister bzw. kunde, Personen eines reinen Dienstleisters typ lieferant;
                      ausStammdaten legt in der Ansicht Dienstleister bzw. bei typ dienstleister/lieferant Dienstleister an. Beispiel: Dienstleister Kanzlei
                      Musterberater, Wolkenwerk Hosting, Druckhaus Beispiel; Hafenblick Hotel = beides; Verträge VT-008–010 einkauf; 14 Dokumente, 4 E-Mails;
                      beispielKst mit lieferanten. GEPRÜFT im Vorschau-Browser: 339 Darstellungen (Beispiel + leer, alle Ansichten/Reiter/Filter/Panels)
                      ohne Fehler/NaN, normalize idempotent, 375 px ohne Querscrollen (auch Übernahme-, Nachrichten-, Beleg-Dialog); Parser mit in Python
                      erzeugten Dateien (eml multipart/alternative + PDF-Anhang mit RFC-2231-Umlautnamen + Inline-Bild; eml nur HTML ISO-8859-1 QP mit
                      Q-kodiertem Betreff über 4 Zeilen; .msg aus eigenem CFB-Schreiber nach Spezifikation mit Unicode-Strömen, An/Cc, großem und Mini-Stream-
                      Anhang – NICHT mit einer echten Outlook-.msg, Outlook wurde bewusst nicht gesteuert); Übernahme per Maus (Domain-, Personen- und
                      Vertragsnummer-Erkennung, neue Person, Doppel, Rückgängig); erzeugte .eml mit Python email geprüft (ASCII, CRLF, Kopf, 2 Anhänge in
                      Originalgröße, PDF mit PyMuPDF geöffnet); Dokumente im echten Bestand (IndexedDB, Datei-Rundlauf mit Prüfsumme, Dokumentenordner mit OPFS
                      als Ersatz: Verschieben, neue Datei, Fehlerfall, Zurückholen); Beleg-Übergabe echt in zwei Tabs (siehe Buchhaltung). Testdaten entfernt.
                      NICHT geprüft: echte .msg aus Outlook, Öffnen der .eml in Outlook/Thunderbird, mailto (nicht ausgelöst), echter Ordner über
                      showDirectoryPicker unter file:// in Chrome/Edge.
8em-modeller.html     „8EM-Modeller“ (bis 12.10.2026 nachts „8EM-Editor“, Datei 8em-editor.html – auf Peters Wunsch konsistent umbenannt: Datei, Titel,
                      Texte, editoren-version.py NAMES, editoren-versionen.json, Übergabeknopf der fünf Fragebögen; Peters eigene Inhalte – Datenschutz-
                      erklärung generator/inhalte/datenschutz.html und Blog-Entwurf 2026-10-03-tool-8em-editor.md – nennen noch „8EM-Editor“, NICHT geändert).
                      Eigenständiges Coaching-/Beratungswerkzeug (NICHT Teil der Website, nie hochladen):
                      Unternehmen im 8-Ebenen-Modell abbilden (format 'mc4y-8em', version 19 seit 20.10.2026 – Interfaces, Lebenszyklus mit Daten, Eingabemaske, CMDB,
                      siehe „FASSUNG 19“ unten; version 18 seit 15.10.2026 abends – Projektstand aus dem Projektmanagement
                      e.pm, siehe „PROJEKTMANAGEMENT-ANSCHLUSS“ unten; version 17 seit 13.10.2026 abends – EAM-Werkzeuge, Ebenen 5b Wertströme und
                      7d Glossar, siehe „EAM-WERKZEUGE“ unten; version 16 = Kosten mit Kostenart und Kostenstelle, Unterebene
                      7a „Kostenstellen“; version 15 = Rollen/Personen nur zentral, ITIL/COBIT/TOGAF;
                      version 14 = Datenschutz/ISMS und UML; version 13 = Ist-/Soll-Bilder; normalize() übernimmt Version 1–15, migrateFolders(ver) zieht Ablageordner um – Fassung 2 per Tabelle OLD_DIRS, Fassung 3–9 → 10 per OLD10 (moveFolders),
                      Fassung 3: Dateien aus „07a Organisation“ nach „07a Organisation/01 Organisation“).
                      Ebenen (Peters Vorgabe): 1–6, 5a Produkte (seit 12.10.2026, unter den Fähigkeiten, Ordner „05a Produkte“),
                      6a Funktionen (Ordner „06a Funktionen“; ePK-Funktionen und
                      BPMN-Aufgaben werden dort als wiederverwendbare Elemente eingetragen), 7a Organisation mit
                      Unterbereichen Organisation (Organigramme), Rollen, Mitarbeiter, OE-Leiter (ORG_LAYERS;
                      orgart Rolle/Person wird beim Laden zu Rollen; Mitarbeiter/OE-Leiter werden per
                      Häkchenliste assignHtml() = normale Verknüpfung den Rollen zugeordnet, OE-Leiter zusätzlich
                      der geleiteten Organisationseinheit; ePK-Symbol „Person“), 7b Projekte,
                      7c Daten und Informationen (Geschäfts-/Informations-/Datenobjekte), 8 Werkzeuge & IT
                      (Gruppe „Werkzeuge und Methoden“: Nicht-IT-Tools, Methoden; Trennstrich „IT-Unterstützung“: seit Fassung 10
                      IT-Systeme, Technischer Architekturplan, Plattformen, IT-Komponenten – jeweils als ICT-Objekte mit Releases
                      und Versionen – und seit Fassung 11 IT-Komponentenkataloge (Ordner 07); Client/Business/Data Tier, Server, Netzwerk entfallen,
                      s. „EBENE 8 NEU“ und „ICT-OBJEKTE“ unten),
                      9 Standort (seit 29.09.2026 auf gleicher Stufe wie 8; Ordner „09 Standort“; Felder Art,
                      Anschrift, Ansprechperson, Anzahl Mitarbeitende – rein additiv, keine Migration nötig).
                      Modellordner je Modell (showDirectoryPicker, Handle „lastDir“ in IndexedDB „mc4y-8em“):
                      modell.8em.json, je Ebene/Unterebene ein Ablageordner („01 Vision“ … „07a Organisation“ …
                      „08 Werkzeuge und IT-Systeme/06 IT-Komponenten“, Namen aus LEVELS/LAYERS – NICHT umbenennen,
                      Dokumentpfade e.docs hängen daran; bei Umbenennung OLD_DIRS erweitern), _Sicherungen
                      (30 Fassungen), _Papierkorb (nie endgültig löschen). Startdialog: neu/öffnen.
                      Vision/Mission als PDF/Word oder im Text-Editor (s. u.). Fähigkeiten bis 5 Stufen verschachtelt (e.parent), frei
                      definierbare Farbcodierung (doc.colors, Legende wahlweise). Ansicht „Ebenen“: Kärtchen frei
                      verschiebbar und in der Größe ziehbar (e.lay {x,y,w,h} relativ zum Behälter = Band/
                      Unterebene/übergeordnete Fähigkeit; ohne lay automatische Anordnung per firstFit, Höhen
                      geschätzt und nachgemessen; beim ersten Ziehen werden Geschwister eingefroren). Weitere
                      Ansichten: Fähigkeitenlandkarte (automatisch angeordnet), Matrix, Leitfaden, Ablage, Prüfung.
                      Prozessmodelle je Prozess (e.models = {epk, bpmn, seeme}, PM_KINDS, im modell.8em.json;
                      normModels()). SeeMe (Th. Herrmann, Ruhr-Universität Bochum): Rolle (Ellipse → 7a,
                      Standard Rollen), Aktivität (abgerundet → 6a Funktionen), Entität (Rechteck → 7c bzw.
                      Werkzeuge/IT), Konnektoren UND/ODER/XOR, gerade gerichtete Relationen (D.straight),
                      Einbettung = geometrisches Enthaltensein (pmInside/pmKids, D.nest; Kinder wandern mit),
                      Unvollständigkeit n.inc = „…“. SeeMe-Farben frei gewählt, nur die Formen folgen der Methode.
                      Weitere SeeMe-Bestandteile (Modifikatoren, vage Relationen) bewusst noch nicht umgesetzt.
                      Vollbild-Editor #pm (pm*-Funktionen, Notationen in PM_T: Symbole, Paletten, „Weiter
                      modellieren“-Regeln). ePK nach ARIS (Ereignis, Funktion, XOR/UND/ODER, Prozesswegweiser,
                      Organisationseinheit, Informationsobjekt, Anwendungssystem, Werkzeug/Methode), BPMN 2.x mit
                      Pool/Lanes (Start/Zwischen/Ende, Aufgabe mit Typ + IT-System, Teilprozess, Gateways X/+/O,
                      Datenobjekt, Datenspeicher, Anmerkung). Objekte mit n.ref verweisen auf 8EM-Elemente der
                      unteren Ebenen (Layer-Zuordnung in PM_T.types[].layers, Standard-Ebene deflayer). Jedes neu
                      gesetzte Symbol dieser Art legt SOFORT ein Element in deflayer an (pmNewNode, n.auto) und
                      verknüpft es mit dem Prozess (pmLinkProc); Beschriften mit dem Namen eines vorhandenen
                      Elements verknüpft stattdessen dieses (pmRenameNode), Löschen entfernt ungenutzte
                      Auto-Elemente (pmRemoveIfUnused); „Objekte ins 8EM-Modell übernehmen“ (pmAdoptAll) für
                      Altbestand; Ebene umstellbar (#pmMoveLayer). ePK zusätzlich Rolle/Stelle, BPMN zusätzlich
                      IT-System (eigenes Artefakt, Zuordnung zu Aufgaben). Löschen eines
                      Elements → pmDetach() (Name bleibt als Beschriftung). Modellprüfung pmChecks(), Export
                      SVG/PNG, „In Ablage speichern“ → 06 Prozesse. Symbole wachsen mit dem Text (pmFit).
                      Export bisher Drucken/PDF und CSV. In Arbeit – wird schrittweise mit Peter spezifiziert
                      (Stand 28.09.2026: vierter Entwurf, Prozessmodelle). Vorschau-Browser in Claude kann keine Ordner – Ablage
                      dort nur mit simuliertem Ordner getestet.
                      EDITOREN BEIM ANLEGEN (11.10.2026, Peters Auftrag „Word-ähnlicher Editor mit Grundstruktur für Vision, Mission,
                      strategische und operative Ziele; geeignete Editoren für Fähigkeiten, Prozesse, Funktionen, Organisation“;
                      Sicherung editor-backups/*_8em-editoren): act('add:…') ruft editorOnCreate() (abschaltbar: ui.autoEd, Häkchen in
                      den Editor-Hinweisen und im Dialog „Neuer Prozess“; quickAdd im Leitfaden öffnet bewusst nichts).
                      TEXT-EDITOR #tx (Ebenen 1–4, tx*-Funktionen): contenteditable #txEd mit Vorlage TX[level] = {sections, extras
                      (Bausteine), checks (Qualitätscheck; operativ = SMART)}, Abschnitte als <h2 data-s="id">, Platzhalter im ersten
                      Absatz darunter (p[data-ph] + Klasse leer, txMark). Leitfragen rechts je Abschnitt des Cursors (txSecUpdate über
                      selectionchange), Gliederung + Bausteine links (txAddSec fügt Vorlagen-Abschnitte in Vorlagen-Reihenfolge ein),
                      Menüband mit execCommand (Absatzformat, F/K/U/D, Farbe, Textmarker, Listen, Einzug nur in Listen, Ausrichtung,
                      Tabellen inkl. Zeile/Spalte einfügen/löschen, Tab = nächste Zelle). Speichern: e.text (HTML, txHtml() bereinigt per
                      DOMParser – Chrome legt Listen in Absätze), e.textAt, e.textDoc, e.tcheck; ein Rückgängig-Schritt je Bearbeitung
                      (tx.mark). Einfügen nur bereinigt (txClean: keine Bilder/Links/Skripte, wenige Stile). Ausgabe txExpDialog:
                      Titelblock, Eckdaten-Tabelle (txMetaTable), leere Abschnitte weglassen, Leitfragen als Arbeitsblatt (div.lf + Schreib-
                      zeilen); Word (.docx) selbst erzeugt (makeZip/w*-Funktionen wie Mindmap-Editor; Heading1–3, Title/Subtitle, Quote,
                      Listen, Tabellen mit data-w), Drucken/PDF per iframe, „Als Word in die Ablage“ = Ordner der Ebene, Datei
                      „<Ebene> – <Name>.docx“, alte Fassung → _Papierkorb, Pfad in e.docs. In echtem Word geöffnet (Formatvorlagen,
                      Listen, Tabellen korrekt); PDF-Export über Word-COM hängt in der Claude-Umgebung (unsichtbarer Dialog) – nicht nötig.
                      MODELL-EDITOREN #me (me*-Funktionen, ME = faehigkeit|prozess|funktion|organisation): Gliederung links (meRows,
                      HTML5-DnD davor/dahinter/hinein, Filter), Zeichenfläche als SVG (automatisch angeordnet: nestDraw = Landkarte,
                      treeDraw = Baum von oben mit gestapelten Blättern ab stackFrom und seitlichen Stabsstellen, orgDraw = Organigramm mit
                      Leitung/Stellen/Personen je Detailstufe ui.orgDetail, plkDraw = Prozesslandkarte mit Bändern Führung/Kern/Unterstützung,
                      Kernprozesse als Chevron-Kette ihrer Teilprozesse zwischen doc.plk.links/rechts), Eigenschaften rechts. Hierarchie
                      über e.parent jetzt auch für prozess (4 Stufen), funktion (6) und organisation.einheiten (8): H_MAX, hKids/hDesc/
                      hOrder/hHeight (capKids/capDesc/capOrder bleiben als Hüllen), layerEls liefert Baumreihenfolge, indentName rückt ein.
                      Löschen: Fähigkeiten weiter kaskadierend, sonst rücken Kinder eine Stufe nach oben. Organisation: Stelle/Rolle gehört
                      per Verknüpfung zur Einheit, Personen per Verknüpfung zur Rolle, OE-Leiter per Verknüpfung zur Einheit (= Leitung);
                      Ziehen verschiebt Verknüpfungen (Strg = zusätzlich). Tastatur wie Mindmap (Enter/Tab/Umschalt+Tab/F2/Entf/Alt+↑↓/
                      Pfeile), Umbenennen im Bild (#meEdit fixiert über dem Kästchen). Export SVG/PNG/„In Ablage speichern“ (Ordner der
                      Ebene, Organigramme in 07a/01 Organisation). Prüfhinweise kindChecks() (auch in „Prüfung“): Fähigkeit ohne Beschreibung,
                      nur eine Unterfähigkeit, > 4 Stufen, klingt nach Tätigkeit (verbish: letztes Wort klein + Infinitiv), heißt wie OE/
                      IT-System; Funktion ohne Verb, nur eine Teilfunktion, Art passt nicht; Prozess ohne Prozessart, kein Kernprozess; OE ohne
                      Leitung, Rolle ohne Einheit. Neue Prozessfelder Auslöser/Ergebnis. Neuer Prozess → Dialog #npDlg (Name, Prozessart,
                      ePK/BPMN/SeeMe/Prozesslandkarte). #pm liegt mit z-index 45 über #me (Rückkehr in den Editor nach pmClose).
                      Beispiel erweitert (Organigramm mit Stabsstelle, Kernprozess mit Teilprozessen, Funktionsbaum, Vision-Text).
                      FEHLER BEHOBEN (11.10.2026): normalize() machte beim Öffnen Mitarbeiter und OE-Leiter zu Organisationseinheiten
                      (Migration prüfte nur einheiten/rollen) – Modelle, die seit Einführung dieser Unterbereiche geöffnet und gespeichert
                      wurden, können solche umgewandelten Einträge enthalten (Peter informiert).
                      Geprüft im Browser: alle Ansichten aller vier Editoren, Anlegen per Tab/Enter, Umbenennen, Ziehen im Bild und in der
                      Gliederung, Organigramm-Zuordnungen, Prozessart per Ziehen, Rückgängig, Text-Editor (Vorlagen, Listen, Tabelle,
                      Bausteine, Einfügen aus Word), Ablage mit OPFS-Ersatzordner, JSON-Rundlauf ohne Abweichung.
                      MODELL-EDITOREN, ZWEITE RUNDE (11.10.2026 abends, Peters Kritik „keine Beziehungen, keine Heatmaps, keine
                      Organigramme … strenge dich an“; Sicherung editor-backups/*_8em-editoren-2). Datenformat VERSION 6:
                      doc.rels [{id, k, a, b, t, label, note}] = Beziehungen innerhalb eines Editors (k = faehigkeit|prozess|funktion|
                      organisation; Arten REL_T oben bei H_MAX, {id, l, inv, dir, c, dash}; normalize verwirft Beziehungen, deren Enden
                      nicht zum Editor passen; relsClean() nach Ebenenwechsel in #pmMoveLayer; Löschen/pmRemoveIfUnused räumen auf);
                      link.raci R|A|C|I auf normalen Verknüpfungen Prozess/Funktion ↔ Organisation (findLink, raciSet, raciState/
                      raciProblem: genau ein A, mind. ein R). Neue Felder: Fähigkeit bedeutung (differenzierend/wichtig/Standard),
                      itbedarf, kpi; Prozess kunde, reife (scale), probleme, modellprio; Rolle sollstellen; Mitarbeiter/OE-Leiter umfang
                      (umfangOf: %, 0,5, Std./40). Leiste #meBar unter der Kopfzeile (meRenderBar): Ansicht, „Farbe“ = Heatmap HEAT[kind]
                      (hCat/hNum, heatInfo mit Ø-Mittelwert der Unterelemente bei agg, meStyle; ui.heat[kind]), „Zuordnungen“ = Kärtchen
                      CHIPS[kind] (chipLayout/chipsSvg, bei org mit RACI-Präfix; ui.chips[kind]), Beziehungen alle/zur Auswahl/aus
                      (ui.relShow), „Art beschriften“ (ui.relLabels), P O W Z, Portfolio-Achsen, RACI-Spalten, Organigramm-Detail und
                      „Stellen als eigene Kästen“ (ui.orgRoleBoxes), „Nur diesen Zweig“ (me.focus, rootsOf). Ansichten: Fähigkeiten
                      Landkarte/Baum/PORTFOLIO (portfolioDraw: PF_X luecke|reifeIst|reifeSoll × PF_Y bedeutung|prio, Felder an Zellgrenzen,
                      neutrale Feldtexte PF_LBL, Einträge ziehen = Werte setzen über PF_X/PF_Y.set, nicht eingeschätzte rechts);
                      Prozesse Landkarte/Baum/RACI; Funktionen Baum/waagerecht (htreeDraw)/Landkarte/RACI (raciDraw: Zeilen in Baum-
                      reihenfolge, Spaltenbreite je Spalte nach längstem Wort, Klick zyklisch, Umschalt/Rechtsklick rückwärts, Summen,
                      Prüfspalte); Organigramm senkrecht/waagerecht/STELLENPLAN (stellenDraw). Organigramm neu: farbiger Kopf (Bereichsfarbe
                      branchColor bzw. Heatmap), Leitung mit Namenskürzel-Kreis (avaSvg), Kennzahl „n Stellen · n Personen · x VZÄ“,
                      Stellen mit besetzt/Soll bzw. „teilweise offen“/„unbesetzt“ (roleStatus, roleBadge, badgeW), optional Stellen als
                      eigene Kästen unter der Einheit (orgRoleBoxes, ROLE_W). Kästchen: Reifegrad-Balken matSvg (gold = Soll), Kennzeile
                      (◆ differenzierend, Funktionsart), Warnzeichen bei Schmerzpunkten, Zu-/Aufklappknopf togSvg (data-h="tog:id").
                      BEZIEHUNGEN: Griff ↗ (data-h="rel") am ausgewählten Kästchen zum Ziel ziehen (drag.mode 'rel', relAdd mit zuletzt
                      genutzter Art ui.relLast), relsSvg = quadratische Bögen mit Halo, Pfeilspitzen als Polygon (keine marker → Exporte),
                      kurze Wege stärker gebogen, Beschriftung per relLabelAt (Kandidaten entlang der Kurve, bewertet nach überdeckter
                      Fläche von Kästchen/Pfeilspitzen/anderen Beschriftungen). Hover über ein Kästchen hebt seine Beziehungen hervor
                      (svg.hovering, g.rel.hot). Legende legendSvg: Heatmap, Beziehungsarten (Klick blendet Art aus, ui.relHide), Hinweise.
                      EXPORT-MENÜ „Export ▾“: PPTX/ODP/VSDX/PDF über svgToPrims() (wandelt das Export-SVG in Grundformen: rect/ellipse/
                      poly/text inkl. translate/rotate, Pfade M/L/H/V/Q/C/A/Z, Deckkraft mit Weiß gemischt, Text-Anker end) + XP-Modul
                      (bounds/pptx/odp/vsdx/pdf, aus inneres-team-editor übernommen) sowie SVG/PNG. Prüfhinweise erweitert (IT-Lücke,
                      mehrere IT-Systeme, Bedeutung offen, RACI uneindeutig, hohe Schmerzpunkte, unbesetzte Stellen, Leitungsspanne ≥ 10).
                      Übersicht: Beziehungsliste relListHtml im rechten Bereich, CSV-Spalte „Beziehungen“ und RACI in „Verknüpft mit“.
                      Beispiel ergänzt (Bedeutung, IT-Bedarf mit absichtlichen Lücken, Prozess-Reife/Schmerzpunkte, 22 RACI-Angaben,
                      13 Beziehungen, Soll-Stellen, unbesetzte Datenschutz-Stelle). Geprüft: alle Kombinationen Editor × Ansicht × Heatmap ×
                      Kärtchen ohne Fehler/NaN (auch leeres Modell), Beziehung per Maus gezogen und bearbeitet, RACI-Klicks, Portfolio-Ziehen
                      (Fehler „Lücke an der Skalengrenze“ behoben), Tastatur, Fokus, Hover, JSON-Rundlauf, Version-5-Datei, Löschen + Rückgängig;
                      PPTX/ODP in PowerPoint geöffnet (90–230 Formen), VSDX-XML wohlgeformt, PDF mit PyMuPDF gerendert.
                      MODELLIERUNGSFLÄCHE, DRITTE RUNDE (11.10.2026 spätabends, Peters Kritik „wirkt nicht wie ein Modellierungswerkzeug:
                      nichts in den Objekten machbar, nicht verschieben/vergrößern/frei platzieren, kaum Symbole, alles über die linke Leiste;
                      am 3D-Systembrett orientieren, bei artverwandten Werkzeugen forschen“; Recherche Archi, bpmn-js/Camunda, Signavio, draw.io,
                      Lucid, Miro, LeanIX/Ardoq, ARIS, yEd, Organigramm-Werkzeuge; Sicherung editor-backups/*_8em-modellierung). VERSION 7:
                      doc.diagrams[kind] = {style nest|tree, nest:{id:{x,y,w,h}}, tree:{id:{x,y,w,h}}, items:{nest:[], tree:[]}} (dia(), posMap(),
                      itemsOf(); normalize säubert, unbekannte ids fallen weg). nest = „Kästen ineinander“: Kinder liegen im Inhaltsbereich des
                      übergeordneten Objekts, Lage relativ (PADX/PADT/PADB, Kopfhöhe hb), Behälter wachsen mit (Größe = max(gezogen, Inhalt));
                      tree = „Baum mit Linien“: Lage absolut, Linien treeLinesSvg (Bus, gestapelte Blätter mit Stamm links, Stabsstellen seitlich).
                      Jede Darstellung merkt sich ihre Anordnung; fehlende Lagen placeMissingKids/Roots/Tree/Cards, ganz ohne Lage meAutoLayout
                      (autoNest/shelfRoots/autoBands/autoTree, auch je Zweig). Prozesse (nest): Bänder = items t:'band' {art} (Prozess ins Band
                      gezogen = Prozessart), Akteure t:'actor' (aus doc.plk), Teilprozesse als Chevron-Kette. Gestaltung items note|frame|text|
                      actor|band (Rahmen/Bänder nehmen beim Verschieben mit, was ganz darin liegt). Konstanten MGAP/MGRID (GAP/GRID gab es schon).
                      Fläche: SVG mit Welt-Gruppe #meWorld (translate·scale, me.k/vx/vy, meViews je Editor), #meContent (Inhalt) + Überlagerung
                      (#meSelO Auswahl/Griffe rs:*, gelbe ＋ sib/child, ↗ rel, Bogengriff bend; #meGd Hilfslinien, #meHi Ziel grün/rot, #meTmp,
                      #meRb Auswahlrahmen). Mausrad = Zoom, Leertaste/mittlere Taste/Ziehen auf leerer Fläche = verschieben (ui.meSelMode bzw.
                      Umschalt = Auswahlrahmen, auch im Innenraum eines Bereichs), Doppelklick leer/Bereichsinnenraum = neues Objekt.
                      Verschieben meMoveBegin/Drag/End: nest per transform auf den Gruppen, tree durch Neuzeichnen (Zweig wandert mit, Alt = nur
                      Objekt), meSnap (Hilfslinien an Kanten/Mitten, sonst Raster 10), meDropAt (Verschachteln, H_MAX-Prüfung; lose Org-Karten →
                      meDrop-Zuordnung), meReparent + meForgetPos (andere Darstellung platziert neu), meUnoverlap (abgelegtes Kind weicht nach rechts
                      aus), im Baum rastet ein neu Untergeordnetes unter dem Vorgesetzten ein. Größe meResize* (Behälter nie kleiner als Inhalt;
                      große Kästen zeigen die Beschreibung). Klick-Bedienung im Objekt data-w mat:/mch:/kick: wirkt erst beim Loslassen ohne
                      Bewegung (sonst verschob ein Griff an die Kennzeile nicht, sondern wechselte die Art). Palette #mePal (palTools/palIcon/
                      palGroups: Elemente je Editor, Gestaltung, Beziehungen = Verbindungswerkzeug me.connectType, „Zuordnen: …“ = Elemente anderer
                      Ebenen, ziehen auf Objekt bzw. Klick bei Auswahl = addLink/lösen), Ziehen mit Geisterbild #mePalGhost, Klick = me.place
                      (Umschalt = bleibt aktiv). Organisation: lose Karten orgLoose (Stelle ohne Einheit, Person ohne Stelle/Einheit), orgPlace
                      (Stelle auf Einheit, Person auf Stelle/Einheit), Art „Gremium“ neu (orgart). Kontextleiste #meCtxBar (meCtxRender/Pos;
                      Art-Aufklappfeld artPopHtml), Rechtsklick-Menü #meMenu (meMenuShow), Ausrichten/Verteilen/gleich groß (meAlign/meDist/
                      meSameSize), Pfeiltasten meNudge, Strg+A/D, Entf mehrerer (meDelete), Übersichtskarte #meMini, Legende #meLegend (aus
                      legendSvg), schwebende Leisten #meTopL (Ansicht, Darstellung, Anordnen, Stufen = ui.meDepth, Verschieben/Auswahlrahmen),
                      #meBotL (Farbe, „⚙ Anzeige“ mit Zuordnungen/Beziehungen/Raster/Übersicht …), #meTopR (Zoom, unten rechts). Linke Leiste
                      mit Reitern Symbole/Gliederung (ui.meLeftTab), Trennleisten #meSplitL/R und Laschen wie im 3D-Systembrett (ui.meLay).
                      Eigenschaften: mehrere (meMultiPropsHtml), Gestaltungselement (meItemPropsHtml, data-itf), Darstellung (meLayoutHtml, data-ps).
                      Beziehungen mit Bogen r.bend {x,y} (relGeo, normalize). Export meSvg(true) aus derselben Fläche (Grenzen aus Objekten).
                      Geprüft: 240 Kombinationen Editor × Darstellung × Farbe × Kärtchen × Ansicht ohne Fehler/NaN (auch leeres Modell), mit der
                      Maus Verschachteln (nest und tree), Palette in Bereich ziehen + Namen tippen, Größe, Auswahlrahmen, Ausrichten, Pfeiltasten,
                      Löschen + Rückgängig, Beziehungswerkzeug, Notiz, Rahmen mit Inhalt, Rechtsklick-Menü, lose Stelle zuordnen, Portfolio/RACI,
                      Rundlauf normalize idempotent, Fassung-6-Datei ohne Flächen ordnet automatisch an.
                      ÖFFNEN, UMBENENNEN, BEZIEHUNGEN ZEICHNEN (12.10.2026, Peters Wunsch „Beschriftungen direkt bearbeitbar, Doppelklick öffnet den
                      passenden Editor, Beziehungen per Mausklick herstellen, ein-/ausblenden, grafisch ausrichten, Pfeilrichtung“; Sicherung
                      editor-backups/*_8em-beziehungen). VERSION 8: doc.links und doc.rels haben optional arrow none|fwd|back|both (fehlt =
                      Verknüpfung ohne Pfeil, Beziehung nach REL_T.dir), route auto|straight|ortho (fehlt = Einstellung der Ansicht: ui.lineForm in
                      der Übersicht, ui.meRoute[kind] im Editor), via [{t, v}] (Ziehpunkte relativ zur Strecke Mitte A → Mitte B: t Anteil, v seitlich
                      in px – Form bleibt erhalten, wenn Kästchen wandern), Verknüpfungen zusätzlich label (lnNormFields; normalize entfernt doppelte
                      Verknüpfungen, weil eine Linie über 'L:<a>|<b>' angesprochen wird). Gemeinsamer Baustein „Linienführung“ hinter clipBox: lnGeo
                      (auto = o.auto-Standardform, straight = Polylinie, auto mit Ziehpunkten = Catmull-Rom lnSpline, ortho = lnSide/lnLeg rechtwinklig
                      durch die Ziehpunkte), Abtastpunkte S + seg je Abschnitt (Treffer, Einfügestelle lnNearest, Pfeile lnArrowsSvg/lnTip, Beschriftung
                      at(t)), lnSnapPt (Ziehpunkt rastet waagerecht/senkrecht zu Nachbarn ein, Alt = frei), lnBarAt (Leiste neben, nie auf der Linie).
                      ÜBERSICHT „Ebenen“: drawLines zeichnet Verknüpfungen UND Beziehungen (Häkchen „zwischen Ebenen“ ui.lvLinks / „innerhalb einer Ebene“
                      ui.lvRels, „Linien: alle/zur Auswahl/aus“, Taste B, „Form“ ui.lineForm), Standardform lvAuto (S-Bogen); gewählte Linie lvLine
                      ('L:a|b' bzw. 'R:id') liegt samt Griffen auf eigener oberer Ebene svg#lvTop (über Ebenenköpfen), Leiste #lvBar (Art bei Beziehung,
                      Pfeil, Form, Enden tauschen lnAct rev mit via-Spiegelung, Form zurücksetzen, Beschriftung, Löschen), Eigenschaften linePropsHtml
                      (data-ln). Gewählte Linie ziehen = Ziehpunkt einfügen, ◆ ziehen/Doppelklick = verschieben/entfernen, ＋ je Abschnitt, Doppelklick
                      auf Linie = Beschriftung (#lvEdit). Verbinden: Knopf „↗ Verbinden“ (lvConn, Taste V), Griff .cn am gewählten Kärtchen (Klick =
                      Ausgang, Ziehen = direkt verbinden, Gummiband #lvTmp, Ziel grün/rot tgt-ok/no, Mitscrollen am Rand lvScroll), Rechtsklick „Beziehung
                      von hier …“; lvCanConnect: verschiedene (Unter-)Ebenen = Verknüpfung, gleiche Ebene mit REL_T = Beziehung (relAddK, letzte Art),
                      sonst Hinweis; Ausgang bleibt für weitere Ziele (erneuter Klick auf verknüpftes Ziel löst wie bisher). Beim Verbinden keine
                      Ketten-Abblendung; renderEbenen hält #lv beim Ein-/Ausblenden der Hinweiszeile an seiner Bildschirmstelle. Kärtchen/Kacheln
                      (auch Fähigkeitenlandkarte): Doppelklick (Klickpaar im document-click, weil renderView das DOM ersetzt) → openEditorFor:
                      editorTargets = Text-Editor (TX) | grafischer Editor meShowEl (Organisation hebt ggf. ui.orgDetail an) | Ablaufmodelle des Prozesses |
                      pmUses (Knoten n.ref/n.sys oder Lane.ref in Ablaufmodellen) → pmOpenAt (Knoten bzw. Lane ausgewählt); erstes Nicht-Ablauf-Ziel
                      direkt, sonst ein Ablauf direkt bzw. Auswahlmenü #lvMenu, ohne Ziel = Umbenennen. Zweiter langsamer Klick auf den Namen bzw. F2 =
                      lvEditStart (Textfeld über .nm). Rechtsklick-Menü lvMenuShow mit allen Öffnen-Zielen. Ein Klick auf ein gewähltes Kärtchen hebt die
                      Auswahl nicht mehr auf (Klick auf leere Fläche schon).
                      MODELL-EDITOREN: relGeo → lnGeo, Standard weiter relGeoQ (quadratischer Bogen inkl. altem r.bend); Bogengriff ersetzt durch Ziehpunkte
                      (data-h via:i / vins:j, drag 'via'); Linie anklicken = wählen, gewählte ziehen = Ziehpunkt, Doppelklick = Beschriftung meEditRel
                      (#meEdit dataset.rel). Kontextleiste/Rechtsklick/Eigenschaften: Pfeil (me:relArrow), Form (me:relRoute), Enden tauschen (relRev
                      spiegelt via/arrow), Form zurücksetzen (bend0 löscht bend/via/route), Beschriftung. Klick-Klick: Griff ↗/„↗ Beziehung“/Palettenart
                      anklicken ohne Ziehen → me.connFrom (+connFromType), Gummiband per pointermove, Klick aufs Ziel legt an, leere Fläche/Esc bricht ab;
                      Knopf „↗ Verbinden“ (me:connTool, Taste V, nimmt gewähltes Objekt als Ausgang). „⤳ Beziehungen“ unten (me:relTog, Taste B),
                      Linienform unter „⚙ Anzeige“ (#meRoute). relAddK schaltet ausgeblendete Beziehungen beim Anlegen wieder ein. Doppelklick auf Objekt
                      = meOpenDeep: Prozess → vorhandenes Ablaufmodell (ui.npLast bevorzugt) bzw. Menü „anlegen“, Funktion → Ablauf, in dem sie vorkommt
                      (mehrere = Menü), sonst Umbenennen; Doppelklick in Bereichsinnenraum legt weiter an. Zweiter langsamer Klick auf den Namen (data-nm
                      an den Namenstexten, nicht im Export) = Umbenennen (me.renameT, abgebrochen durch Doppelklick). Funktion im Rechtsklick-Menü: „Im
                      Ablauf … zeigen“ (me:pmat). pmOpenAt lässt den Modell-Editor offen (Rückkehr nach pmClose).
                      Geprüft: 180 Darstellungen (Übersicht Form × Linien × Häkchen × Auswahl, Editoren × Ansicht × Form × Anzeige) ohne Fehler/NaN,
                      Exporte PPTX/ODP/VSDX/PDF aller vier Editoren mit Pfeilen/Ziehpunkten/rechtwinklig, normalize idempotent (Felder bleiben), mit der
                      Maus: Linie wählen, Pfeil, rechtwinklig, Ziehpunkt einfügen/entfernen, Doppelklick Vision/Fähigkeit/Prozess/Funktion, Verbinden
                      per ↗ (Klick-Klick und Ziehen mit Mitscrollen), Beziehung im Editor per Klick-Klick, Beschriftung per Doppelklick, B/V/F2/Esc,
                      Umbenennen per zweitem Klick und Rechtsklick-Menü. In schmalen Fenstern liegen Ebenenköpfe über nicht gewählten Linien (wie bisher).
                      NACHTRAG 12.10.2026 (Peters Wunsch „zwei markieren und per Klick verbinden, Beziehungen per Maus lösen, löschen, umhängen,
                      als Ganzes aus- und einblenden“): Übersicht – Strg/Umschalt+Klick markiert Kärtchen (lvMarks, erstes = Ausgang, Klassen mk/mk1;
                      lvMark übernimmt ein gewähltes Kärtchen als erstes), Leiste lvMarksBar (#lvBar) bzw. Eigenschaften marksPropsHtml mit „↗ verbinden“,
                      „⇄ umgekehrt“, „✕“; lvConnectMarks verbindet das erste mit allen anderen in EINEM Rückgängig-Schritt (relAddK hat dafür noUndo);
                      „↗ Verbinden“/V mit ≥ 2 markierten verbindet direkt. Enden der gewählten Linie als Griffe (lnEndHandles, data-lh e:a/e:b bzw.
                      data-h rend:a/b im Editor): auf ein Kärtchen/Objekt = umhängen (lnEndOk prüft Ebene/Editor, Doppel; via und bend werden verworfen),
                      auf freie Stelle = Linie lösen (löschen), außerhalb der Fläche = nichts; Esc bricht ab. lvTargetAt nutzt elementsFromPoint und
                      übergeht Linien/Griffe/Leiste. Editor – Kontextleiste, Rechtsklick-Menü und Eigenschaften (Art #meRelMultiT) bieten bei ≥ 2
                      markierten Objekten „↗ verbinden“/„⇄“ (meConnIds, meConnSel; connTool/V verbindet direkt). Ein- und Ausblenden als Ganzes:
                      relVis(kind) = all|some|none; Knopf „⤳ … Beziehungen sichtbar / x von y sichtbar / ausgeblendet“ (me:relTog, B): bei „some“ und
                      „none“ blendet ein Klick ALLE ein (relShowAll löscht auch ui.relHide der Legende), bei „all“ alle aus; „Beziehungen: alle“ unter
                      ⚙ Anzeige löscht ui.relHide ebenfalls; relAddK blendet eine ausgeblendete Art beim Anlegen wieder ein. Übersicht: „Linien: alle“
                      und B (einblenden) setzen beide Häkchen. lnBarAt wählt aus mehreren Lagen eine, die die Linienenden nicht verdeckt (top-Rand im
                      Editor 48 px). Geprüft mit der Maus: markieren + verbinden (Übersicht und Editor), umhängen, lösen, Strg+Z; Logik für ungültige
                      Ziele und Doppel; 61 Darstellungen inkl. Markierung ohne Fehler/NaN, normalize idempotent.
                      VERLAUF, UMHÄNGEN, PRODUKTE, PROJEKTAUFTRAG (12.10.2026, Peters Kritik „Umhängen per Maus klappt nicht, ich finde nichts zum
                      Zurücknehmen – mindestens 20 Schritte; es fehlt die Kategorie Produkte unter den Fähigkeiten; je Projekt ein Template mit
                      Auftraggeber, Auftragnehmer, Auftrag, Zielen, Qualitätszielen, Projektorganisation (Editor, im Dokument eingebunden) und
                      Meilensteinen als Tabelle und Grafik“; Sicherung editor-backups/*_8em-produkte-projekte). Datenformat bleibt VERSION 8.
                      RÜCKGÄNGIG: undoStack/redoStack + undoInfo/redoInfo ({t, label, desc}), UNDO_MAX 300; pushUndoSnap merkt Zeit, undoLabel()
                      übernimmt die nächste Meldung (hint/meMsg/pmMsg) als Beschriftung, histDesc(alt, neu) beschreibt den Unterschied (Element
                      angelegt/gelöscht/umbenannt/verschoben, Verknüpfung/Beziehung umgehängt, Modell bearbeitet …). Knöpfe .undogrp „↶ Rückgängig“ ▾
                      „↷ Wiederholen“ in allen Kopfleisten (undoBtnsHtml, undoUi aktualisiert Zustand), ▾ = #histMenu (histMenuShow/histFill, Klick
                      = undoSteps(n) über mehrere Schritte, auch aus dem Text-Editor nach txCommit). UMHÄNGEN robuster: Linienende ohne vorherige
                      Auswahl greifbar (16 px/k um das Ende → drag 'rend' bzw. pick in der Übersicht), Klick-Variante ⤺/⤻ (me.rehang bzw. lvRehang:
                      Leiste, Rechtsklick, Eigenschaften; nächster Klick auf Objekt/Kärtchen = neues Ende, Esc/leere Fläche = Abbruch), im Baum
                      (Darstellung „Baum mit Linien“) Trefferpfade data-tl je Verbindungslinie → drag 'tl' = Kind unter neues Objekt (meTlOk,
                      meReparentKeep behält die Lage). PRODUKTE: Ebene 'produkt' (n 5a, Ordner „05a Produkte“, nach Fähigkeiten; Felder produktart,
                      zielgruppe, nutzen, lebenszyklus, bedeutung, kundennutzen, prio, verantwortlich), NEST = {faehigkeit, produkt} (verschachtelt wie
                      Fähigkeiten, capHtml allgemein), H_MAX, REL_T.produkt (setzt voraus, ergänzt, wird gebündelt mit, löst ab, konkurriert mit),
                      ME.produkt „Produktmodell“ 📦 (Produktlandkarte + Portfolio über PF/pfCfg: PFP_X Lebenszyklus/Kundennutzen × Bedeutung/Priorität),
                      HEAT/CHIPS/palTools/palIcon/kickOf/artPopHtml/kindChecks('produkt'), MX_PRESETS mit Produkt × Fähigkeit/Prozess/Ziel; Beispiel
                      mit zwei Produktbereichen (fiktiv). PROJEKTAUFTRAG: TX.projekte (Abschnitte auftraggeber, auftragnehmer, auftrag, ziele,
                      qualitaet, organisation fig 'porg', meilensteine table + fig 'ms'; Bausteine betroffene, risiken, aufwand, kommunikation, abnahme;
                      Qualitätscheck). Projektfelder + auftraggeber, beginn/ende mit Platzhalter TT.MM.JJJJ (fieldHtml kennt f.ph). Eingebundene
                      Grafiken: gespeichert nur <figure data-fig="porg|ms"></figure> (txClean behält sie außer beim Einfügen, txHtml leert sie);
                      txFigRender zeichnet sie im Editor (contenteditable=false, img aus figSvg als data:-SVG, Knopf .figbar „✎ Projektorganisation
                      bearbeiten“ = pmOpen(projekt, 'porg') über dem Text, „← Projektauftrag“ zurück, pmClose rendert neu; „↻ Zeitleiste aktualisieren“).
                      Zeitleiste msParse (Tabelle des Abschnitts meilensteine bzw. Tabelle mit Kopf „Meilenstein“, Spalten über Kopfzeile) + msDate
                      (TT.MM.JJJJ, JJJJ-MM-TT, MM/JJJJ, Qn/JJJJ, KW n/JJJJ, „Mai 2027“, JJJJ) + msSvg (Achse mit Monaten/Quartalen/Halbjahren/Jahren,
                      Laufzeitbalken aus beginn/ende, heute-Linie, Rauten nach Status msKind offen|erreicht|gefährdet|verschoben, Beschriftungen
                      abwechselnd oben/unten ohne Überlappung, Legende); txChanged zeichnet sie neu, wenn sich txMsKey ändert; Seitenleiste „Grafiken im
                      Dokument“ (txFigInsert fügt entfernte wieder ein). Ausgabe: txExportFigs (ohne Inhalt fällt die Grafik weg), Druck mit <img>,
                      Word: PNG über pmPngBlob → word/media/grafikN.png, wFigure (wp:inline, max. Satzspiegel 9070 dxa, Bildunterschrift kursiv),
                      Content-Type png, xmlns:wp. PROJEKTORGANISATION: PM_T.porg (project:true, dir v, Berichtsweg ohne Pfeil, Typen ag/lk/pl/tp/team/
                      stab/ext/grem/role/person/note, Form 'box'), PM_ALL = PM_KINDS + 'porg' (normModels), pmOpen nur prozess↔ePK/BPMN/SeeMe bzw.
                      projekte↔porg, pmNew('porg') Vorlage (Auftraggeber → Lenkungsausschuss → Projektleitung, Stab, drei Teilprojekte), pmChecks porg,
                      Ablage im Ordner der Projekte (lyOf). normalize behält Modelle jetzt auch bei Projekten (nur porg; vorher wären sie beim Laden
                      verworfen worden). pmUses kennt porg → Rolle/Person: „In der Projektorganisation …“; pmRemoveIfUnused zählt Projekt-Verknüpfungen
                      nicht als Nutzung. Eigenschaften „🏢 Projektorganisation anlegen/bearbeiten“ (pm:open:porg), editorTargets, Prüfung „Projekte ohne
                      Projektauftrag/ohne Projektorganisation“. Beispiel: Pilotprojekt mit Projektauftrag, Projektorganisation und fünf Meilensteinen
                      (fiktiv, 2027). Hilfe ergänzt (Projekte, Rückgängig und Verlauf, Produktmodell, drei Wege zum Umhängen).
                      Geprüft im Browser: Verlauf (Beschreibungen, mehrere Schritte zurück, Strg+Y), Umhängen per Ziehen/Klick/Baumlinie, Produktmodell
                      (Landkarte, Portfolio, Heatmaps, Export ohne NaN), Projektauftrag (Vorlage, Grafiken, Projektorganisation bearbeiten und zurück,
                      Zeitleiste aktualisiert sich beim Ändern der Tabelle, Grafik entfernen/wieder einfügen, Rückgängig in der Projektorganisation),
                      Word-Ausgabe in echtem Word geöffnet (2 Bilder, Satzspiegelbreite), alle Ansichten und Editoren ohne Fehler/NaN, normalize idempotent.
                      RÜCKGÄNGIG-PFEILE + DATENMODELL (12.10.2026 mittags, Peters Wunsch „Rückgängig/Wiederholen über die üblichen Pfeile, nicht klein,
                      kaum wahrnehmbar; Editor für Daten-, Geschäfts- und Informationsobjekte und ihre Zusammenhänge; Attribute als Tabelle beim Klick auf
                      das Objekt“; Sicherung editor-backups/*_8em-daten-editor). PFEILE: undoBtnsHtml(u, r) = große gebogene Pfeile UNDO_IC/REDO_IC (eigene
                      Zeichnung, SVG-Striche) in .undogrp/.urb, ▾ = Verlauf; immer direkt nach dem Titel (Übersicht: Platzhalter .undoslot wird beim Start
                      ersetzt; #meTop, #pmTop, #txTop – dort u/r = tx:undo/tx:redo ohne undoB/redoB, also nie gesperrt). undoLabel entfernt auch „– Strg+Z …“.
                      DATENMODELL: VERSION 9. ME.daten (key 'daten' = Pseudo-Schlüssel für alle drei Unterebenen: keyHas(key, e), hKids/layerEls kennen ihn;
                      meLY() liefert Ordner „07c …“ für Ablage, meNewLY(spec) die Unterebene neuer Objekte aus Palette (t.sub) bzw. gewähltem Objekt).
                      ME_FLAT = {daten} = ohne Hierarchie: nur Darstellung nest (dia erzwingt), meDropAt null, meAdd child → sib, freie Lage (Karten-Zweig
                      in meMoveEnd), Leiste statt Darstellungswahl „☰ Attribute | 🔑 nur Schlüssel | ▭ ohne“ (ui.daAttr, daMode/daShown). DA_T je Unterebene
                      {l, st, c, s, tier 0–2}, daCardSvg/daMeasure (Kopf mit Sinnbild daGlyph, «ART», Name; Attributzeilen DA_ROW 15 mit PK/FK-Kästchen, Name
                      + „*“, Typ rechtsbündig bzw. „→ Ziel“ bei FK, roter Punkt = personenbezogen; data-w atfoc/atadd/kick nur am Bildschirm; kick wechselt
                      die Art). Anordnen: je Stufe eine Reihe, Abstand 110 (shelfRoots mit gap). Element e.attribute = [{id, name, typ, fmt, key ''|PK|FK,
                      pflicht, pb, desc, bsp, ref}] (daNormAttr; normalize nur bei level daten, ref nur auf andere Datenobjekte). Felder data-at=
                      "<Element>|<Attribut>|<Feld>" (applyField/isEdit, Rückgängig wie alle Felder; attrAct hält vorher getippten Text als eigenen Schritt fest).
                      attrSectionHtml = schmale Tabelle (Schlüssel-Umschalter kt, Name, Typ mit datalist #daTypes = DA_TYPES, Pflicht, pb, ×) gleich unter dem
                      Namen in den Eigenschaften (Datenmodell UND Übersicht); Enter im Textfeld = neues Attribut darunter. #attrDlg (attrDlgOpen/Render,
                      Doppelklick auf Objekt = meOpenDeep, „☰ Tabelle“ in der Kontextleiste, Rechtsklick) = große Tabelle mit Nr., Schlüssel, Name, Datentyp,
                      Format/Länge, Pflicht, pers.-bez., verweist auf (Optgroups je Art), Beschreibung, Beispielwert, ↑↓×, Objektwechsel ◀ ▶/Auswahl.
                      REL_T.daten: assoz, teil (besteht aus), spez, abgebildet, gespeichert, abgeleitet, verweis; Kardinalität r.ca/r.cb (normalize, relPropsHtml
                      mit datalist #daCards, Kontextleiste #meCtxCa/#meCtxCb, daCardsSvg an den Linienenden). relAddK im Datenmodell ohne Art bzw. 'auto'
                      (connTool, Griff ↗, Markieren + verbinden, Übersicht, Matrix): daRelDefault – gleiche Stufe = assoz, sonst abgebildet bzw. gespeichert,
                      Richtung von fachlich nach technisch (tauscht a/b). Fassung < 9: Verknüpfungen zwischen zwei Datenobjekten werden zu solchen Beziehungen
                      (normalize). lvCanConnect: Daten↔Daten = Beziehung; Matrix (mxDa/mxOn/daRelsOf, 'mcell') zählt bzw. setzt bei Daten×Daten Beziehungen.
                      Ansicht „Attributkatalog“ (katalogDraw, auch Export/Ablage) + Export „Attributkatalog (CSV)“ (daCsv); allgemeine CSV mit Spalte „Attribute“.
                      HEAT.daten (Objektart, personenbezogen, Schutzbedarf, Anzahl Attribute, Schlüssel, Prozesse, IT, Leitfrage, Zustand), CHIPS.daten,
                      kindChecks('daten') (ohne Attribute, Datenobjekt ohne PK, pb-Attribute ohne Kennzeichen, ohne Schutzbedarf – ersetzt die alte Prüfung –,
                      Geschäftsobjekt ohne Abbildung/Verantwortliche, FK ohne Ziel, ohne Zusammenhang). Beispiel: 7 Objekte (neu Bestelltabelle), 21 Attribute,
                      7 Beziehungen mit Anzahl (fiktiv). Geprüft: Tabelle rechts (Klick ins Objekt, Attributzeile, ＋, Enter, Schlüssel-Umschalter), große
                      Tabelle (Bearbeiten, Wechseln, Rückgängig), Kardinalität per Kontextleiste, Verbinden mit automatischer Art und Richtung, Katalog, Anzeige-
                      modi, Exporte (SVG/PPTX ohne NaN, „＋ Attribut“ nicht in Ausgaben), Übersicht + Matrix, Übernahme Fassung 8, normalize idempotent,
                      alle 12 Editor-Ansichten ohne Fehler, Pfeile mit der Maus.
                      SCHLÜSSEL, CRUD, MASTER (12.10.2026 nachmittags, Peters Wunsch „Zuordnung Primär-/Fremdschlüssel oder nichts; Zuordnung
                      Create, Read, Write …; Objekte als Masterobjekte kennzeichnen“; Sicherung editor-backups/*_8em-crud-master). Format bleibt 9
                      (rein additiv). SCHLÜSSEL: DA_KEYS ausgeschrieben („kein Schlüssel“, „Primärschlüssel (PK)“, „Fremdschlüssel (FK)“), daKeySel/daRefSel;
                      Tabelle rechts jetzt je Attribut zwei Zeilen (tbody.atg: Name + Datentyp + ×, darunter Schlüssel, bei FK „→ verweist auf …“, Pflicht,
                      personenbezogen, CRUD-Kurzfassung crudSumShort → öffnet große Tabelle an der CRUD-Stelle). CRUD: e.crud / a.crud = {<id>: Teilmenge
                      von 'CRUD'} (crudNormRaw in daNormAttr bzw. normalize, nur ids der Ebenen aus CRUD_COLS = Prozesse, Funktionen, IT-Systeme/Plattformen,
                      Stellen/Rollen, Einheiten); crudToggle verknüpft Objekt und Element (addLink). Große Tabelle: crudBoxHtml unter den Attributen
                      (Spalten-Art #crudKind = ui.crudCols, „＋ … hinzufügen“ #crudAdd = Verknüpfung, Zeile „ganzes Objekt“ + Attribute, Knöpfe .crudc,
                      „⇣ auf alle Attribute“ = at:crudAll). Ansicht „CRUD-Matrix“ (crudDraw, data-crud im SVG, Klick in meWrap-pointerdown; Leiste
                      #meCrudCols und „auch nicht zugeordnete“ ui.crudAll; in Ausgaben nur gesetzte Buchstaben). HEAT.daten + crud. Attributkatalog mit
                      Spalte „Zugriffe (CRUD)“, daCsv mit Master/führendem System/CRUD. MASTER: F_MASTER (fieldHtml type 'check' mit data-on, applyField
                      setzt 'ja' bzw. '') in allen drei Unterebenen; bei Häkchen Auswahl „Führendes System“ (attrs.fuehrend = id eines IT-Systems/einer
                      Plattform, normalize prüft). Kasten: Schild „★ MASTER“ oben rechts über der Kante, Rahmen 3 px bronze; Übersichtskärtchen-Tag „★ Master“
                      und „n Attribute“ (hasMeta), HEAT.daten.master, Art-Menü, Palettensymbol „Masterobjekt“ (dmaster). kindChecks: Masterobjekt ohne
                      Verantwortliche (Data Owner) / ohne führendes System / mehrere „C“, Objekte nur gelesen ohne „C“. Beispiel: Kunde = Masterobjekt
                      (führend Kundendatenplattform), CRUD für Kunde, Bestellung, Kundentabelle und drei Attribute. Geprüft: Schlüsselwahl (FK zeigt Verweis),
                      Häkchen Master + Rückgängig in der Übersicht, CRUD-Klicks im Dialog und in der Matrix (auch mit der Maus), Spalte hinzufügen, auf alle
                      übertragen, alle Ansichten und Heatmaps ohne Fehler/NaN, PPTX der Matrix, normalize idempotent.
                      STANDORTMODELL + FRAGEBÖGEN (12.10.2026 nachmittags, Peters Wunsch „Editor mit Symbolen für Land, Bundesland, Stadt, Gebäude, Raum,
                      Serverraum, zugriffsgeschützte Räume, verschachtelbar; Tabelle der Mitarbeitenden mit Rollen, die die Orte nutzen dürfen; Fragebögen
                      Theorie U / Spiral Dynamics / kombiniert bei Mitarbeitern und Organisation abrufen; drei Farben mit dem größten Anteil der
                      Organisationseinheit zuordnen; Organisationsbögen auch für Gesamtorganisation und Standorte“; Sicherung editor-backups/
                      *_8em-standorte-frageboegen). Format bleibt 9 (rein additiv). STANDORT: ME.standort (📍 Standortmodell, Ansichten modell|zugang),
                      H_MAX.standort 8, ME_KINDS/ME_DEF_STYLE (nest)/DEF_W, diagrams-Whitelist = ME_KINDS. Arten ORT (oben bei F_MASTER: k, v = attrs.ortart,
                      sh Kennzeile, c/s Farben, nn Name, next = Art eines untergeordneten Ortes, z = voreingestellter Zutrittsschutz); Felder ortart,
                      standortart (jetzt „Nutzung“), zutritt (ZUTRITT öffentlich … sehr hoch), anschrift, ansprechperson, mitarbeitende. stGlyph (eigene
                      Sinnbilder je Art, nur Grundformen), stCardSvg/stMeasure (Blatt: Symbol links), contSvg mit stGlyph, lockSvg/LOCK_COL je stProtLvl
                      (stZutritt = Wert oder Voreinstellung der Art). stInit: neue Orte erben die Art (Gebäude › Etage › Raum) bzw. die des Nachbarn.
                      Palette je ORT-Art (Behältergrößen), Kennzeile wechselt die Art, Art-Menü mit Art und Zutrittsschutz. ZUTRITT: e.zugang = [{ref, art,
                      note}] (ref = Mitarbeiter/in, OE-Leitung oder Stelle/Rolle = ZG_REFS; ZG Zutritt/Karte/Begleitung/Admin/Notfall/verantwortlich mit
                      Kürzel Z K B A N V); jeder Eintrag = Verknüpfung (zgSet/addLink); removeLink ruft zgDrop (Lösen irgendwo entfernt den Eintrag);
                      zgList ergänzt verknüpfte Personen/Stellen ohne Eintrag („noch offen“); zgPersons = Personen direkt + über Stellen. Tabelle
                      stZugangHtml (Übersicht und Editor): Name als Eingabe (data-zgnm = Name in der Organisation), Zugang (data-zg), Rollen + „＋ Rolle
                      zuordnen“ (data-zgrole → Link Person–Rolle), Bemerkung (data-zgn), „＋ Person oder Stelle …“ (data-zgadd), „＋ Neue Person/Stelle“
                      (zgAct new/newrole), „▦ Zutrittsmatrix“. Palette „Zuordnen: Zutritt“ → meAssign trägt Zutritt ein. Ansicht zugangDraw (Orte ×
                      Personen bzw. Stellen ui.zgCols, „auch ohne Zutritt“ ui.zgAll; data-zgc, zgCycle; gestrichelt = über Stelle). HEAT.standort
                      (Art, Zutrittsschutz, Personen, Nutzung, Einheiten, IT = ST_IT inkl. Server/Netz, Werteprofil), CHIPS.standort (zugang, einheit, it,
                      prozess), REL_T.standort (anbindung, ausweich, beliefert, zugang, nachbar), kindChecks (Orte ohne Art, geschützte Orte ohne Liste/
                      Verantwortliche, Zutritt ohne Art, Personen ohne Rolle, > 10 Personen, Raum ohne Zutritt zum geschützten Bereich darüber, Serverraum
                      ohne IT), Export „Zutrittsliste (CSV)“ (zgCsv). FRAGEBÖGEN: FB {sd, u, kompass} mit Dateinamen je Perspektive; profile = [{id, art,
                      persp, datum, einheit, laenge, sd, sdSoll, u, uSoll, quelle}] NUR Anteile (fbNormList), bei doc (Gesamtorganisation), Einheiten,
                      Mitarbeitenden, OE-Leitungen (fbHolds, fbPersp) und Standorten. fbOpen öffnet den Bogen per window.open mit #mc4y8em=<Kennung>&persp=
                      &einheit=; message-Listener (format 'mc4y-8em-fragebogen', Kennung, ev.source) → fbParse → fbStore (Perspektive muss passen, Doppel
                      erkannt, Zwischenstand abgelehnt). „📂 Ergebnis einlesen …“ = #fbFile (fbImportFiles). fbTopOf = drei stärkste Farben des jüngsten
                      Ergebnisses mit SD-Anteilen (Werteprofil oder Kompass); fbShow nur für Einheiten/Orte (Personenergebnisse nie in Bildern) → sdRowSvg
                      im Organigramm-Kopf, in Orten, Tag .sdtag in der Übersicht, HEAT werte (Organigramm + Standort). fbSectionHtml: Farbchips, Theorie-U-
                      Felder, ▶ Werteprofil/Theorie U/Kompass, Liste mit ×, Hinweise (Einverständnis bei Personen, Durchschnitt der Personenbögen einer
                      Einheit erst ab drei Personen fbTeamAvg, nicht validiert, Reflexionsmodelle, Markenhinweis Spiral Dynamics®). Gesamtorganisation:
                      Modell-Eigenschaften (nichts gewählt) und Übersicht in Organigramm/Standortmodell. Geprüft: 180 Kombinationen Darstellung × Ansicht ×
                      Heatmap × Kärtchen ohne Fehler/NaN, PPTX/ODP/VSDX/PDF beider Ansichten, Palette mit der Maus in eine Etage gezogen, Matrix-Klicks +
                      Strg+Z, Tabelle (Stelle hinzufügen), Zuordnen per Palette, normalize idempotent, Übergabe ECHT getestet: Fragebogen (SD Organisation,
                      Kompass Person) in einem iframe mit window.opener = Editor ausgefüllt, Knopf geklickt → Ergebnis kam an; Doppel/Perspektive/
                      Zwischenstand. Hinweis: Das Vorschau-Browserfenster in Claude öffnet window.open im selben Tab – dort lässt sich die Übergabe nur so
                      (iframe) prüfen; in Chrome/Edge öffnet sich ein neuer Tab mit opener. Regression: alle 21 Ansichten aller Editoren ohne Fehler.
                      EBENE 8 NEU, TAP-RASTER, IT-SYSTEMLANDSCHAFT, IT AM ORT, NACH VORN/HINTEN (12.10.2026 nachmittags, Peters Wunsch „TAP-Editor nach der
                      beigefügten Excel (fachliche Layer × Tier-Matrix), Zeilen und Spalten an beliebiger Stelle einfügen/löschen; TAP zwischen IT-Systemen und
                      Plattformen, IT-Systemen zuordenbar; IT-Systeme als Releases und Varianten; Plattformen analog TAP editierbar und mehreren IT-Systemen
                      zuordenbar; darunter IT-Komponenten, mit Plattformen verknüpfbar; Client/Business/Data Tier, Server, Netzwerk löschen“ + Fehler „nach vorne/
                      hinten funktioniert im Standort-Editor nicht“, „Anlegen von Datenbanken, IT-Systemen, Servern, Clients funktioniert nicht“; Sicherung
                      editor-backups/*_8em-tap). VERSION 10. Unterebenen werkzeug: nichtit, methoden, itsysteme (Gruppe „IT-Unterstützung“, H_MAX 3, Felder itart
                      IT_ART, version, itstatus IT_STATUS, produkt, betrieb, systemverantwortung, einsatzVon/Bis), architekturplan (version, planstand, geltung; kein
                      docFirst mehr), plattformen (produkt, betrieb, version, itstatus, plattformverantwortung), komponenten (komponentenart KOMP_ART, produkt,
                      version, technologie, bereitstellung, betrieb, itstatus, standort-Text). Ordner 03 IT-Systeme, 04 Technischer Architekturplan, 05 Plattformen,
                      06 IT-Komponenten. Migration: normalize macht aus client/business/data/server/netzwerk komponenten (TIER_SUBS → komponentenart; serverart →
                      bereitstellung, netzart → technologie), Dokumentpfade per migratePath(p, OLD10); openFolder ruft moveFolders(OLD10) für Fassung 3–9
                      (überspringt gleiche Ordner, „(aus alter Ablage)“ bei Namensgleichheit). OLD_DIRS (Fassung 2) zeigt jetzt auf komponenten. Verweise angepasst:
                      ST_IT = IT_KEYS + komponenten, BPMN-Datenspeicher → komponenten, MX_PRESETS, HEAT/CHIPS daten, checks(). RASTER (e.grid, gridNorm/gridNew,
                      GRID_SUBS architekturplan|plattformen): {corner, rows [{id,t}], cols [{id,t,w}], rw, cells {'z|s': {t, refs}}}; TAP_TPL = Peters Excel
                      (7 Layer × 5 Tiers, Zeile „Anwendung & Benutzerschnittfläche“ wörtlich übernommen); Vorlagen 'tap' (leer), 'tapbsp' (mit Inhalten), 'leer' 3 × 3.
                      Raster-Editor #tg (tg*-Funktionen, Vollbild wie #tx; sel = bearbeitetes Element, data-f rechts): HTML-Tabelle table.tgt mit festen Spalten-
                      breiten (colgroup + Tabellenbreite), Texte als textarea data-tg="<id>|k|r|c|x…" (applyField/isEdit, Rückgängig über focusin/change),
                      Kopf-Knöpfe .hb (＋ oben/unten, ＋ links/rechts, ▲▼ ◀▶, ✕) bei Hover/Fokus, Rechtsklick #tgMenu (tgHeadMenu, auch je Zelle), Spaltenbreite
                      .cw ziehen (ein Schritt), Bausteine cell.refs (IT_ALL) mit Chips, „＋ Baustein“ = tgRefMenu (Suche #tgRefQ, neue IT-Komponente #tgNewName/
                      #tgNewArt), addLink zum Raster-Element; Chip-Klick = tgRefPropsHtml (data-tgel). itAssignHtml (auch Übersicht und IT-Systemlandschaft):
                      IT-System ↔ Plattformen/Architekturplan, Plattform ↔ IT-Systeme/IT-Komponenten/Architekturplan, Komponente ↔ Plattformen (Häkchen
                      data-assign). Import: xlsxRows (eigener ZIP-Leser zipOpen/zipText mit DecompressionStream, sharedStrings, erstes Blatt über workbook.rels,
                      Spaltenbreiten) bzw. csvRows; Export tgXlsx (makeZip, inlineStr, Kopf fett/hinterlegt, Umbruch, fixierte Kopfzeile/-spalte, Breiten), tgCsv,
                      tgSvg → svgToPrims/XP (PPTX/ODP/VSDX/PDF), SVG, PNG; tgToAblage (xlsx+svg+png). Einstiege: Editoren-Menü, Knöpfe in Band-Kopf und
                      Leitfaden der Ebene 8 (tg:first:<sub> legt bei Bedarf an), Übersicht „▦ Raster bearbeiten“, Doppelklick (editorTargets), editorOnCreate.
                      IT-SYSTEMLANDSCHAFT: ME.itsystem (key werkzeug.itsysteme, meKindOf mappt), Palette it:sys/rel/var/soll/weg, HEAT.itsystem (Art IT_ART_C,
                      Lebenszyklus, Betrieb, Plattformen, Fähigkeiten, Prozesse, Daten …), CHIPS (plattform, tap, komp …), REL_T.itsystem (schnittstelle, nutzt,
                      abloese, ueberschneidung), kick = Art + Version, stInit setzt itart (oben IT-System, darunter Release), kindChecks (ohne Plattform, Release
                      ohne Version, Lebenszyklus offen, mehrere Releases im Einsatz, Auslauf ohne Nachfolger). normalize/relsClean prüfen Beziehungen jetzt über
                      meKindOf. IT AM ORT (Standortmodell): Palettengruppe 'itnew' (ST_NEW: Server, Client, Datenbank, Netzwerk, Speicher, Sicherheit, sonstige
                      Komponente, Plattform, IT-System; itGlyph/itGlyphKey), placeTool bzw. Klick bei gewähltem Ort → stItNew (anlegen in Ebene 8 + addLink +
                      Kärtchen „it“), stItHtml rechts (Name editierbar, × löst, „＋ Vorhandenes …“ data-stitadd, ＋-Knöpfe stit:new). NACH VORN/HINTEN: act('move')
                      tauscht jetzt auch die Plätze (meSwapPos: nest x/y, tree samt Unterbaum; Übersicht e.lay) – vorher änderte sich nur die Gliederung; neu
                      „⤒ Vordergrund/⤓ Hintergrund“ (meZOrder, Rechtsklick/Aktionen) und Gestaltungselemente mit it.z (itemBack; Notiz nach hinten, Rahmen nach vorn).
                      Geprüft: Excel-Import von Peters Datei (35 Zellen, Breiten), Export→Import verlustfrei, .xlsx in echtem Excel (Blatt, fett, Umbruch, fixiert) und
                      .pptx in PowerPoint (100 Formen) geöffnet, Zeile/Spalte per Maus und Rechtsklick eingefügt und benannt, Baustein neu per Menü, Strg+Z/Esc,
                      Release per Palette in System gezogen, Platztausch per Klick, Server per Palette in Serverraum, Fassung-9-Ordner (OPFS) mit Dateien migriert,
                      344 Darstellungen aller Editoren × Darstellung × Ansicht × Heatmap ohne Fehler/NaN, normalize idempotent.
                      ICT-OBJEKTE, KATALOGE, VERANTWORTUNG, KÄRTCHEN UMHÄNGEN, START (12.10.2026 abends, Peters Kritik „Abfrage beim Start, obwohl
                      die Einstellung nicht gemerkt wird; Beziehungen lassen sich immer noch nicht lösen und anders zuordnen“ + Wunsch „ICT-Objekt für
                      Plattformen, IT-Systeme, TAPs, IT-Komponenten; Komponentenkataloge; zu jedem IT-System automatisch eine IT-Komponente; Rollen und
                      verantwortliche Mitarbeiter je Objekt per rechter Maustaste auswählen oder neu anlegen; Komponenten in Releases und Versionen;
                      in der Rasterzelle per Rechtsklick zuordnen/neu anlegen, mehrere je Zelle; eigenes Verzeichnis je Kategorie; Standardisierung“;
                      Sicherung editor-backups/*_8em-ict-objekte). VERSION 11.
                      START: init ruft autoOpenRecent() – lastDir mit queryPermission 'granted' (Browser-Rückfrage „Bei jedem Besuch zulassen“) wird
                      ohne Startfenster geöffnet, sonst Startfenster mit großem, vorausgewähltem Knopf #startRecentBtn (Enter) und Häkchen #startAutoChk
                      (ui.startAuto, Standard an) samt Erklärung. Die frühere Abfrage war das Startfenster selbst (erschien immer). saveToFolder ruft
                      ensurePerm() (fordert zurückgenommenen Zugriff neu an). Ob Chrome bei file:// „Bei jedem Besuch zulassen“ anbietet, ist ungeprüft.
                      ICT-OBJEKTE: attrs.itart = Stufe (IT_ART '', ICT-Objekt, Release, Version, Variante; F_STUFE) in allen vier Kategorien ICT_KEYS
                      (itsysteme, architekturplan, plattformen, komponenten), H_MAX je 3; Fassung < 11: „IT-System“ → „ICT-Objekt“, ohne Stufe → ICT-Objekt.
                      stInit: oberste Stufe ICT-Objekt, darunter ictNextStage (Release, dann Version). Standardisierung attrs.standard (IT_STD mit Farben:
                      in Erprobung, in Einführung, Standard, Non-Standard, Limited Standard, abgekündigt, in Abschaltung, abgeschaltet; F_STD) – Farbstreifen
                      links im Kasten (stdBarSvg) und im Kärtchen (chipsSvg c.std), Tag in der Übersicht, Heatmap, Art-Menü, Raster-Bausteinmenü.
                      KATALOGE: neue Unterebene werkzeug.kataloge („IT-Komponentenkataloge“, Ordner „08 …/07 IT-Komponentenkataloge“, katalogart KAT_ART
                      Katalog › Kategorie › Unterkategorie, H_MAX 3); Komponente ↔ Katalog = normale Verknüpfung (n:m); katGroups() gruppiert Komponenten
                      nach Katalogpfad (Zellenmenü, Häkchenlisten > 8). NEUE ME-EDITOREN: itkomp (⚙ IT-Komponenten, key werkzeug.komponenten) und itkat
                      (📚 Komponentenkataloge) – ME_SUB-Zuordnung in meKindOf, REL_T.itkomp (läuft auf, nutzt, verbunden, löst ab, austauschbar),
                      REL_T.itkat, HEAT/CHIPS/palTools (Komponenten je Art als ICT-Objekt, Release, Version; Katalog/Kategorie), itGlyph os|mw|app|cloud|
                      katalog|tap, KOMP_GLYPH, kindChecks + ictChecks(L, tops, push, what) (auch Plattformen/Architekturpläne in checks()).
                      GEGENSTÜCK (twinSync, in refresh()/meRender() über ictSync): je IT-System eine IT-Komponente mit komponentenart KOMP_APP „Anwendung
                      (technisch)“, gleicher Stufe, Einordnung unter dem Gegenstück des übergeordneten Systems, verknüpft. System attrs.technisch = id bzw.
                      'aus'; Komponente attrs.fachlich, twinName (Name folgt, bis die Komponente eigens umbenannt wird), twinParent (Einordnung folgt, wenn
                      das System umgehängt wird). Fehlt die Verknüpfung (gelöst/umgehängt) oder wird die Komponente gelöscht → Kopplung aufgehoben ('aus');
                      „Wieder automatisch anlegen“ (chip:twin). twinGone(gone) beim Löschen (act del, meDelete, pmRemoveIfUnused): ungenutztes Gegenstück
                      fällt mit weg. twinHtml rechts. Beim ersten Öffnen älterer Modelle entstehen die Gegenstücke automatisch.
                      VERANTWORTUNG: e.verantw = [{rolle, person, rname, pname, note}] bei VA_KEYS (ICT + kataloge); rolle = 7a Rolle, person = Mitarbeiter/
                      OE-Leitung (VA_PERS); rname/pname = noch nicht zugeordneter Name (früheres Freitextfeld systemverantwortung/plattformverantwortung,
                      Felder entfallen; normalize ordnet gleichnamige Rollen/Personen zu). vaAdd (Namen eintippen oder aus datalist wählen; Neues wird in 7a
                      angelegt, Person ↔ Rolle verknüpft), vaDel, vaFix („anlegen“), vaDrop in removeLink, vaLinkSync. Dialog #vaDlg (vaDlgOpen/Render,
                      Enter in #vaRole/#vaPers/#vaNote), Abschnitt vaSectionHtml (Übersicht, Modell-Editoren, Raster, Bausteinmenü), Rechtsklick „👤
                      Verantwortung …“ in Übersicht, Modell-Editoren, Kärtchen-Menü, Raster-Baustein; CSV-Spalte „Verantwortung“; Heatmap vaState.
                      KÄRTCHEN (Zuordnungen im Objekt) jetzt greifbar: chipItems liefert {t, id, std}, chipLayout/chipsSvg(…, owner) → <g data-chip=
                      "owner|id"> (nicht im Export). meWrap-pointerdown: drag.mode 'chip' (Ghost, meHiRect grün/rot über chipOk) → chipMoveTo (Strg =
                      zusätzlich; Zutritt-Art, RACI/Beschriftung/Pfeil, Verantwortungseinträge wandern mit) bzw. freie Fläche = chipDrop (lösen);
                      Rechtsklick (meMenuShow cEl): lösen, „umhängen …“ (me.chipMove, nächster Klick = Ziel, Esc bricht ab), öffnen (goEl/tgOpen), Verantwortung.
                      RASTER: Rechtsklick in eine Zelle bzw. „＋ Baustein“ = tgCellMenu (Suche, IT-Systeme, Plattformen, Komponenten nach Katalog; Klick
                      = an/abwählen, Menü bleibt offen – Klick-Handler schließt #tgMenu bei tg:togref nicht; Neu anlegen: #tgNewKind Komponente/Plattform/
                      IT-System, #tgNewArt, #tgNewKat; Zeilen-/Spaltenbefehle tgRowColItems darunter). tgTogRef löst beim letzten Vorkommen auch die
                      Verknüpfung. Bausteine ziehen (tgDrag, #tgGhost, td.dropok) = tgMoveRef (Strg = kopieren); Rechtsklick auf Baustein = tgChipMenu
                      (bearbeiten, Verantwortung, Standardisierung tg:std, öffnen, aus Zelle nehmen). Navigation links eingerückt mit Stufe; „＋ Release/
                      Version von …“ (tg:newstage) kopiert das Raster samt Verknüpfungen. tgRefPropsHtml mit Stufe, Standardisierung, Katalog, Verantwortung.
                      itAssignHtml: Komponente ↔ Kataloge/Plattformen/IT-Systeme, Katalog ↔ Komponenten, IT-System ↔ Komponenten.
                      Geprüft: 2082 Darstellungen (Editoren × Ansicht × Darstellung × Heatmap × Kärtchen) und alle Übersichtsansichten ohne Fehler/NaN,
                      normalize idempotent; mit der Maus: Kärtchen umhängen, lösen (freie Fläche), Rechtsklick-Menü + „umhängen …“, Zellenmenü mit zwei
                      Bausteinen, Baustein in andere Zelle gezogen, Standardisierung per Bausteinmenü, Verantwortungsdialog (neue Rolle, vorhandene Person);
                      per Skript: Standort-IT und Zutritt umhängen, Gegenstück entkoppeln/neu anlegen/löschen/umbenennen/umhängen, Release mit Raster,
                      Fassung-10-Datei (Freitext-Verantwortung, Stufen), OPFS-Ordner (07 angelegt, Speichern, Autostart mit erteilter Erlaubnis),
                      Exporte PPTX/SVG der neuen Editoren und des Rasters ohne NaN.
                      NACHBESSERUNG 12.10.2026 spätabends (Peters Kritik: „völlig sinnbefreiter Dialog nach dem Öffnen – merken oder ganz lassen“,
                      „Rechtsklick in Katalogen: kein Menü zum Auswählen/Zuordnen von Komponenten“, „Beziehungen farblich markieren, sonst nicht
                      ersichtlich, wozu sie gehören“, „TAP: aus dem Suchfeld anbieten oder mit diesem Namen anlegen“, „Vorder-/Hintergrund, nach
                      vorn/hinten nach wie vor ohne Funktion“). DIALOG: Es war confirm „ungespeicherte Änderungen aus der letzten Sitzung“ in openFolder –
                      erschien jedes Mal, weil schon das Öffnen (Fassungswechsel, twinSync) dirty machte. Jetzt userDirty (nur eigene Änderungen:
                      markDirty() ohne Argument; automatische = markDirty(true), ictSync) – Autosave mit user-Kennzeichen, booting unterdrückt Autosave beim
                      Start. openFolder(dir, noRestore): kein confirm mehr; eigene ungespeicherte Änderungen (s.user === true, gleicher Ordner, neuer als die
                      Datei) werden still übernommen, Statuszeile mit „gespeicherten Stand laden“ (act reloadSaved). Rückfragen beim Wechseln, Dirty-Anzeige
                      und beforeunload nur noch bei userDirty. ZUORDNEN PER RECHTSKLICK: #asgMenu (asgOpen/asgRender/asgAct/asgEnter, Einträge
                      „⊕ Zuordnen: …“ je CHIPS-Art im Rechtsklick-Menü der Editoren und der Übersicht; lastCtxXY aus contextmenu-Capture): Liste mit
                      Suchfeld, Klick = zuordnen/lösen (Menü bleibt offen, Klick-Handler schließt #asgMenu nicht), kein exakter Treffer → „＋ … neu anlegen“
                      (elKindName, Auswahl der Unterebene bei mehreren); pickFilter/pickEnter gemeinsam mit dem Raster (Enter: ein Treffer/exakter Name =
                      wählen, kein Treffer = anlegen, mehrere = Hinweis). katGroups ordnet Releases/Versionen dem Katalog ihres ICT-Objekts zu. CSS-Fehler
                      behoben: „.tgrl [hidden]“ wurde von display:flex überstimmt (Filter wirkte nicht). RASTER: #tgRefQ ist zugleich Name für Neues
                      (#tgCreate, „neu als“ #tgNewKind/#tgNewArt/#tgNewKat; #tgNewName entfällt), Menü bleibt nach dem Anlegen offen. LINIENFARBEN:
                      Übersicht ui.lineColor element|grau („Farbe:“), lineOwner/lineColorOf (LINE_PAL, Nachbarn verschieden), Punkt am Ausgangsende;
                      Editoren ui.meRelCol art|objekt („⚙ Anzeige › Linienfarbe“). NACH VORN/HINTEN: immer Rückmeldung (kein Nachbar, schon ganz
                      vorn/hinten), flashEls (goldener Rahmen .meflash bzw. #view .flash), lvSwapLay (Übersicht, auch wenn nur ein Kärtchen frei liegt).
                      VORDER-/HINTERGRUND: q.z = 'front'|'back' in der Lage-Tabelle je Darstellung (normalize behält z); modelSvg zeichnet 'back' zuerst
                      (vor Rahmen/Bändern bzw. vor Geschwistern), 'front' samt Inhalt zuletzt (nach Notizen); erneut wählen hebt auf; Meldung nennt
                      Überlappungen. ALT BEIM ZIEHEN (d.keep): nicht hineinlegen/unterordnen, nicht ausweichen → Objekte dürfen überlappen.
                      Geprüft: OPFS-Ordner (Fassung 10 öffnen, erneut öffnen, eigene Änderung ungespeichert → still wiederhergestellt, reloadSaved,
                      speichern) ohne einen confirm-Aufruf; Rechtsklick im Katalog-Editor mit der Maus → Zuordnen-Menü, Filter, Enter legt „MariaDB“ an;
                      Raster: Enter wählt einzigen Treffer, Hinweis bei mehreren, legt neuen Namen an; Vorder-/Hintergrund mit überlappenden Räumen
                      (Zeichenreihenfolge, Sichtprüfung), Alt-Ziehen ohne Hineinlegen; Linienfarben in der Übersicht; 4164 Darstellungen ohne Fehler,
                      normalize idempotent, PPTX mit farbigen Beziehungen.
                      ICT-OBJEKT NEU, PLANUNGSSTÄNDE, ROADMAP, MODELL FRAGEN (12.10.2026 spätabends, VERSION 12, Build 20:05; Sicherung editor-backups/
                      *_8em-zeit-fragen). Peters Korrektur: „Das ICT-Objekt ist unabhängig vom zugehörigen Objekt. Einem ICT-Objekt sollen Releases und
                      Versionen des Objekts zum Bündeln zugeordnet werden können“ (Fassung 11 hatte es fälschlich als oberste Stufe der Einträge).
                      ICT-OBJEKTE: eigene Unterebene werkzeug.ictobjekte (ICTO_KEY, Ordner „08 Werkzeuge und IT-Systeme/08 ICT-Objekte“ – angehängt, keine
                      Ordnerumbenennung), Felder ictkat (F_ICTKAT, ICT_CAT [Unterebene, Bezeichnung, Kurzform]), Produkt, Standardisierung, Lebenszyklus,
                      Verantwortung (VA_KEYS). Bündelung = normale Verknüpfung ICT-Objekt ↔ Eintrag der vier Kategorien; je Eintrag höchstens ein ICT-Objekt:
                      addLink ruft ictExcl (löst andere ICT-Verknüpfungen, Meldung „vorher in …“). ictOf/ictMembers/ictFits/ictState/ictMake („ICT-Objekt aus
                      … bilden“: Name des Grundobjekts, bündelt dessen Releases/Versionen, schon gebündelte bleiben)/ictPropsHtml/ictAct (ict:make|off|rm).
                      IT_ART = ['', 'Release', 'Version', 'Variante'] ('' = Grundobjekt, IT_ART_L); normalize löscht itart 'ICT-Objekt'/'IT-System' und
                      ungültige Werte, ictkat nur bei ICT-Objekten. ME.ictobj (🗂, ME_SUB ictobjekte, ME_FLAT, DEF_W 260), Palette icto:<Kategorie>,
                      itGlyph 'icto', CHIPS.ictobj mitglied/org (CHIP_DEF zeigt „mitglied“ ohne eigene Wahl), CHIPS itsystem/itkomp + 'ict', ASG_X.ict für
                      Plattformen/Architekturpläne (asgCats), Zuordnen-Menü filtert nach Kategorie (ictKeysOf/ictFits), zeigt „in …“ bei anderem ICT-Objekt,
                      „neu“ setzt Kategorie bzw. hängt ein neues Release unter das gleichnamige Grundobjekt. meAssign/chipOk lehnen falsche Kategorie ab.
                      HEAT ictobj + „ICT-Objekt“ (C_ICT) in itsystem/itkomp, REL_T.ictobj (nutzt, löst ab, Alternative), kindChecks('ictobj'), ictChecks meldet
                      Releases ohne ICT-Objekt. Beispiel: ICT-Objekte „Modellierung (Anwendung)“ (w2a/b/c) und „PostgreSQL“ (k2a/b).
                      (Die Planungsstände der Fassung 12 – doc.plans, ab/bis, lifeOf, ui.stage-Filter, stageNewSync, lifeHtml, „Gilt ab/Entfällt ab“ – sind
                      seit Fassung 13 ERSETZT, siehe „IST-/SOLL-BILDER“; nur bilderAusPlaenen liest sie noch beim Öffnen alter Dateien.)
                      IST-/SOLL-BILDER + NEUE ABFRAGE (12.10.2026 nachts, VERSION 13, Sicherung editor-backups/*_8em-bilder-abfrage). Peters Kritik: „Die
                      Abfragelogik bekommst Du mit Sicherheit noch bedeutend besser hin … Zudem sind die einzelnen Ist- und Sollbilder nicht einzeln darstellbar
                      und modellierbar; die Sollbilder sollten das Ist oder letzte zurückliegende Soll übernehmen und dann veränderbar sein, ohne Einfluss auf
                      die zurückliegenden Bilder.“ BILDER: doc.bilder = [{id:'ist', name:'Ist'}, {id, name, date 'JJJJ-MM', note, von}] (Ist zuerst, Soll nach
                      Datum, bildSort), doc.bild = gezeigtes Bild. Jedes Bild ist ein VOLLSTÄNDIGES eigenes Modell (IMG_PART elements, links, rels, diagrams,
                      plk): das gezeigte liegt in doc.elements/links/rels/diagrams/plk (alle Ansichten/Editoren unverändert), die übrigen in bilder[i].m.
                      bildSwap(id) tauscht die Arbeitsdaten, bildSwitch(id, elId) zeigt ein Bild (bildAfter: Auswahl, offene Editoren), withBild(id, fn) rechnet
                      vorübergehend in einem anderen Bild. Gleiche Element-ids in mehreren Bildern = dasselbe Element. Neues Soll-Bild = Kopie des gezeigten
                      bzw. gewählten Bildes (bildNew; Datum ein Jahr später bzw. zwischen zwei Bildern; beim ERSTEN Soll-Bild: Zustand „geplant“ = nur im
                      Soll, „entfällt“ = nur im Ist, danach state 'ist'). bildDel (übrige unverändert), bildReach (erstes Soll wird Ist, doc.date = Zieldatum).
                      Vergleich bildCmp() (zwischengespeichert über docVer – zählt in markDirty) → stageMark 'neu' (nicht im vorigen Bild) / 'geaendert'
                      (elDiff ≠ leer: Name, Ebene, Einordnung, Beschreibung, Merkmale, Verantwortung, Attribute, Zutritt, Raster, Text, Abläufe, CRUD …) / 'aus'
                      (fehlt im nächsten Bild), kombinierbar; markMain/planCat. Darstellung: Kärtchen .stg-neu/.stg-gea/.stg-aus, bildTag, meStyle (grün/blau/
                      orange gestrichelt), stageBadgeSvg NEU/GEÄNDERT/ENTFÄLLT DANACH, Heatmap „Ist-/Soll-Bilder: Veränderung“ (C_PLAN). Leiste #stageBar
                      (stageBarHtml): Name + Zieldatum direkt änderbar (data-pl – input-Ereignis zeichnet nicht neu, change sortiert), Zahl neu/geändert,
                      „n entfallen ▾“ (ui.bildGone, Liste mit „zurückholen“ = bildBack samt Gegenstück und Verknüpfungen), ◀ ▶, ＋ Soll-Bild, ⚙ (#planDlg =
                      Tabelle der Bilder: Name, Datum, Beschreibung, übernommen von, Änderungen +~−, zeigen, ＋ Kopie, erreicht, ×; unten „Neues Soll-Bild als
                      Kopie von [Auswahl #bildFrom]“), Vergleich (ask:cmp), Roadmap. Auswahl stageSelHtml (#stageSel, #meStageSel) mit „＋ Neues Soll-Bild“
                      und „⚙ Bilder verwalten“. Aktionen bildAct (data-act bild:…: dlg, close, show:<bild>[:<el>], step, new[:<von>], newsel, del, reach, gone,
                      back, push/add/drop, props). Übernahme in spätere Bilder (Rechtsklick extraMenuItems, Art-Menü ◈, Eigenschaften bildPropsHtml „🗓 In den
                      Bildern“ mit Chips je Bild = dort zeigen): bildPush → bildPush1 je Element + technisches Gegenstück; 'push' überträgt nur die DELTA
                      gegenüber dem vorigen Bild (bildDelta: geänderte Felder/Merkmale, hinzugekommene/entfernte Verknüpfungen und Beziehungen; lay nie),
                      ist das Element hier neu, wird es ganz übernommen (Meldung sagt das); 'add' nimmt es auf, 'drop' entfernt es (Kinder rücken hoch).
                      Gegenstücke IT-System ↔ IT-Komponente haben seit Fassung 13 die id 'tw' + id des Systems (gleich in allen Bildern); ictSync gleicht die
                      übrigen Bilder einmal je Datenbestand ab (WeakSet bildSynced, nach Übernahmen gelöscht). elAny(id) findet Elemente in allen Bildern.
                      normalize: Fassung ≤ 12 mit doc.plans → bilderAusPlaenen (je Stand die dort geltenden Elemente/Verknüpfungen/Beziehungen; ab/bis
                      entfallen), jedes Bild über normalize(…, _bild:true) geprüft, Reihenfolge Ist + Datum; Rundlauf idempotent. Prüfung: „Entfällt im
                      nächsten Bild, wird dort aber noch gebraucht“ (ohne „löst ab“ in beiden Bildern) und Soll-Bilder ohne Zieldatum. CSV-Spalte „Ist-/Soll-
                      Bilder“ (bildText + Veränderung). Beispiel: Planung wie bisher in exampleDoc, am Ende bilderAusPlaenen + eigene Änderungen je Soll-Bild.
                      ROADMAP neu (rmModel/rmSvg/rmChangesHtml): Vereinigung aller Bilder (Fassung aus dem gezeigten bzw. jüngsten Bild), Balken je Lauf
                      über die Bilder, in denen das Element vorkommt, ＋/✕, blaue Raute = geändert (Hinweis mit Unterschieden), Zeilen nicht im gezeigten
                      Bild grau kursiv (Klick zeigt das erste Bild mit dem Element), Bild-Beschriftung oben = bild:show, Änderungen je Bild (neu, geändert mit
                      Einzelheiten, entfällt, Verknüpfungen +/−, „im Einzelnen …“ = ask:cmpi).
                      MODELL FRAGEN neu (Block „Modell fragen“ Fassung 13): Frage → askParse → askToQ → Abfrage Q {bild, vs, mode (ASK_MODES: liste, anzahl,
                      kette, rang, luecke, wirkung, grundlage, verantwortung, weg, steckbrief, vergleich), start {t, els, conds}, steps [{t, conds}], depth,
                      wide, out tabelle|matrix, top, only neu|gea|weg} → askExec (withBild, AX = Verzeichnis+Graph askIx je Bild/docVer, prev/next-Bilder für
                      neu/geändert/entfällt). Baukasten askBuilderHtml (data-aq → askField, sofort neu gerechnet; Art-Auswahl Gruppen ASK_T + jede
                      Unterebene 'ly:<key>'; Bedingungen askCondFields: Name, Beschreibung, alle Merkmale (Auswahl/Text/Skala), Dient den Menschen,
                      Anzahl Verknüpfungen, Verantwortung, Dokumente, benannte ASK_F_PICK; Formular .aqform mit ist/ist nicht/enthält/mindestens/leer …,
                      ⇄ verneint; Bedingung {id 'f:k'|'x:links|va|docs'|'af:i', op, v, neg}, askCondTest – Prozessart erben Teilprozesse). Parser: Namen in
                      Anführungszeichen, Bildnamen, Elementnamen (Vereinigung aller Bilder, askNames zwischengespeichert), Jahr/heute/Zielbild/nächstes/
                      voriges Bild, Absicht (Rangliste „meiste/Top N“, Matrix, davon, Vergleichswörter mit \b), ASK_F (verneint durch „nicht/kein“ davor;
                      „ohne Verantwortliche“ ist Bedingung), Zahlen („mindestens 3 Verknüpfungen“, „Reifegrad Soll mindestens 4“ – Merkmal nach Nachbarwörtern,
                      bevorzugt passend zur Art), Auswahlwerte aller Merkmale (ab 5 Zeichen, nicht ASK_VAL_STOP), Arten, „Wer“ = Organisation, „Meinten Sie“
                      (Teilwörter ≥ 5 Zeichen außer ASK_STOP/ASK_STOP_PRE). Antworten: Kette (askChain, ● direkt/○ indirekt als Matrix askMatrix), Rangliste mit
                      Balken, Lücken, Auswirkungen/Grundlagen (askDir), Verantwortung, Weg (askPaths), Steckbrief (mit Bildern/Unterschieden), Vergleich
                      askCompare (Elemente: neu / geänderte Merkmale / nur Verknüpfungen / entfallen; mit Schritten: Paare Ausgang → letzter Schritt in beiden
                      Bildern, „hat keine … mehr“). askChip führt bei Ergebnissen aus einem anderen Bild zu bild:show. Folgefragen: res.ids → „→ Mit diesen n
                      weiterfragen“ (ask:more) bzw. „davon“ (askState.lastIds). Namensvorschläge beim Tippen (#askSug, askSuggest/askInsert). Gemerkte Fragen
                      doc.fragen [{id, name, text, Q}] (☆ Merken, Chips oben, ×; normalize mit askQNorm). renderFragen rechnet neu, wenn askKey (docVer, Bild,
                      Q) sich ändert.
                      Geprüft: 57 + 405 Darstellungen (alle Ansichten und Editoren × 3 Bilder, alle Heatmaps × Bilder, alle Beispiel- und Zusatzfragen je Bild)
                      ohne Fehler/NaN, Roadmap- und Editor-Exporte (PPTX) ohne NaN, normalize idempotent, Fassung-12-Umwandlung (2 Pläne, ab/bis an Elementen
                      und Verknüpfungen), erstes Soll-Bild aus geplant/entfällt, Bearbeiten im Soll ändert Ist/Zielbild nicht, Übernahme push (Delta bzw. ganz),
                      add (mit Gegenstück, per Rechtsklick mit der Maus), drop, zurückholen, Bild löschen, erreicht + Strg+Z, im Modell-Editor per Doppelklick
                      angelegt (nur im Soll-Bild), Baukasten: Bedingung per Maus (Prozessart), Bild umgeschaltet, weiterfragen, davon, merken/ausführen,
                      Vergleich aus der Bildleiste; 25 Fragen per Skript.
                      AUSWERTE-WERKZEUG (12.10.2026 nachts, Build 22:03, Format bleibt 13; Sicherung editor-backups/2026-10-12_21-32-45_8em-auswertung).
                      Peters Kritik: „Du kannst ja noch nicht einmal die simple Frage beantworten: ‚Wie viele Objekte gibt es insgesamt im ausgewählten
                      Modell‘ … vernünftige Abfragelogik zusammen mit ordentlichen Reports, auch grafisch, leicht und verständlich, managementtauglich, aber
                      auch CSV-Exporte … ein vernünftig bedienbares Auswerte-Tool“. Ansicht 'fragen' heißt jetzt „📊 Auswertung“ (VIEWS, Knopf in #meTop,
                      Rechtsklick „🔎 Steckbrief und Auswirkungen von …“ = ask:about → Bericht 'element'). Der Block „Modell fragen“ wurde ganz ersetzt
                      (Kommentar „Auswertung („Modell fragen“, Fassung 13 – Auswerte-Werkzeug …“; Teile in Reihenfolge: Arten/ASK_F/Graph/Bedingungen/
                      Reichweite/Kette – Parser/askToQ/askSentence – Grafiken/Ausgabe – Berichte – askExec – Oberfläche). Art 'alle' = „Elemente“, „Objekte/
                      Einträge/Elemente insgesamt“ wird erkannt. JEDE Antwort ist ein Bericht R {title, sub (Modell · Organisation · Bild · Stand), kpis
                      [{l, v, s, tone ok|warn|bad}], summary [Sätze], blocks [{h, text, chart {svg, W, H}, table {head, rows, w}, html, half, sort}], ids,
                      notes, key, sentence}. Zellen: Text/Zahl, RC(e) = Element-Chip (Klick wählt, anderes Bild → bild:show), {tag, cls}, {bar, max, t},
                      {list, more}, Matrix-Punkte. Grafiken als eigenes SVG ohne Bibliothek: chBars (Wertspalte passt sich der Beschriftung an), chDonut,
                      chStack, chCols, chGantt. Berichtskatalog REPORTS (17, links in .awside nach Gruppen): ueberblick, qualitaet (Pflegegrad je Unterebene),
                      mensch, faehigkeiten (Reifegrad Ist/Soll, Lücken), ziele, produkte, prozesse, prozess_it (Matrix Prozess × IT-System, direkt/indirekt),
                      it_landschaft, it_risiken (Befunde: abgekündigt/Auslauf/Non-Standard/ohne Verantwortung, betroffene Prozesse), verantwortung, daten
                      (pb, Schutzbedarf, Master), organisation, standorte, projekte (chGantt + Meilensteine), veraenderung (Ist → Soll, Roadmap), element
                      (Steckbrief, Auswahlfeld). Schlüsselwörter REP_KW (z. B. „Bericht IT-Risiken“, „Übersicht über die Prozesse“), REP_BY_TYPE; REP_NEED
                      = Leerzustand („noch nichts erfasst … So füllen Sie den Bericht“) statt leerer Grafiken. repRun(id) rechnet im gewählten Bild
                      (askState.rbild, Auswahl „Bild“ im Bericht). Fragen: askExec → askExec1 je Modus, zusätzlich askKennzahl (Verknüpfungen, Beziehungen,
                      Bilder …), askSuche (Volltext, auch „in anderen Bildern gefunden“), askCompare. „So verstanden: …“ (askSentence) über dem Bericht,
                      „✎ Abfrage anpassen“ öffnet den Baukasten (wie Fassung 13), st.edited → gemerkter Name = askTitle(Q) statt Fragetext. EINBINDUNG
                      (nach dem Nachtest): loseEl(e) = kein Bezug außer Über-/Unterordnung ('kid'/'parent' zählen nicht), embedded(e) = selbst oder über
                      Unterelemente verknüpft; ASK_F 'lose' („ohne Verknüpfung“, „isoliert“, „nicht verbunden“) nutzt loseEl und schreibt einen Hinweis;
                      Übersicht/Qualität zählen „eingebunden“ (Ring direkt / über Unterelemente / nicht eingebunden). Ausgabe: repPrint (repPrintHtml, A4,
                      Druck-iframe → „Als PDF speichern“), repDocx (Word über wBlocks/wPara/makeZip, Grafiken als PNG per pmPngBlob, Tabellen), repCsv
                      (alle Tabellen bzw. eine; ';' + BOM für Excel), Kopieren (TSV). Spaltenköpfe sortieren (repSorted). Gemerkt: doc.fragen [{id, name,
                      text, rep, el} | {…, Q}] (normalize behält rep/el bzw. askQNorm), links „☆ Gemerkt“. Oberfläche .aw (Seitenleiste 236 px, unter 900 px
                      gestapelt, askScroll), Fragefeld mit Namensvorschlägen, Beispiele aus dem Modell (askExamples), „Meinten Sie“, davon/weiterfragen.
                      Geprüft: 17 Berichte × 3 Bilder und im leeren Modell ohne Fehler/NaN, 27 Testfragen (u. a. Peters Frage → 92 Elemente, 131 Verknüpfungen,
                      22 Beziehungen, 13 von 13 Ebenen), Kennzahl/Rang/Matrix/Lücke/Wirkung/Vergleich/Suche/Steckbrief, Baukasten, Merken + Strg+Z, Druck-
                      vorschau (Screenshots), .docx wohlgeformt (Bilder, Tabellen, Überschriften – in echtem Word NICHT geöffnet), mit der Maus Katalog,
                      Sortieren, Vorschlag einsetzen + Enter; Regression 132 Darstellungen (alle Ansichten, Editoren × Ansicht, Berichte × 3 Bilder),
                      normalize idempotent.
                      ABFRAGESPRACHE NACHGEBESSERT (12./13.10.2026 nachts; Peters Kritik: „Was verstehst Du eigentlich unter einer vernünftigen und einfachen
                      Abfragesprache … ‚Wie viele IT-Systeme gibt es im Ist und wie sind die Beziehungen zu anderen Objekten‘“). Ursache: eine Frage = genau
                      eine Absicht; „Beziehungen“ zählte alle Beziehungen. Jetzt kontrollierte natürliche Sprache mit TEILFRAGEN: askSplit trennt an „;“, „?“
                      und „und/sowie/außerdem/dazu“ vor einem Fragewort; askParse(q, ctx) übernimmt Art/Elemente/Bild des vorigen Teils („… und wie sind sie
                      verbunden?“); Modus 'profil' (Beziehungsprofil „womit verbunden?“, „zu anderen Objekten/Ebenen“ = res.others); askCombine fügt die Teile
                      zu EINEM Bericht (Titel „IT-Systeme: Anzahl und Beziehungen zu anderen Ebenen“). „So verstanden“ je Teil, Teil-Leiste (askAct 'part').
                      Fassung 14 dazu: ASK_T massnahme (re2 „Maßnahmen“ nur mit Datenschutz-/Sicherheitsbezug ASK_DSCTX oder ohne Zielwörter, Hinweis
                      ASK_NOTE_TOM, sonst operative Ziele), verfahren, partner, risiko; ASK_F ds (Drittland, DSFA, Art. 9), avv, rhoch (Restrisiko hoch,
                      über Akzeptanzgrenze), mumg (nicht umgesetzt), besetz (unbesetzte Stellen); ASK_VAL_STOP + mehrere/fehlt/sehr hoch/Schutzziele/
                      Deutschland …; dsGroup: Ordner in Verfahren/Maßnahmen/Risiken zählen nicht als Eintrag (außer bei „alle Elemente“); „von X betroffen“
                      (passiv) ≠ Auswirkung; Superlativ („größter Reifegradabstand“) → Fachbericht der Art; Element, das wie ein Berichtsthema heißt
                      („Risikoanalyse“) → Bericht + Hinweis auf den Steckbrief; ASK_REPW + Komposita (…landkarte, …portfolio, …verzeichnis).
                      DATENSCHUTZ UND INFORMATIONSSICHERHEIT (Fassung 14, Peters Auftrag „Datenschutzaspekte und IT-Sicherheitsaspekte nach ISO 27001 …
                      unter Organisation einen Layer Verfahrensverzeichnis … TOM zuordnen … ISO 27001 Vorschläge, eine IT-Risikoanalyse“; Sicherung
                      editor-backups/*_8em-datenschutz-isms). Unterebenen von 7a (DS_KEYS): verfahren (Art. 30: vnr, zweck, rolle_ds, rechtsgrundlage RG_OPTS,
                      rg_text, art9, betroffene, datenkat, quelle, empfaenger, drittland DRITT_OPTS, loeschfrist, dsfa, dsfakrit = Schwellwertprüfung DSFA_K
                      (9 Kriterien, ab 2 → DSFA in der Regel nötig), vstatus …), partner (PARTNER_ART, land, garantie, avv AVV_OPTS, kritikalitaet, ausstieg),
                      massnahmen (TOM_KAT nach Art. 32, mart, isothema/isoref = NUR Nummern des Anhangs A der ISO/IEC 27001:2022 – ISO_NUMS 93; Titel sind
                      urheberrechtlich geschützt und werden NICHT mitgeliefert, Peter kann sie aus seiner Normausgabe einfügen – mstatus, wirksam, geltung),
                      risiken (gefaehrdung GEF = 47 elementare Gefährdungen BSI G 0.1–0.47, eh/sh vor, eh2/sh2 nach Maßnahmen, RISK_M 4 × 4, behandlung,
                      akzeptiert, rstatus, wiedervorlage). Schutzbedarf sbV/sbI/sbA (SB_OPTS normal/hoch/sehr hoch) + krone (Kronjuwel) + sbGrund bei Prozessen,
                      Daten, Verfahren (SB_PRIMARY); sbCalc vererbt nach dem Maximumprinzip über den askIx-Graph an IT-Systeme (Stufe 1), Plattformen/
                      Architekturpläne (2), Komponenten (3), Standorte (4); eigene Festlegung = Kumulation/Verteilung. Früheres Feld schutzbedarf → sbV.
                      ME-Editoren vvt/partner/tom/risiko (ME_ORGSUB, Portfolio Risikomatrix PF.risiko), HEAT/CHIPS/kindChecks; Dialoge #tomDlg (TOM_VORSCHLAG,
                      42 eigene Vorschläge mit Kategorie und Anhang-A-Nummern) und #soaDlg (Erklärung zur Anwendbarkeit doc.soa {Nummer: {anw, grund, erl,
                      titel}}, doc.isms {akz Akzeptanzgrenze 0|1, scope}). Berichte (REP_GRP 'sec'): vvt (Übersicht + je Tätigkeit ein Blatt nach Art. 30),
                      tom (Umsetzung je Schutzziel, Anhang-A-Abdeckung, Beschreibungen als Anlage), schutz (Einstufung + Vererbung), risiko (Matrix vor/nach
                      Maßnahmen chRiskMatrix, Restrisiken, Behandlungsplan, betroffene Werte), soa; „IT-Risiken und Lebenszyklus“ heißt jetzt „IT-Lebenszyklus
                      und Altlasten“ (Schlüsselwort „Risiko“ führt zur Risikoanalyse). Beispiel: 3 Verfahren, 3 externe Parteien, 14 TOM, 4 Risiken (fiktiv),
                      Daten liegen im Ist auf Dateiserver/PostgreSQL (Vererbung bis zum Serverraum sichtbar).
                      UML-EDITOR (Fassung 14, Peters Auftrag „auf der untersten Ebene einen UML-Editor nach der neuesten UML-Spezifikation“ = UML 2.5.1, OMG
                      formal/17-12-05): e.uml an UML_HOLD-Elementen (Ebene 8: itsysteme, architekturplan, plattformen, komponenten, ictobjekte), Liste von
                      Diagrammen {id, k, name, desc, route, num, nodes [{id, t, x, y, w, h, name, st, abs, at, op, ref, opn, tmax}], edges [{id, k, a, b, label,
                      st, mA, mB, rA, rB, nav, y, route, via}]} (umlNorm). Alle 14 Arten UML_K (Gruppen s/v/i, je Palette pal und Beziehungsarten ed), Symbole UT
                      (sh = Form: cls Classifier mit Abschnitten at/op, pkg, note, comp, node 3D, actor, uc, subj, act, init/final/…, dia, bar, life/alife
                      Lebenslinie, exec, frag mit Operator UML_OPS, iref, sdf, tline Zeitverlauf; refs = Ebenen für den Bezug ins 8EM-Modell), Linien UE
                      (Endsymbole umlMark open/fill/tri/ftri/odia/fdia/cplus als Grundformen, Multiplizitäten/Rollen, Schlüsselwörter «use» «include» …).
                      Darstellung umlSvg (Behälter → Linien → Lebenslinien → Symbole → Nachrichten → Beschriftungen; rechtwinklig mit verteilten Anschlüssen
                      umlPorts/umOrthoPts, gerade mit Umriss-Schnitt umClip, Beschriftung auf dem längsten Abschnitt, lange Texte umbrochen; Sequenz:
                      Nachrichten waagerecht auf e.y, Ausführungsbalken, Löschen = Kreuz, Lebenslinien wachsen mit). Editor #um (z-index 47, um-Zustand,
                      umlOpen/umClose/umRender, Palette mit 8EM-Elementen, Eigenschaften data-uf/data-ud, Doppelklick = Name bzw. Abschnitt bzw. Linie im Bild,
                      Griff = verbinden, Ziehpunkte, Nachricht senkrecht ziehen, Pfeiltasten, Entf, F2, Esc; Textfelder rendern nur Fläche + Kopf neu, damit
                      Tab/Klick ins nächste Feld nicht verloren geht). umlChecks (Regeln je Art), umlGenerate (Klassen aus Datenmodell inkl. daten-Beziehungen,
                      Komponenten aus IT-Landschaft, Verteilung aus Plattformen/Komponenten, Anwendungsfälle aus Funktionen/Rollen), umlAddExample (14 fiktive
                      Lernbeispiele), Dialog #umlDlg (Besitzer, Name, Art, leer/Beispiel/erzeugen), umExport (SVG/PNG/PPTX/ODP/VSDX/PDF über svgToPrims + XP),
                      umToAblage (Ordner der Unterebene). Einbindung: umlSectionHtml in den Eigenschaften, editorTargets und ME-Rechtsklick, Editoren-Menü,
                      Kärtchen-Tag „📐 UML n“, askIx-Kanten 'uml'/'umlrel' (PROF_KIND, askHowText), elDiff/histDesc, umlDetach beim Löschen, Prüfung „UML-
                      Diagramme mit Regelverstößen“. Beispiel: Kundendatenplattform (Soll) Klassen- und Anwendungsfalldiagramm, Collaboration-Software
                      Sequenzdiagramm. Geprüft: alle 14 Arten (Auswahl jedes Symbols/jeder Linie, Exporte ohne NaN), 76 Erzeugungsläufe, mit der Maus
                      Lebenslinien setzen/benennen, Nachricht ziehen/beschriften/verschieben, Attribute im Kasten ergänzen, verbinden, Multiplizitäten per Tab,
                      Dialog über Rechtsklick; Regression 180 Darstellungen × 3 Bilder, leeres Modell, normalize idempotent. PowerPoint selbst nicht geöffnet.
                      ROLLEN/PERSONEN ZENTRAL, FINANZEN, ITIL/COBIT/TOGAF (13.10.2026, VERSION 15, Build 10:31; Sicherung editor-backups/*_8em-rollen-itil-finanzen).
                      Peters Auftrag: „an allen Stellen … die Verantwortlichkeiten und Rollen, die vorhanden sind oder falls noch nicht vorhanden, diese anlegen. Im
                      Modeller sollen nirgends Rollen und Personen vorkommen, die nicht zentral gemanagt sind“ + Beschäftigungsart + alle ITIL-/COBIT-/TOGAF-Vorschläge
                      + Finanzkennzahlen „so dass auch ein Finanzreporting möglich ist“. GRUNDSATZ: keine freien Personen-/Rollen-Texte mehr – immer Element der
                      Ebene 7a (Rolle organisation.rollen, Person mitarbeiter/oeleiter, Einheit, externe Partei). FELDARTEN (LEVELS-fields): 'who' (WHO(k, l, role):
                      Einträge in e.verantw unter Rolle f.role; k = früheres Freitextfeld; Anzeige vaFor/vaWhoText, Eingabe data-who → whoInput/whoAdd: vorhandene
                      Person bzw. Rolle, sonst neu – guessWho(): Anrede bzw. Vor- und Nachname = Person, sonst Rolle; Rolle als Verantwortliche = {rolle, note:f.role}),
                      'ref' (attrs[k] = Kennung, data-ref → refInput, find-or-create in f.make, Verknüpfung folgt), 'eur'/'num' (EUR()/NUM(), eurN() liest „12.000“,
                      „12,5 T€“, „1,2 Mio.“; eurFmt), 'scale0' (0–5, STUFE05), 'bild' (Soll-Bild); f.g = Zwischenüberschrift (fieldsHtml), f.show(e) = Bedingung,
                      f.lbl = Anzeigetexte einer Auswahl; fieldText(e, f) für Tabellen/CSV/Steckbrief/Abfrage. VA_KEYS = alle (Unter-)Ebenen außer ORG_WHO (nach LAYERS
                      gebildet); VA_IT = IT-Bausteine + Services. Das Modell selbst hält „Beteiligte am Modell“ (doc.verantw, Kennung '_doc', vaEl/vaId; früher doc.people).
                      MIGRATION whoMigrate(o, who, top) in normalize (je Bild): who-Felder → Verantwortung, rname/pname → Rolle/Person, einheiten.leitung → OE-Leitung
                      (Mitarbeiter gleichen Namens wird OE-Leitung) bzw. Rolle, rollen.besetzung → Soll-Besetzung bzw. Personen, oeleiter.vertretung (Text) → ref,
                      rollenart „Person“ → Mitarbeiter/in, Ablaufmodell-/Projektorganisations-Symbole und BPMN-Lanes sowie UML-Akteure/Partitionen ohne Bezug →
                      zentrale Elemente („Auftraggeber: X“ → X), doc.people → doc.verantw. Gleiche Namen = gleiche Kennung in allen Bildern (Karte who aus allen Bildern,
                      sonst 'w' + Art + hashId(Name)). Idempotent geprüft. ABLAUFMODELLE: porg-Typen ag/pl/stab (ORG_WHO, reuse, cap = Kopfzeile im Kasten),
                      lk/grem/team (Einheit mit orgart Gremium/Team, init), ext (externe Partei); pmRenameNode/pmWhoKey/pmLaneResolve: anderer Name wählt bzw. legt an,
                      zentral geführte Elemente werden im Diagramm NICHT umbenannt; porgVa übernimmt Auftraggeber/Projektleitung ins Projekt; UML: UM_WHO/umWho.
                      BESCHÄFTIGUNGSART (BESCH intern|extern|Dienstleister, BESCH_L/C/K, isExt): Feld bei Mitarbeiter/OE-Leitung, firma (ref externe Partei) und
                      kostenJahr nur bei extern/Dienstleister; Kennzeichen „ext“/„DL“ im Organigramm (beschTagSvg), Kärtchen, Heatmap organisation.besch, Dialog
                      „Verantwortung“ (#vaBesch/#vaFirma für neue Personen), Prüfungen, Bericht Verantwortung („Verantwortung bei externen Personen“).
                      FINANZEN (Fassung 15 – seit Fassung 16 ERSETZT durch Kosteneinträge, siehe „KOSTEN, KOSTENARTEN, KOSTENSTELLEN“ unten; die
                      Betragsfelder für Kosten gibt es nicht mehr, beim Öffnen wandelt kostMigrate sie um): Felder je Ebene (siehe LEVELS: IT kostenEinmal/kostenLizenz/kostenBetrieb/nutzer, Prozess kostenJahr/faelleJahr/einsparung, Produkt
                      umsatzJahr/dbJahr/kostenJahr, Fähigkeit investBedarf, Ziele zielbudget, Einheit kostenstelle/budgetJahr, Rolle kostensatz, Partner vertragswert/
                      vertragEnde/kuendigung, Projekt budget/istkosten/prognose/nutzenJahr, Maßnahme kostenEinmal/kostenJahr, Risiko schadenEUR/proJahr/proJahr2,
                      Standort/Service kostenJahr). finData() (zwischengespeichert je doc.elements/docVer): IT-Kosten gleichmäßig auf verknüpfte Prozesse, sonst
                      Fähigkeiten; procCost, capCost, roleVz/roleCost/persCost, riskAle, finTotals. Bericht „Finanzen und Kosten“ (repFinanzen) inkl. Entwicklung über
                      die Bilder; Heatmaps Kosten (S_EUR); Abfragen zeigen Beträge als Summen/Spalten (askFin); Prüfung „Beträge, die keine Zahl sind“.
                      ITIL® 4: Unterebene werkzeug.services („09 IT-Services“, H_MAX 2, Rang vor den IT-Systemen für Ketten), ME 'service' (🛎 Service-Katalog,
                      Palette svc:*, HEAT/CHIPS/kindChecks), Felder Service-Level und F_CONT (rto/rpo/notfallplan) auch bei Prozessen, IT-Systemen, Plattformen,
                      F_CHANGE (Änderungs-/Ticketnummer); durH/durFmt; kontIt/kontState; Schutzbedarf-Stufe 0,5; Bericht „Services und Kontinuität“.
                      COBIT® 2019: operativ hierarchisch (H_MAX 3), zielart (ZIELART), cobitRef, stufeIst/stufeSoll (scale0); strategie.bsc; doc.design = Gestaltungsfaktoren
                      (DESIGN, designHtml in Modell-Eigenschaften); RACI_KEYS (+ Verfahren, Maßnahmen, Risiken, Services): Auswahl R/A/C/I in linkBoxHtml (data-raci);
                      Bericht „Governance und Zielkaskade“. TOGAF® 10: neue Ebene 2a Architekturprinzipien (prinzip, Ordner „02a Architekturprinzipien“), Projekt adm
                      (ADM_OPTS) + zielbild (Soll-Bild), doc.adm {Phase: {st, note}}, Leitfaden-Umschalter „ADM-Phasen“ (renderAdmGuide, ADM_DO), Bericht „Architektur:
                      Lücken und Zyklus“ (ARCH_DOM, Ist × Ziel), Berichtsmappen MAPPEN (Leitung, Datenschutz, IT, Fachbereich; repMappe). Abfrage: ASK_T service/prinzip,
                      ASK_F besch/kost/kont/ziel, Grundlage-Wörter „beruht/stützt sich“, Teilfragen auch „… und auf welcher …“. Beispiel (fiktiv) ergänzt.
                      Geprüft im Browser: alle Ansichten, Leitfaden (Ebenen + ADM), Matrix-Vorlagen, Eigenschaften aller Elemente, alle 30 Berichte × 3 Bilder und
                      leeres Modell ohne Fehler/NaN, 516 Editor-Darstellungen (16 Editoren × Ansicht × Heatmap bzw. Kärtchen × Bilder), Altmodell (Fassung 14 mit
                      Freitexten in allen Feldern, BPMN-Lanes, Projektorganisation, UML, zwei Bilder) → zentrale Elemente mit gleicher Kennung, normalize idempotent;
                      mit der Maus: Person/Rolle eintippen (Prozessverantwortung), Rückgängig, Beschäftigungsart + neue externe Partei, RACI bei einer Maßnahme; Fragen
                      („Was kosten die IT-Systeme?“, „Wie viele Services gibt es und auf welcher IT beruhen sie?“, „Mappe für die Geschäftsleitung“ …).
                      Hinweis: Peters eigene Inhalte (Datenschutzerklärung, Blog-Entwurf) nennen weiterhin „8EM-Editor“ – nicht geändert.
                      KOSTEN, KOSTENARTEN, KOSTENSTELLEN (13.10.2026, VERSION 16, Build 11:24; Sicherung editor-backups/*_8em-kostenstellen). Peters Klarstellung
                      zu Fassung 15: „wo passend, sollen Kosten erfassbar und ggf. Kostenstellen, die entweder angeboten oder neu angelegt werden, zuordenbar sein.
                      Dafür sollen dann Kategorien vorgeschlagen werden, wie Personalkosten, Lizenzkosten, … aber ggf. auch eigene Kostenarten eingegeben werden
                      können und später zugeordnet werden kann … unter Organisation ein Layer Kostenstellenmanagement“. DATEN: e.kosten = [{id, art (Kostenart),
                      kst (Kostenstelle = Element organisation.kostenstellen), betrag (Text, eurN), rh KA_RH jährlich|monatlich|einmalig, st KO_ST Ist|Plan|Prognose,
                      note}] bei KOST_KEYS (alle Unterebenen außer Vision, Mission, Prinzipien, Verfahren, Risiken, Kataloge, Architekturpläne, Kostenstellen, Daten);
                      doc.kostenarten = Katalog [{id, name, gruppe, nr, rh, note}] (Vorschläge KA_VORSCHLAG 'ka_…' werden bei Verwendung übernommen – kaEnsure;
                      Gruppen KA_GRUPPEN/KA_GC; eigene Kostenart = eingetippter Name, Gruppe '' = „Gruppe noch offen“, später zuordnen oder per kaMerge mit einer
                      anderen zusammenführen – wirkt über kaEachEntry in ALLEN Bildern). Unterebene organisation.kostenstellen (KST_KEY, Gruppe „Kosten-
                      stellenmanagement“, Ordner aus LAYERS, H_MAX 3): kstNr, kstArt KST_ART (Bereich/Haupt-/Hilfs-/Projektkostenstelle), WHO w_kst
                      „Kostenstellenverantwortliche/r“, budgetJahr, gueltigAb/Bis; Einheiten per Verknüpfung. Die EUR-Kostenfelder der Fassung 15 sind entfernt
                      (Erlöse, Nutzen, Einsparung, Durchläufe, Nutzende, Vertragsende/Kündigung, Risikobewertung bleiben Felder); normalize → kostMigrate(o, who)
                      mit KO_OLD (kostenEinmal → Anschaffung, Lizenz/Betrieb → Lizenz/Wartung, kostenJahr je Ebene Miete/externe Mitarbeit/Hosting/Sachkosten,
                      vertragswert → Dienstleistung, investBedarf/zielbudget → Investition Plan, Projekt budget/istkosten/prognose → Projektkosten Plan/Ist/Prognose,
                      Rollen-kostensatz → Personalkosten Plan mit Hinweis „Betrag prüfen“; Eintrags-id 'ko' + hashId) und Einheit kostenstelle/budgetJahr →
                      Kostenstellen-Element (gleiche Kennung über die who-Karte in allen Bildern), verknüpft. Element ↔ Kostenstelle wird verknüpft (kostLinkSync
                      in ictSync, kostDrop in removeLink, koUnlink). RECHNEN: koAmt (monatlich × 12), koEff = je Element und Kostenart nur Ist, sonst Prognose, sonst
                      Plan (keine Doppelzählung), costYear/costOnce/costSt/hasKosten/allKosten, kstCost(x, deep), kstPayers; finData/procCost/capCost/persCost
                      (Einheit samt Untereinheiten, Stellen, Personen) aus den Einträgen; koPairs = Plan-Vergleich NUR wo dasselbe Element für dieselbe Kostenart
                      Plan UND Prognose bzw. Ist hat. BEDIENUNG: Abschnitt „💶 Kosten“ in den Eigenschaften (kostenSectionHtml/kostRowHtml, data-ko → koSet;
                      Kostenart/Kostenstelle per datalist #kaList/#kstList wählen oder eintippen → kaResolve/kstResolve legen an; Schnellknöpfe KA_DEF je Ebene,
                      koDefKst/koDefSt Vorschlag), kstSectionHtml bei Kostenstellen. Dialog #kstDlg „💶 Kostenstellenmanagement“ (kstDlgOpen/Render, koAct; Reiter
                      Kosten mit Filtern ui.kstF + „Kosten erfassen für …“, Kostenstellen data-kse → kseSet, Kostenarten data-kae → kaeSet + data-kamerge, Vorschläge
                      übernehmen; koCsv). Fokusführung koKeep: Tab/Umschalt+Tab nach dem Neuzeichnen ins nächste Feld, Listenauswahl bleibt im Feld; × der Zeilen
                      mit tabindex -1. Editor ME.kst (💶, ME_ORGSUB kostenstellen, Palette kst:…, KST_C, HEAT.kst Art/Kosten/Budget/Auslastung/…, CHIPS.kst
                      einheit/verursacher/org, CHIP_DEF einheit, kindChecks('kst')). Kärtchen „Elemente mit Kosten“ = kstPayers mit Betrag auf dieser Kostenstelle;
                      zuordnen (Palette meAssign, Rechtsklick asgAct/asgOn) = leeren Kosteneintrag auf der Kostenstelle anlegen (kstPay), umhängen = Kosten auf die
                      andere Kostenstelle (chipPay, Strg = zusätzlicher Eintrag), lösen = Einträge ohne Kostenstelle (bleiben stehen). Alle übrigen Editoren
                      (außer kst/vvt/risiko/itkat/daten) haben Kärtchen „Kostenstellen“. Heatmaps H_KOST/kstz („Kostenstelle zugeordnet“). AUSWERTUNG: Bericht
                      „Kosten und Finanzen“ (repFinanzen: Kennzahlen inkl. „Abweichung vom Plan“, Kostenartengruppen, Kostenarten, Kostenstelle × Gruppe, Budget/
                      Auslastung, Plan/Prognose/Ist je Kostenart, größte Verursacher, Kosten je Element, Prozess-/Fähigkeitskosten, Einheiten, externe Mitarbeit,
                      Verträge, Produkte, Projekte, Risiken, Entwicklung über die Bilder finTotals, „Noch zuzuordnen“) und NEU „Kostenstellen“ (repKostenstellen,
                      p:4, REP_NEED, REP_BY_TYPE kostenstelle; Budget gegen Kosten, Matrix, je Kostenstelle alle Einträge, Kostenartenkatalog); Steckbrief zeigt
                      Kosten. Abfrage: ASK_T kostenstelle; ASK_F-Kostenmerkmale (ohne Kostenstelle, über dem Budget, je vorgeschlagener Kostenart, z. B.
                      „Lizenzkosten“) AM ENDE von ASK_F angehängt (Nummern 'af:i' gemerkter Abfragen bleiben gleich – neue Merkmale immer anhängen); Kostenfragen
                      werden nie zum Bericht; ASK_REPW erkennt „…bericht“-Komposita; Auswahlwerte wie „Finanzen“ sind bei Berichtswunsch keine Bedingung;
                      askFin aus Einträgen (Kostenstellen „direkt gebucht“, damit Summen nicht doppelt zählen). Prüfung „Projekte über Plan“, „Beträge, die keine
                      Zahl sind“ auch für Einträge. CSV: Kosten pro Jahr/einmalig + Kosteneinträge. Hilfe-Abschnitt „💶 Kosten, Kostenarten und Kostenstellen“.
                      repPrintHtml mit weißem Hintergrund (color-scheme light). Beispiel (fiktiv): Kostenstellen 100 Geschäftsführung, 200 Coaching und Beratung
                      (absichtlich über Budget), 900 Verwaltung und IT mit 910/920, 500 Pilotprojekt; 38 Einträge, eigene Kostenart „Supervision und kollegiale
                      Beratung“ ohne Gruppe, ein Eintrag ohne Kostenstelle. Neue Kostenstellen entstehen im gezeigten Bild (in spätere Bilder wie andere Elemente
                      übernehmen). Geprüft im Browser: 31 Berichte × 3 Bilder + Steckbriefe + leeres Modell ohne Fehler/NaN, 1014 Editor-Darstellungen (alle
                      Editoren × Ansicht × Heatmap × 3 Bilder), Übersichtsansichten, normalize idempotent, Fassung-15-Datei mit allen alten Feldern umgewandelt,
                      CSV, Export des Kostenstellen-Editors ohne NaN; mit der Maus: Kosten erfassen, eigene Kostenart und neue Kostenstelle eintippen, Tab-Folge,
                      Dialog (Filter, Zusammenführen, Anlegen, Strg+Z), Kärtchen umhängen/lösen im Kostenstellen-Editor; 12 Kostenfragen.
                      EAM-WERKZEUGE (13.10.2026 abends, VERSION 17, Build 23:10; Sicherung editor-backups/*_8em-eam-werkzeuge). Peters Auftrag: „Mach alle von Dir
                      vorgeschlagenen Tools“ (14 Vorschläge: 1 IT-Erhebungsbogen, 2 Massenimport, 3 ArchiMate-Austausch, 4 Anwendungsportfolio, 5 Vorhaben-
                      Priorisierung, 6 Cloud-/Migrationsbewertung, 7 Technologie-Radar, 8 Architekturentscheidungen/Ausnahmen, 9 Architektur-Review, 10 EAM-
                      Reifegradcheck, 11 Schnittstellenkatalog + Datenflusslandkarte, 12 Wertströme, 13 Glossar, 14 Stakeholder-Landkarte → eigenes Kern-Werkzeug
                      stakeholder-editor.html, 1 und 10 → eigene Fragebögen, s. unten). CODE: fast alles in EINEM Block zwischen den Kommentaren
                      „==== FASSUNG17-BEGINN ====“ und „==== FASSUNG17-ENDE ====“ vor „---------- Start ----------“ (erstmals per Bauskript im Scratchpad
                      eingefügt, künftige Änderungen direkt in der Datei). Haken im übrigen Code: VERSION, Konstanten G_PF/G_MIG/PF_ENT/MIG_OPT/MIG_ZIEL/AUFW3/
                      PRIO_ENT/BEGRIFF_ST/WS_ART vor LEVELS, normalize (e.bew nur IT-Systeme über bewNorm, e.prio nur Projekte über prioNorm, r.sx nur Beziehung
                      itsystem/schnittstelle über sxNorm, Dokument über x17NormDoc bzw. X17_DOC löschen bei _bild), act 'x:…' → x17Act, Eigenschaften
                      x17PropsHtml (beide Stellen), checks → x17Checks, relPropsHtml → sxRelHtml, palTools/palIcon/Ansicht 'wskarte', exampleDoc → x17Example
                      (vor bilderAusPlaenen), restoreFrom → x17Dlgs (Dialoge nach Rückgängig neu), Menüs. undoBatch(fn) fasst verschachtelte Rückgängig-Schritte
                      zu EINEM zusammen (Import, Übernahme aus Fragebögen). Dialoge (in x17Init erzeugt, ids <x>Dlg/<x>Tabs/<x>Body/<x>Msg/<x>Foot): bewDlg,
                      prioDlg, ifDlg, govDlg, glDlg, impDlg; CSS #x17css (Tabellen table.x17t, Zahlenzellen td.xr – NICHT .num, das ist global ein Navy-Kreis).
                      MENÜS: „EAM ▾“ (#eamMenu: Anwendungsportfolio, Cloud/Migration, Schnittstellenkatalog, Technologie-Radar, Vorhaben-Priorisierung,
                      Architektur-Governance, Wertströme, Glossar, IT-Erhebungsbogen, EAM-Reifegradcheck, Mappe), „Import ▾“ (#importMenu: Excel/CSV,
                      ArchiMate, Erhebungsbogen, Reifegrad), Export + ArchiMate und Importvorlage; Editoren-Menü + 🌊 Wertströme, 📖 Glossar; Hilfe-Abschnitt
                      „🧰 Werkzeuge des Architekturmanagements“.
                      ANWENDUNGSPORTFOLIO (4): e.bew = {Kriterium: 0–5} an IT-Systemen, Kriterien BEW_DEF f (fachlich: abdeckung, prozesse, ziele, nutzende, mensch)
                      und t (technisch: zustand, betrieb, sicherheit, wartung, integration, kosten), Gewichte doc.bewKrit (änderbar); bewScore/bewVorschlag →
                      Vorschlag investieren/tolerieren/migrieren/ablösen (PF_ENT, nur Vorschlag – Entscheidung im Feld portfolio mit Datum portfolioBis und
                      Begründung, Gruppe G_PF). pfSvg = Portfolio-Bild (Blasengröße Kosten, Beschriftungen weichen den Blasen aus). Dialog Reiter Bewerten /
                      Portfolio / Migration / Kriterien. CLOUD/MIGRATION (6): Felder migOption (MIG_OPT: beibehalten, Rehost, Replatform, Refactor, Repurchase,
                      Retire – deutsch beschriftet), migZiel (MIG_ZIEL inkl. „Public Cloud – Anbieter mit Sitz außerhalb der EU“, SaaS), migTermin, migAufwand,
                      migNote; migHints leitet Hinweise aus dem Modell ab (personenbezogene Daten/Schutzbedarf bei externem Ziel → Drittland/AVV prüfen,
                      Schnittstellen, Kontinuität) – nur Hinweise, keine Rechtsbewertung.
                      SCHNITTSTELLEN (11): eine Schnittstelle = Beziehung k 'itsystem', t 'schnittstelle' mit r.sx {nr 'S-001', tech SX_TECH, freq SX_FREQ, krit,
                      st SX_ST, daten [Datenobjekt-ids], richtung, note …}; ifList/ifAdd/sxSet, Dialog Reiter Liste/Matrix/Datenflusslandkarte (ifMapSvg),
                      Medienbruch = Technik „manuell“ (sxManual), personenbezogene Daten aus den verknüpften Datenobjekten (sxDaten/SX_PERS).
                      TECHNOLOGIE-RADAR (7): nur Bericht 'radar' (radSvg) aus Komponenten/Plattformen; Ring aus attrs.standard (radStd/radRing: einsetzen,
                      einführen, erproben, zurückhalten), Sektor aus der Komponentenart (radSec); kein eigener Datenbestand.
                      VORHABEN-PRIORISIERUNG (5): e.prio = {Kriterium: 0–5} an Projekten, Kriterien PRIO_DEF (Strategie, Nutzen, Dringlichkeit, Mensch, Architektur,
                      Risiko und Aufwand umgekehrt gewertet), Gewichte doc.prioKrit, Budget doc.prioBudget (Text, eurN); prioRanked (Kosten aus den Projekt-
                      Kosteneinträgen), Rangliste mit Budgetlinie (prioRankSvg), Wert-Belastungs-Bild (prioBubbleSvg), Entscheidung prioEnt (PRIO_ENT) + prioNote.
                      GOVERNANCE (8, 9): doc.gov = [{id, t 'adr'|'aus'|'rev', nr, titel, st, datum, refs [Element-ids], … je Art nach GOV_T}] – ADR (Kontext,
                      Entscheidung, Alternativen, Folgen, Wiedervorlage), Ausnahme (Abweichung von welchem Standard, Begründung, befristet bis, Ausgleich),
                      Review (Prüfpunkte REV_STD mit Urteil REV_V, Ergebnis, Auflagen); Verantwortliche über whoRef (zentrale Rollen/Personen). Prüfungen:
                      abgelaufene Ausnahmen (ausAbgelaufen), bald ablaufend, fällige ADR-Wiedervorlagen. Textfelder zeichnen beim Tippen NICHT neu (Fokus).
                      WERTSTRÖME (12): neue Ebene 'wertstrom' (n 5b, Ordner aus LEVELS, H_MAX 2): Wertstrom (oben: Auslöser, Stakeholder, Wert, Priorität,
                      WHO w_ws) → Stufen (e.parent: Eingang, Ausgang, Wert der Stufe, Erleben gut/mittel/schlecht, Schmerzpunkte); ME.wertstrom (🌊, Ansichten
                      Modell und Wertstromkarte wsKarteDraw = Stufen als Chevrons, darunter verknüpfte Fähigkeiten mit Reifegrad-Lücke wsGap, Prozesse, IT),
                      Palette ws:strom/ws:stufe/ws:soll (Stufe in einen Wertstrom ziehen), HEAT/CHIPS/REL_T.wertstrom. GLOSSAR (13): neue Ebene 'glossar' (n 7d,
                      nach Daten): Definition, Synonyme, Abgrenzung, Beispiel, Kürzel, englisch, Quelle, begriffstatus (BEGRIFF_ST), WHO w_begriff; Dialog
                      glDlg (Suche, A–Z), glConflicts = gleiche Bezeichnung als Begriff/Synonym mehrfach bzw. gleichnamige Elemente anderer Ebenen.
                      KOST_KEYS ohne wertstrom/glossar. Ältere Modelle brauchen keine Umwandlung (rein additiv).
                      MASSENIMPORT (2): impDlg – Excel (.xlsx über xlsxRows) oder CSV, Zielebene wählen, Spalten automatisch zuordnen (IMP_SYN, impAuto),
                      Vorschau mit neu/aktualisiert (gleicher Name = Aktualisierung, kein Doppel), Spalte „Verantwortlich“ → va:role/va:pers (vaAdd, zentral);
                      ein Rückgängig-Schritt (undoBatch); Importvorlage (impTemplate). ARCHIMATE (3): amExport = Open Group Exchange Format 3.x (AM_NS, Elemente
                      nach amType, Beziehungen nach AM_RULES, Schnittstellen-Merkmale als properties), amImport (AM_IN; gleichnamige Artifacts werden an die
                      ApplicationComponent zurückgekoppelt – keine doppelten Gegenstücke; zweiter Import ohne Doppel). Kostenstellen werden bewusst nicht
                      exportiert. Archi selbst NICHT geöffnet (nur XML wohlgeformt + Rundlauf geprüft). Markenhinweis ArchiMate®/The Open Group in der Hilfe.
                      FRAGEBÖGEN (1, 10): X17_FORMS {erh, rf}, x17OpenForm öffnet per window.open mit #mc4y8em=<Kennung>; eigener message-Listener: Bogen
                      meldet 'mc4y-8em-bereit' → Modeller antwortet 'mc4y-8em-vorlage' (Erhebungsbogen: vorhandene IT-Systeme, Kriterien BEW_DEF, Organisation),
                      Ergebnis 'mc4y-8em-fragebogen' → erhImport (IT-Systeme anlegen bzw. ergänzen, Verantwortliche zentral, Schnittstellen mit sx, Bewertung
                      e.bew) bzw. rfImport (doc.eamReife {datum, org, bereiche {k: {heute, gewuenscht}}}); auch per Datei (Menü Import). Bericht „EAM-Reifegrad“
                      stellt die Selbsteinschätzung neben Kennzeichen aus dem Modell (eamIndicators).
                      BERICHTE (Gruppe 'eam', X17_REPORTS): portfolio, migration, schnittstellen, radar, vorhaben, gov_ent, wertstroeme, glossar, eam_reife + Mappe
                      m_eam „Mappe Architekturmanagement“; REP_NEED/REP_BY_TYPE, ASK_T wertstrom/begriff. Beispiel (fiktiv) ergänzt: Buchhaltungssoftware,
                      Kundenliste (entfällt 2027), Bewertungen, Schnittstellen S-001–S-003, zwei Wertströme, fünf Begriffe (absichtlicher Konflikt „Klient“), zwei
                      priorisierte Projekte mit Budget, je ein ADR/Ausnahme/Review, Reifegrad-Beispiel.
                      SYNC-HINWEISE: EAM_DIM (10 Bereiche) ist identisch in fragebogen-eam-reifegrad.html; BEW_DEF ↔ KRIT_DEF in fragebogen-it-erhebung.html
                      (Rückfall, wenn der Bogen ohne Modeller geöffnet wird) – bei Änderungen beide nachziehen.
                      Geprüft im Browser: 840 Prüfungen (alle Ansichten, alle Berichte, alle Dialog-Reiter, Eigenschaften von bis zu 400 Elementen × 3 Bilder +
                      leeres Modell) ohne Fehler/NaN, 1062 Editor-Darstellungen (ME_KINDS × Ansicht × Heatmap × 3 Bilder) inkl. Export-SVG, normalize idempotent,
                      Peters MC4Y-Modell (Fassung 16, 238 Elemente) lädt; ArchiMate 185 Elemente/440 Beziehungen, Rundlauf; Erhebungsbogen und Reifegrad-Übergabe
                      echt über iframe mit window.opener; mit der Maus: EAM-Menü, Schnittstelle anlegen (Auswahl + Tastatur, Fokus bleibt), Verantwortliche
                      eintippen → Person angelegt, Wertstromstufe aus der Palette in einen Wertstrom gezogen + benannt + Strg+Z/Strg+Y. PowerPoint/Archi nicht geöffnet.
                      STAMMDATEN FÜHREND (15.10.2026, Peters Auftrag „in allen Systemen“ – ausdrücklich auch der Modeller; Build 14:40; siehe stammdaten-editor.html):
                      Block „==== STAMMDATEN-ANSCHLUSS …“ vor „---------- Start ----------“: Adapter für Mitarbeitende + OE-Leitungen (SD_PERS; name, kuerzel = attrs.
                      kuerzel, typ aus isExt) und Rollen (name, beschreibung = desc) NUR des IST-Bildes (sdIstDaten = bildData('ist')); Rollenzuordnung = normale
                      Verknüpfung Person ↔ Rolle (Änderungen per withBild('ist', addLink/removeLink)). Namen/Kürzel aus den Stammdaten gelten in ALLEN Bildern
                      (gleiche id). Elemente nur in Soll-Bildern gehen NICHT in die Stammdaten (Planung). sdAnwenden8, sdZusammen8 (in allen Bildern: „von“ fällt weg
                      bzw. bekommt die id von „nach“, dann JSON-Ersetzung + normalize – Verknüpfungen, Beziehungen, Verantwortung, Diagrammbezüge wandern mit),
                      sdGesperrt8, sdSperrFelder (Name/Kürzel bzw. Name/Beschreibung readonly in renderProps UND meRenderPropsEl), lvEditStart/meEditStart lehnen
                      Umbenennen gesperrter Elemente ab, Hinweis + Knopf „in den Stammdaten öffnen“ (act 'sdZeigen8'). doc.stammdaten bleibt bei restoreFrom erhalten;
                      markDirty/refresh melden lokalGeaendert. Verknüpfen/Lösen = markDirty() (Nutzeränderung, muss gespeichert werden), Abgleiche = markDirty(true).
                      bestandName = doc.title || doc.org. Organisationseinheiten, OE-Leitung der Einheit, Beschäftigungsart usw. bleiben Sache des Modellers.
                      Hilfe-Abschnitt „👥 Verknüpfung mit den Stammdaten“.
                      PROJEKTMANAGEMENT-ANSCHLUSS (15.10.2026 abends, VERSION 18, Build 21:41; Sicherung editor-backups/*_8em-projektmanagement). Peters Auftrag:
                      „ergänze die sinnvollen Erweiterungen im Projektmanagement-Tool auch im 8EM-Modeller, wie bspw. Übernahme Meilensteinplanung“. Code im Block
                      „==== PROJEKTMANAGEMENT-ANSCHLUSS (Fassung 18 …“ bis „…-ENDE ====“ (vor dem Stammdaten-Block), Funktionen prj*. Haken außerhalb: normalize
                      (x.pm = prjNorm(e.pm) NUR bei level projekte), Kärtchen-Tag prjTag, checks → prjChecks, Eigenschaften prjPropsHtml (+ prioPropsHtml), Bericht
                      „Projekte“ (Kennzahl verknüpft/Ampeln, Gantt in Ampelfarbe mit Prognose-Ende, Tabelle Ampeln/Fortschritt/Ziel/Prognose, Meilensteine mit
                      Plan/Prognose/Abweichung, bis 3 × prjMtaSvg = Meilensteintrendanalyse), Steckbrief-Zeile „Projektmanagement“ (prjKurz), msKind wertet
                      „verfehlt“ als verspätet, act 'prj:…' → prjAct, Menü Import › „Alle verknüpften Projekte abgleichen“ (prjAlleAbgleichen, ein Rückgängig-
                      Schritt über undoBatch), ASK_F 'pjst' (rote/gelbe Ampel, verzögert, mit dem Projektmanagement verknüpft) am ENDE von ASK_F angehängt.
                      GEMEINSAMER SPEICHER mit projektmanagement-editor.html: IndexedDB 'mc4y-uebergabe' Store 'kv' (wie Buchhaltung 'r:'/'s:'), Schlüssel
                      'ps:<Kennung>' = Projektstand (format 'mc4y-projektstand', vom PM nach jeder Änderung geschrieben; auch in *.projekt.json unter
                      „projektstand“) und 'pa:<uid>' = Projektauftrag (format 'mc4y-projektauftrag', status neu|uebernommen|verworfen); BroadcastChannel
                      'mc4y-uebergabe' (projektstand, auftrag, auftrag-uebernommen, auftrag-verworfen) + Fenster-Fokus laden prjCache/prjPa neu.
                      DATEN e.pm (prjNorm): kennung (= doc.kennung des PM-Projekts), name, nr, datei, modell, status, am (Stand), uebernommen, uebergeben, start,
                      ziel, soll, prog, fortschritt, verzug (Arbeitstage), spi, cpi, ampel {gesamt, termin, kosten, qualitaet, risiken: g|y|r}, warum, kosten
                      {budget, bac, ac, eac} und erloes in CENT, ms [{id, name, plan, prog, ist, st offen|erreicht|verfehlt, gate, ph, ziel, wer, mta [{am, d}]}],
                      ph (Phasen), risiken (Top 12), team [{name, art, rollen, anteil, sd}], zeilen (früher übernommene Tabellenzeilen), hinweise. e.pm steht in
                      ALLEN Bildern mit dem Projekt (prjAllen); elDiff kennt es nicht (keine Veränderung zwischen Bildern). Änderungen am MODELL nur im gezeigten
                      Bild. ÜBERNEHMEN (Dialog #prjDlg, prjDlgOpen/Render: 1 Projekt im Modell oder neu, 2 Projektstand aus dem Browser bzw. „📂 Projektdatei
                      wählen“ – Handle in kvSet('pjf:'+Kennung) für späteres Abgleichen –, 3 Vorschau, 4 Optionen PRJ_OPT0, gemerkt in ui.prjOpt): ms =
                      Meilensteintabelle des Projektauftrags (prjMsTabelle; fehlt der Projektauftrag, wird er aus TX.projekte angelegt; eigene Zeilen bleiben,
                      Statuswort prjMsWort erreicht|verfehlt|verschoben|offen → Zeitleiste msSvg), termine (beginn/ende/projektstatus über PRJ_ST8), kosten
                      (prjKosten: Einträge 'pmk'+hashId(Kennung|Art|Stand), rh einmalig, Plan + Prognose bzw. nach Abschluss Plan + Ist, Kostenarten PRJ_KA →
                      KA_VORSCHLAG, Kostenstelle wählbar oder „＋ neue Projektkostenstelle“; kostErs ersetzt von Hand erfasste Einträge nur im Dialog –
                      prjOptVon setzt beim schnellen Abgleich kostErs:false), wer (Auftraggeber/in, Projektleitung nur wenn leer; prjPerson über Stammdaten-
                      Verknüpfung oder gleichen Namen), team (Verknüpfungen, neuPers legt fehlende Mitarbeiter/innen an). prjAnwenden = ein Rückgängig-Schritt.
                      „↻ Abgleichen“ (prjSync: Browser-Stand, sonst gemerkte Datei, sonst Dialog; nur wenn neuer), „Verknüpfung lösen“ (prjLoesen: e.pm weg,
                      übernommene Angaben bleiben). ÜBERGEBEN („📤 An das Projektmanagement …“, prjAuftragObj): Name, Ziele/Auftrag/Qualitätsziele aus dem
                      Projektauftrag-Text, Kunde = doc.org, Auftraggeber/in, Projektleitung, Zeitraum, Budget = Plankosten (pjSt), Meilensteine aus msParse,
                      Beteiligte (Verantwortung, verknüpfte Personen, Projektorganisation; sd-Kennung bei Stammdaten-Verknüpfung), verknüpfte Ziele → 'pa:' bzw.
                      Datei *.projektauftrag.json; e.pm bekommt kennung + uebergeben, Kärtchen „📤 PM“. Rechnungen bleiben bewusst im PM/in der Buchhaltung.
                      Prüfungen prjChecks: rote Ampel, Meilensteine verschoben/verfehlt, Stand älter als 30 Tage, neuerer Stand liegt bereit. Hilfe-Abschnitt
                      „📋 Projekte und Projektmanagement-Werkzeug“. GEPRÜFT im Vorschau-Browser (zwei Tabs, gleiche Herkunft): Pilotprojekt mit Beispiel-Stand
                      übernommen (Dialog mit der Maus, auch bei 414 px Breite; ein Rückgängig-Schritt, Meilensteintabelle, Termine, 14 Kosteneinträge auf neuer
                      Projektkostenstelle), Anzeige rechts/Kärtchen/Bericht „Projekte“ mit Trend (per Edge-Aufnahme angesehen), Meilenstein im PM verschoben →
                      „neuerer Stand“ → Abgleich, Rückgängig/Wiederholen, Prüfhinweise, Fragen (rote Ampel, verzögert), Gegenrichtung übergeben → im PM angelegt
                      (gleiche Kennung) → Stand zurück, Übergabe mit gefülltem Projektauftrag und Beteiligten; 156 Darstellungen über drei Bilder + leeres Modell
                      ohne Fehler/NaN, normalize idempotent. NICHT geprüft: echte Nutzung über file:// in Chrome/Edge (gemeinsame IndexedDB beider Dateien im
                      selben Ordner) und Abgleich über die gemerkte Projektdatei (Datei-Handle) – beim ersten Einsatz von Peter prüfen lassen.
                      FASSUNG 19 (20.10.2026, VERSION 19, Build 16:32; Sicherung editor-backups/2026-10-20_15-29-57_8em-fassung19; Planungsnotiz 8em-fassung19-plan.md im
                      Notizordner der Sitzungsgruppe). Auftrag über die Sitzung „8EM-Modell Tool“ (Peters Wortlaut): Interfaces senden oder empfangen (beide Richtungen = zwei
                      Interfaces), Daten der Interfaces aus dem Datenmodell bzw. neu, Datenobjekte und Funktionen den IT-Systemen zuordnen bzw. dort anlegen, Kontextdiagramm bis
                      fünf Stufen mit ein-/ausblendbaren Status, Interface-Status, Lebenszyklus mit Datum je Phase, Standardisierung Eigenentwicklung/Kauflösung, Betriebsort, fachlich/
                      IT-verantwortliche Abteilung, Plattformlayer in TAP und Plattform, CMDB-Elemente in den Rasterzellen, ICT-Objekt-Vorschlag beim Anlegen, Übertragung in die CMDB;
                      dazu Peters Wunsch „eine vernünftige Eingabemaske … optional“. Peters Entscheidungen (AskUserQuestion): Feld „standard“ bleibt, attrs.stdArt (STD_ART) als zweites
                      Feld überall in Ebene 8 inkl. Kataloge, Technologie-Radar mit beiden Sichten; Lebenszyklus automatisch aus den Daten; Betriebsort zusätzlich zu „Betrieb“; CMDB
                      automatisch, die CMDB bekommt die Phasen als Status.
                      CODE: Block „==== FASSUNG19-BEGINN ====“ … „FASSUNG19-ENDE ====“ vor dem PROJEKTMANAGEMENT-ANSCHLUSS; Konstanten nach F_STD: STD_ART/F_STDART, BETRIEBSORT/F_ORT,
                      LZ_PH/LZ_BY/F_LZ (Feldart 'lz' → lzHtml/lzText), F_ABT (ref auf Organisationseinheiten); ICT_CAT um Kataloge erweitert, ICT_MEM/isIctM. LEBENSZYKLUS e.lz {idee, projekt,
                      einfuehrung, betrieb, abschaltung, abgeschaltet (ISO-Datum), fest, note} bei LZ_KEYS (ICT_KEYS + ICTO_KEY): lzPhaseOf(lz, D), lzRefDate (Ist = heute, Soll-Bild = letzter
                      Tag seines Zielmonats), lzPhase/lzSt/lzTag/lzText/lzAus; lzNorm übernimmt itstatus/einsatzVon/einsatzBis (LZ_ALT; ergäben die Daten eine andere Phase → fest).
                      Services und Produkte behalten ihr Feld „Lebenszyklus“. Heatmaps H_LZ/H_STDART/H_ORT/H_CMDB, Berichte, Abfragen, Prüfungen, Migration und Altlasten rechnen mit lzPhase.
                      INTERFACES = Beziehung itsystem/schnittstelle (a sendet, b empfängt); SX_ST in Planung|im Betrieb|Abschaltung geplant|abgeschaltet (SX_ST_ALT alte Werte); relAddK
                      legt Kennung und Status an; x19SplitBidi teilt sx.bidi in zwei (id + 'r', Nr + '-R'; normalize, ArchiMate-Import); sxGegen = Gegenrichtung. IT-Erhebungsbogen:
                      „beides“ → zwei Interfaces; seit 20.10.2026 abends Bogen-Fassung 2 (siehe fragebogen-it-erhebung.html): erhPrefill schickt lz (TT.MM.JJJJ), Phase nur bei
                      lz.fest, stdArt, betriebsort(+Sonst), abtFach/abtIT (Namen), daten/datenLesen (nach CRUD getrennt), Interface-Status; erhOne übernimmt sie (Abteilungen
                      find-or-create in organisation.einheiten, Abläufe ohne Treffer → neue Funktion (x19Fkt), Daten ohne Treffer → neues Datenobjekt (x19Daten), auch in
                      Interfaces; CRUD nur ab Fassung 2: verwaltet → CRUD, liest → R, nur wenn leer oder Gruppe wechselt; n-te Zeile gleichen Partners/gleicher Richtung =
                      n-tes vorhandenes Interface je IT-System, weitere neu), lzAusBogen (Daten aus s.lz gelten; Phase = fest, wenn sie von den Daten abweicht, sonst fest
                      entfernt; Fassung 1 wie bisher über LZ_ALT/lzNorm). LZ_BOGEN/lzMonat entfernt, LZ_BOGEN_PH = fold(Phase) → Schlüssel. itIfHtml/itDatenHtml/
                      itFktHtml (data-sx/-sxp/-sxnew/-sxdat, data-itdat/-itdatr mit CRUD bzw. führendem System, data-itfkt; Eintippen = vorhandenes wählen oder neu anlegen).
                      EINGABEMASKE #itm (itmOpen/itmRender/itmSecs, ITM_KEYS; sel = Eintrag → gleiche Feldbehandlung wie rechts; ◀ ▶, Alt+←/→, Esc; refresh() zeichnet sie neu; Eingaben
                      zeichnen das Hintergrundbild erst beim Verlassen des Feldes). KONTEXTDIAGRAMM = ME.itsystem Ansicht 'kontext' (ui.kx {z, t 1–5, st, ph, lbl}, kxGraph, kontextDraw,
                      kxBarTop/kxBarBot, Doppelklick = kxCenter, Pfeil data-kxr → x:if:open; keine allgemeine Legende). PLATTFORMLAYER e.grid.pl {refs, t} (PL_KEY '_pl|_pl', tgCellOf/
                      tgCellClean, tgPlRow; nur Plattformen; Excel mit zusammengeführter Zeile, CSV, Bild, Import). ICT-VORSCHLAG: ictWatch (in ictSync, liefert Anzahl) → #ictPanel
                      (icoAct pick|neu|skip), Importe still (x19Still); Name eines aus einem Element gebildeten ICT-Objekts folgt dem Element (icoVon/icoName, icoNameSync). CMDB (Suite):
                      SUITE19/cmdbR, cmdbSammeln (Ist + nur in Soll-Bildern, ohne Gegenstücke und attrs.cmdbAus 'ja') → MC4YSuite.cmdb.abgleich; x19NachSpeichern nach saveToFolder/
                      saveDoc (neue CMDB-Kennungen → einmal nachspeichern), attrs.cmdb in allen Bildern; LZ_CMDB/CMDB_LZ, cmdbKat; Zuordnen aus der CMDB im Raster (#tgCmdbL), im
                      Zuordnen-Menü (#asgCmdbL) und in der Maske; ohne Suite CMDB-Export einlesen (cmdbKat19, nur Arbeitsspeicher) und Übergabedatei format 'mc4y-cmdb', quelle
                      '8EM-Modeller'. doc.kennung (stabile Modellkennung), doc.cmdbAuto (false = aus). Technologie-Radar ui.radSicht std|art (RAD_RING_ART, radVal; repKey/askKey kennen
                      die Sicht). ASK_F-Erweiterungen hinter den pjst-Einträgen angehängt (Nummern gemerkter Abfragen bleiben). Beispiel x19Example (Daten je Phase passend zu den
                      Soll-Bildern, zweite Plattform im Plattformlayer, Interfaces S-004–S-006, Abteilungen, Daten/Funktionen der Systeme). Hilfe-Abschnitt „🖥 IT-Systeme, Plattformen,
                      Komponenten …“ mit Markenhinweis zu den Cloud-Anbietern. ACHTUNG: Der Sprachblock MC4Y-SPRACHE (Zeile 21) enthält deutsche Texte als Schlüssel – Ersetzungen
                      per Skript nur NACH diesem Block vornehmen.
                      GEPRÜFT im Vorschau-Browser: Beispiel alle Ansichten × 3 Bilder, alle Editoren × Ansicht inkl. Export-SVG, Maske aller Einträge (ohne NaN und Querscrollen), Raster mit
                      Plattformlayer (Menü, Ziehen, Abweisen, Excel-Rundlauf), 41 Berichte × 3 Bilder, Abfragen, normalize idempotent, nachgebaute Fassung-18-Datei (bidi, alte Stufen);
                      mit Maus und Tastatur: Maske öffnen, Interface und Daten eintippen (neues System, neues Datenobjekt), ICT-Vorschlag „Neues ICT-Objekt“, Kontextdiagramm, Radar-
                      Sicht; Wegwerf-Suite: Beispiel über „Modellordner anlegen“ gespeichert → 21 CMDB-Einträge mit Kategorie und Phase, zweiter Lauf unverändert. Peters Modell
                      (unterlagen-privat/MC4Y, Fassung 16, 241 Elemente) in einer Wegwerfkopie geladen: ohne Fehler, idempotent, 43 IT-Bausteine mit früherem Lebenszyklus „von Hand“
                      (Kopie und Browser-Speicher danach gelöscht, die Originaldatei unverändert). Rauchtest (nur Warnung Sprachumschalter bei 375 px), UE-05/06/07, DATEI-itsm/uml grün.
                      NICHT geprüft: Datumsfelder per Tastatur (Vorschau-Browser), Archi, die installierten Suiten (nicht aktualisiert). BEHOBEN 20.10.2026 abends (Build 21:10,
                      Sicherung editor-backups/2026-10-20_*_erhebung-suite-speichern): in der Suite ohne Modellordner (z. B. Beispiel) legten „Speichern“/„Kopie speichern“ keinen
                      Ordner in der Ablage an, sondern öffneten den Dateidialog des Browsers (anschluss.js ersetzt showSaveFilePicker nur bei Ablage 'datei'). Jetzt act 'save'/
                      'saveAs': !root && suiteOrdner() (MC4YSuite.aktiv, server, ablage 'ordner') → createModelFolder(Titel, true) = Ordner-Dialog der Suite; mit Modellordner
                      unverändert (Kopie speichern = Datei auf diesem Rechner). Geprüft in einer Wegwerf-Suite mit der Maus: Beispiel → Speichern → Ablage-Dialog → „Diesen
                      Ordner wählen“ → modell.8em.json + 40 Ordner auf dem Server, kein Browser-Dialog.
                      ABFRAGESPRACHE „INTERFACE“ (21.10.2026, Volltest; Sicherung editor-backups/2026-10-21_*_8em-abfrage-interface): (1) „Welche IT-Systeme haben kein/keine
                      Interface/Schnittstelle?“ ignorierte die Bedingung (zählte alle IT-Systeme) – ASK_F 'if' jetzt /(ohne|kein\w*|fehlend\w*)\s+(interface|schnittstelle)\w*/;
                      (2) ein Element gleichen Namens (Glossarbegriff „Schnittstelle“) verdrängte das Merkmal – askParse Schritt 3 übergeht Namen innerhalb eines ASK_F-Treffers
                      (fSpans); (3) „Welche Interfaces …“ führte zum Bericht „Ist-/Soll-Veränderung“ – REPORTS 'schnittstellen' kw um interface\w* ergänzt. Geprüft: 13 Fragen +
                      14 Beispielfragen ohne Abweichung, TESTPLAN 8EM-19.
                      PLATTFORMEN SCHACHTELN / TAP ↔ IT-SYSTEM (22./23.10.2026, Build 2026-10-23 08:16, Format bleibt 19; Sicherung editor-backups/
                      2026-10-22_22-58-31_8em-plattformen-tap; Peters Wortlaut über „8EM-Modell Tool“: „die angelegten Plattformen [werden] nicht andersfarbig eingeblendet.
                      Da TAP und Plattform die gleiche Logik haben, können Plattformen geschachtelt werden, das heißt eine höhere Plattform blendet die Elemente der unten
                      liegenden Plattform in seiner Matrix ein und die TAP ist quasi das oberste Element und fix mit einem IT-System verbunden, während Plattformen in
                      verschiedenen TAPs vorkommen können“). SCHACHTELN: Block nach tgCellClean – plDirect(e) (Plattformen im Plattformlayer), plTree(e) (Tiefensuche,
                      jede Plattform einmal, {x, via, col, tiefe}, Farbe aus PL_PAL je Plattform), plCanAdd(e, x) (nur Plattformen, nicht sich selbst, kein Kreis – im
                      Zellmenü als „dis“, in tgTogRef/tgMoveRef/cmdbzelle/x19PlAdd/Eingabemaske abgewiesen), plToggle (legt bei Bedarf das Raster nach Vorlage an),
                      plEmb(e) = Einblenden über gleiche Zeilen-/Spaltenbezeichnung (tgLbl, gefaltet), Bausteine ohne passende Zeile/Spalte → rest (im Plattformlayer
                      „Aus „…“ ohne passende Zeile oder Spalte“ + Prüfhinweis). Darstellung: eigene Plattform-Bausteine farbig (.ref.pl, CSS-Variablen --pb/--pc/--pt),
                      eingeblendete gestrichelt in der Farbe ihrer Plattform (.ref.emb, ↗ öffnet deren Raster, optional .embt = Zelltexte), Kopfleiste tgEmbBar
                      (ui.tgEmb Standard an, ui.tgEmbTxt; gespeichert über saveUi – kein neuer Speicher), Legende je Plattform, Plattformlayer-Zeile tgPlRow mit „unter …“.
                      Exporte: tgCellText/tgPlText für Excel/CSV („Plattform X: a, b“), tgSvg neu (Farben je Chip, Legendenzeile, Plattformzeile blau). TAP ↔ IT-SYSTEM:
                      Feld F_TAPSYS attrs.itsystem („Gehört zum IT-System (genau eines)“, Feldart ref, legt neue IT-Systeme an) als erstes Feld des Architekturplans;
                      tapSys(t) erbt entlang der Stufen (Release/Version), tapsOf(s), tapFam, tapExcl (je IT-System genau ein TAP – andere TAP-Familie wird gelöst, Meldung
                      tapMsg „vorher gehörte er zu …“), tapSet, tapAuto (addLink: Verknüpfung TAP ↔ IT-System füllt NUR ein freies Paar, nie für Raster-Bausteine),
                      tapDrop (removeLink löst die Zuordnung), sysPlats(s) = Plattformen direkt + über den TAP (auch geschachtelt), plSystems(p) umgekehrt (für checks()
                      „Plattformen ohne IT-System“/„IT-Systeme ohne Plattform“). normalize: itsystem nur bei Architekturplänen mit vorhandenem IT-System; alte Modelle:
                      TAP ohne itsystem, der mit genau einem IT-System außerhalb seiner Zellen verknüpft ist, bekommt es. whoMigrate 'ref' legt bei Nicht-Personen-Feldern
                      jetzt Elemente der Zielebene an (vorher fälschlich Personen). OBERFLÄCHE: tapSysHtml am IT-System (TAP mit ×, „Vorhandenen/Anderen TAP zuordnen“
                      data-tapsys, „＋ Neuer TAP für dieses IT-System“ x19Act tap:neu, Plattformen über den TAP, „als TAP festlegen“/„Verknüpfung lösen“), tapLinksHtml
                      am TAP, plAssignHtml (Plattformlayer per Häkchen, data-plsel), plInHtml an Plattformen („Kommt vor in“, data-plin nur kreisfreie Ziele), Raster-Kopf
                      „🖥 TAP von …“ bzw. Warnung, Eingabemaske (itmSecs „▦ Architekturplan (TAP), Plattformen, Komponenten“), Übersicht x19RasterHtml/x19PropsHtml mit
                      farbigen bzw. geschachtelten Chips. x19Checks: TAP ohne IT-System, IT-System mit mehreren TAPs, Plattformen im Kreis, Bausteine ohne passende Zeile/
                      Spalte. Beispiel: „TAP Modellierungswerkzeug (Zielbild)“ gehört zu Modellierungswerkzeug, Plattformlayer = Container-Plattform, darunter
                      Virtualisierungsplattform (eigenes Raster mit k3) → 5 eingeblendete Bausteine in zwei Farben. Texte mit Namen als ganze Sätze (plOpenT, plEmbT,
                      plSubT, plSubOpenT, plUnterT, plUnterKT, plWieT, plRestT, tapVonT, tapBisherT), damit die Sprachwahl sie als Muster findet; Muster und Zählzeilen
                      der Raster-Navigation („{0} · {1} Zeilen × {2} Spalten“, „Läuft direkt auf Plattformen ({0})“ …) in sprache/werkzeuge/8em-modeller.html.json texte/
                      zusatz, Übersetzungen sprache/texte/0099.json und 0100.json. Hilfe-Abschnitt „▦ Plattformlayer“ mit „Plattformen schachteln“ und „TAP und IT-System“.
                      GEPRÜFT im Vorschau-Browser: Beispiel in allen drei Bildern (Farben, Einblenden, Texte an/aus, Legende), Kreis abgewiesen (Menü, Auswahl, Maske),
                      umbenannte Zeile → Rest + Prüfhinweis + Rückgängig, Regeln tapAuto/tapSet/Erben/removeLink/Rückgängig, mit Maus und Tastatur (neuer TAP aus dem
                      IT-System, Vorlage, „＋ Plattform“, Auswahl „Kommt vor in“, TAP umhängen mit Meldung, neues IT-System eingetippt + Tab), SVG/XLSX/CSV ohne NaN,
                      normalize idempotent, DE und EN (MC4YSprache.offen ohne neue Reste); Peters Modell (unterlagen-privat/MC4Y, Fassung 16) in einer Wegwerfkopie:
                      fehlerfrei, idempotent (Kopie und Browser-Speicher danach gelöscht, Original unverändert). TESTPLAN 8EM-20. Bekannt, nicht geändert: nach Eingabe in
                      einem ref-Feld + Tab landet der Fokus auf der Seite (bestehendes Verhalten aller ref-Felder).
                      ORG-EINHEITEN UND BEZIEHUNGEN AUS DEN STAMMDATEN (26.10.2026, Build 14:09, Peters Auftrag „Der 8EM-Modeller … nutzt diese Org-/
                      Einheiten-Beziehungen noch nicht … Einheiten samt Organisation bzw. diese Beziehungen aus den Stammdaten übernehmen“; Peters Wahl
                      per AskUserQuestion: BEIDE Sichten – Einheit UND Datenschutz-Partei – und die Beziehungen ausrichtbar mit Ziehpunkten). Menüpunkt
                      „Import ▾ › 🏢 Organisationseinheiten und Beziehungen aus den Stammdaten“ (act sdOrgEinheiten) → sdOrgDialog/sdOrgUebernehmen (Block
                      vor „Start“ nach dem STAMMDATEN-ANSCHLUSS; SD_BEZ_REL, sdOrgArt, sdOrgBestand). Liest window.MC4YStammdaten.daten() (einheiten mit
                      einheiten[].org, beziehungen der Stammdaten-Fassung 1, siehe stammdaten-editor.html „BEZIEHUNGEN …“) und legt im IST-BILD
                      (withBild('ist')) an: Organisationseinheiten (orgart aus der OE-Art per sdOrgArt, parent nach Namen), externe Organisationen (typ ≠
                      'eigen', in den Beziehungen referenziert) wahlweise als Einheit „extern / Partner“, als Datenschutz-Partei (organisation.partner) oder
                      beides (Dialog sdoExt einheit|partei|beides|keine), und die Beziehungen als Organisations-Beziehungen doc.rels k='organisation'
                      (SD_BEZ_REL: unterstuetzt/leistet/beraet/liefert/beauftragt(swap)/kunde(swap) → REL_T 'leistung', partner/mitglied → 'zusammen',
                      tochter → 'berichtet', sonst → 'zusammen'; r.label = Originalwortlaut, r.note = notiz) MIT Ziehpunkten (die Organisations-Beziehungen
                      des Modellers haben die Ziehpunkte/via bereits). Vorhandene Elemente werden nach Namen gefunden (vaFind) – keine Doppel, erneuter
                      Aufruf aktualisiert (idempotent); Beziehungen zu Datenschutz-Parteien werden NICHT als Linie gezeichnet (nur Organisations-Elemente
                      sind beziehungsfähig – nSkip, Hinweis). EIN Rückgängig-Schritt, nur bei echter Änderung (Snapshot-Vergleich; wirkungsloser Import
                      meldet „Nichts Neues zu übernehmen“). Nur Übernahme, kein Dauer-Sync; der Stammdaten-Anschluss selbst (anschluss.js) blieb unverändert.
                      DE/EN: texte in sprache/werkzeuge/8em-modeller.html.json (Menü-Button als EIN kombinierter <1>-Schlüssel wie die übrigen Import-Buttons,
                      Zählsatz und Hints als {0}-Muster; SD_BEZ_REL-Label „ist Tochtergesellschaft von“ = „is a subsidiary of“; „eigen“ = „eigen“ als
                      Datenwert). GEPRÜFT im Vorschau-Browser mit simuliertem Bestand (eigene Firma + Steuerkanzlei, 2 Einheiten, 2 Beziehungen): 4 Elemente +
                      2 Beziehungen angelegt, orgart/parent korrekt, zweiter Import 0 Änderungen/0 Undo, undoSteps(2) nimmt alles zurück; DE↔EN (Titel, Label,
                      Radios, Button inkl. <small>); statisch 0/0, sprache.py offen „keine neuen offenen Texte“, uml-editor neu gebaut (Inhalt unverändert),
                      Suite gebaut. Datenschutzerklärung unverändert (liest denselben Stammdaten-Bestand im selben Browser, legt nichts Neues an, überträgt
                      nichts; Abschnitt 7 deckt die Stammdaten-Verarbeitung ab). ITSM und Kanban übernehmen diese Org-Beziehungen bewusst NICHT (ihre
                      Stammdaten-Adapter führen nur Personen/Rollen, keine Organisationseinheiten).
BEZIEHUNGS-ZIEHPUNKTE ÜBER ALLE MODULE (26.10.2026, Build 15:01, Peters zweiter Teil derselben Antwort: „die Beziehungen auch ausrichtbar … Ziehpunkte … an
                      mehreren Stellen angreifen … über alle Module konsistent“). Schon vorhanden im 8EM-Modeller, 2D-/3D-Systembrett und 3D-Kern (l.via [{t, v}],
                      Catmull-Rom splinePts, ◆/+-Griffe). Mit DEMSELBEN System nachgerüstet – ◆-Griff je Punkt (ziehen formt, Doppelklick entfernt), „+“-Griff je
                      Abschnitt (einfügen), Einrasten auf gerade (v < Rast → 0), EIN Rückgängig-Schritt, via überlebt normalize – in: (1) WIRKUNGSGEFÜGE
                      (werkzeug-kern/mod_wg.js: l.via an scene.links; wgGeo/wgSpline/wgCum/wgAtP/wgTV, Griffe im overlay, onPointerDown/Move/Up mit wg.drag, onDouble,
                      Knopf „╱ Gerade“ wgStraight; OHNE via bleibt der bisherige automatisch gebogene Pfeil, damit Gegenrichtungen sich nicht überdecken und Bestände
                      gleich aussehen. Änderung nur in werkzeug-kern/, dann `py werkzeug-kern/bauen.py alle --force`). (2) VERTRIEBS-NAVIGATOR EINKAUFSGREMIUM
                      (vertrieb-quellen/v5_gremium.js: b.via an account.bez; gremGeo/gremSpline/gremTV, gremLinienSvg zeichnet polyline + Griffe .gvh/.gah, gremVerdrahten
                      (pointerdown/move/up art 'via', dblclick auf .gvh); v2_daten.js normalize reicht via durch). (3) STAMMDATEN-BEZIEHUNGSNETZ
                      (stammdaten-quellen/s4_grafik.js: b.via NUR an den „bez“-Kanten (doc.beziehungen) – die abgeleiteten Kanten parent/leitung/oeorg/… bleiben gerade,
                      da ohne Objekt zum Speichern; netzGeo/netzSpline/netzTV, netzSvg zeichnet polyline + Griffe .gvia, gPointerDown/Move/Up art 'netzvia', Doppelklick über
                      die vorhandene gLetzt-Mechanik; s2_daten.js normalize säubert via). GRENZFÄLLE (Peters Default-Entscheidung, von ihm bestätigbar): Mindmap hat
                      bereits Form-Griffe (zwei Bézier-Steuerpunkte + zwei Endanker) – als „schon umgesetzt“ belassen, nicht auf Zwischenpunkte umgebaut; Kanban-
                      Abhängigkeitslinien verbinden Board-Karten, die beim Verschieben automatisch in Spalten/Bahnen einrasten – von Hand ausgerichtete Linien wären sofort
                      falsch, daher ausgenommen. Keine Beziehungslinien zwischen frei beweglichen Knoten: Stakeholder, Kunden-Navigator, ITSM (reine Datenkopplungen).
                      DE/EN: texte in sprache/werkzeuge/{wirkungsgefuege,vertrieb,stammdaten}-editor.html.json (Ziehpunkt = handle; „Ziehpunkt eingefügt“ = Handle added,
                      „Linie geformt“ = Line shaped, „Ziehpunkt entfernt“ = Handle removed (global, texte/0053), „Linie gerade gezogen“ = Line straightened, „╱ Gerade“ =
                      ╱ Straight, Bedien-Hinweise je Werkzeug). GEPRÜFT im Vorschau-Browser je Modul: Geometrie mit/ohne via (ctrlPts/adds, NaN-frei), SVG-Griffe, End-to-End
                      Einfügen + Formen + Doppelklick-Entfernen (je ein Undo), normalize-Rundlauf (via erhalten), DE↔EN; statisch 0/0, Suite gebaut, `sprache.py offen` 0 bei
                      den drei Werkzeugen. Datenschutz unverändert (reine Bedienung; via wird in den vorhandenen Dateien/Beständen gespeichert, kein neuer Browser-Speicher,
                      keine Übertragung). Kein eigener Blog-Punkt (Bedien-Detail wie die übrigen internen Linien-/Griff-Verbesserungen).
                      NACHBESSERUNG 26.10.2026 (Build 18:12, Peters Fund „Ziehpunkte im Stammdatenmodul funktionieren nicht“): Im Beziehungsnetz zeichnete netzSvg die
                      Griffe nur beim Neuaufbau und nur bei Auswahl (`if (bezObj && sel)`), das Anklicken einer Beziehung rief aber nur grafikAuswahl() (reines Umschalten der
                      .sel-Klasse, KEIN Neuaufbau der SVG) – daher erschienen nie Griffe. Behoben nach dem bereits im Modul genutzten Muster von `.plus`/`.hdl` (immer im DOM,
                      per CSS auf `.sel` einblenden): s4_grafik.js zeichnet die Ziehpunkte jetzt für ALLE bez-Kanten (`if (bezObj)`), stil.css blendet sie nur bei Auswahl ein
                      (`#gsvg .gvia{opacity:0;pointer-events:none}` + `#gsvg .kante.sel .gvia{opacity:1;pointer-events:auto}`, Cursor move/copy). Kein Neuaufbau beim Auswählen
                      nötig. Vertriebs-Navigator (v5_gremium.js: Auswahl ruft renderFlaeche(), voller Neuaufbau) und Wirkungsgefüge (werkzeug-kern/mod_wg.js: Kern zeichnet das
                      overlay() bei Auswahl neu) waren NICHT betroffen – dort erscheinen die Griffe korrekt. GEPRÜFT im Vorschau-Browser (Beispiel, Beziehungsnetz): für die
                      5 bez-Kanten liegen die Griffe im DOM, unselektiert opacity 0 / pointer-events none, bei .sel opacity 1 / auto; eine Beziehung ausgewählt → ◆ und „+“
                      sichtbar, Ziehpunkt eingefügt → rote Linie gebogen (via.length 1). Danach `py stammdaten-quellen/bauen.py` (Build 18:12; Test-Download-Kopie aktualisiert)
                      und `py mc4y-suite/bauen.py` (Suite-Kopie 18:17). Lehre: Bei auswahl-eingeblendeten SVG-Griffen prüfen, dass der Einblend-Weg (Neuaufbau ODER CSS auf .sel)
                      wirklich greift, und die Mausbedienung testen – nicht nur, dass der Zeichen-Code existiert.
uml-editor.html       (seit 17.10.2026, Peters Wunsch „Der UML-Editor sollte auch außerhalb des 8EM-Modellers zur Verfügung stehen und einzeln herunterladbar sein“)
                      Eigenständiges Werkzeug (NICHT eigenmächtig hochladen). ERZEUGT aus dem 8EM-Modeller: QUELLEN in uml-quellen/ (nicht hochladen): seite.html
                      (Gerüst, CSS, Dialoge, Hilfe mit helpX/closeHelp; Platzhalter {{JS}}), u_vorab.js ($, $$, Ersatz für 8EM-Bezüge: DATEN_LAYERS/ICT_KEYS/
                      ORG_LAYERS = [], byId → null, lyOf-Stub – MUSS vor dem UML-Teil stehen, UREF wird beim Laden ausgewertet), u_editor.js (eigene Oberfläche:
                      Projekt, Rückgängig 150 mit Pfeilen + Verlauf, Zwischenstand, Dateien, Lernbeispiele, 8EM-Übernahme, Start). `py uml-quellen/bauen.py` liest
                      aus 8em-modeller.html die Deklarationen HILFEN (xe, uid, …, lnGeo, pmPngBlob, makeZip, svgToPrims, XP, EXP_T – Liste in bauen.py; Block =
                      Zeilen ab Spalte 0 bis zur nächsten Spalte-0-Zeile, die nicht mit } ) ] beginnt) und den zusammenhängenden UML-Teil zwischen den Marken
                      „/* ---------- UML-Editor (Fassung 14“ und „/* ---------- Editor-Zustand ---------- */“ (+ NACH_UML umlAddExample), ersetzt Texte
                      (8EM-Modeller → UML-Editor in Exporten, „aus dem 8EM-Modell erzeugen“ entfällt) mit Mindestanzahl – bricht laut ab, wenn sich der Modeller
                      geändert hat –, ruft editoren-version.py (--ohne-version für Zwischenstände). Marken-Kommentare im Modeller NICHT umbenennen. Änderungen
                      an Notation/Zeichnen/Export im 8EM-Modeller wirken nach erneutem Bauen auch hier; Oberfläche nur in uml-quellen/u_editor.js ändern.
                      Format 'mc4y-uml' version 1, *.uml.json {format, version, name, desc, cur, stand, diagramme [8EM-Diagrammobjekte, ohne ref]}; normalize
                      verwirft Bezüge. Zwischenstand localStorage 'mc4y-uml-entwurf' (nicht im Beispiel), Ansicht 'mc4y-uml-ui'. #beispiel[&k=<Art>&sel=<Name>] =
                      Lernbeispiele (14 Arten, fiktiv). „⋯ › Aus einem 8EM-Modell übernehmen“ liest modell.8em.json (alle Bilder), Bezüge → Namen, neue ids, ein
                      Rückgängig-Schritt. FEHLER BEHOBEN beim Bau: Eigenschaftsfeld schrieb eine noch offene Änderung in das NEU gewählte Symbol (change-Ereignis
                      nach dem Klick) → Ziel wird beim focusin festgehalten (feldZiel). Derselbe Fehler steckt vermutlich im UML-Editor des 8EM-Modellers
                      (umSelObj im change-Handler) – NICHT geändert (kein Auftrag), Peter angeboten. Suite: WERKZEUGE '.uml.json', HAKEN H_2D (let fileHandle,
                      async function openDoc). NAMES/SD_OHNE in editoren-version.py. Geprüft: 14 Arten darstellen + alle Exporte ohne NaN, normalize idempotent,
                      mit der Maus zeichnen/verbinden/beschriften, 8EM-Übernahme (auch Soll-Bild), Rückgängig/Wiederholen, Zwischenstand nach Neuladen, 375 px;
                      in der Suite Speichern in die Ablage und Öffnen per #suite-oeffnen. Blog-Entwurf 2026-10-17-tool-uml-editor.md.
taschenrechner-editor.html (seit 17.10.2026, Peters Wunsch „einfacher Taschenrechner, der auch wissenschaftliche Funktionen kann“) Eigenständiges Werkzeug
                      (NICHT eigenmächtig hochladen), von Hand gepflegt (keine Quellen-Ordner). Eigener Zerleger + rekursiver Abstieg (kein eval): Punkt vor
                      Strich, Potenz rechtsassoziativ, -2^2 = −4, implizites Mal (2π, 3(4+5)), Dezimalkomma oder -punkt, E-Schreibweise, Prozent wie am Tischrechner
                      (a ± b % = a·(1 ± b/100), a × b % = a·b/100, b % = b/100), Funktionen mit ';' als Trenner (FN + ALIAS deutsch/englisch: runden, wurzel, log(x; b),
                      nCr, nPr, min/max/mittel/summe, zufall()), Fakultät über Gamma, DEG/RAD/GRAD, 2nd (Umkehr), hyp. Anzeige zahlText: ganze Zahlen < 1e15 voll,
                      sonst 12 gültige Ziffern, E-Schreibweise ab 1e15 bzw. unter 1e-9; Tausenderpunkte wählbar. Verlauf (max. 200, Klick = Ergebnis, Doppelklick =
                      Rechnung, ↑↓, CSV, Kopieren), Speicher MS/MR/M+/M−/MC, Ans. localStorage 'mc4y-taschenrechner-ui' (Einstellungen) und
                      'mc4y-taschenrechner-verlauf' (nur mit Häkchen „Verlauf und Speicher in diesem Browser behalten“); #beispiel = Beispielverlauf ohne Speichern.
                      Display: nicht aktive Anzeigen 2nd/hyp/M abgeblendet. Suite: Ablageart 'ohne'. NAMES/SD_OHNE in editoren-version.py. Geprüft: 52 Rechnungen,
                      14 Fehlerfälle mit verständlicher Meldung, Formatierung, Maus-Bedienung, Verlauf nach Neuladen, 375 px. Blog-Entwurf 2026-10-17-tool-taschenrechner.md.
                      E-SCHREIBWEISE (21.10.2026, Peters Entscheidung „1e3 = 1000“, Build 13:57): `zerlegen` liest E ODER e direkt hinter einer Zahl, gefolgt von
                      Ziffer bzw. Vorzeichen + Ziffer, als Zehnerpotenz (1e3, 2,5e-3, 1E−3); Kommazahl im Exponenten → Fehler. Sonst bleibt e die Eulersche Zahl
                      (`e`, `2e` = 2·e, `e^2`, `e·3`, mit Leerzeichen `2e - 3`). Ein Name „e<Ziffern>“ (`e3`, auch `3 e3`) → Hinweis „nicht eindeutig“ statt
                      „Unbekannte Funktion“. `operandStart` erkennt Zahl mit Exponent und Vorzeichen (Funktionstaste nach `1e-3`). Hilfe-Abschnitt „Die Zahl e“.
                      Feste Testliste tests/pruefstand/taschenrechner-faelle.json (116 Fälle, TR-02/TR-04) – bei Änderungen am Rechnen mitpflegen.
                      DEZIMALZEICHEN NACH SPRACHE (21.10.2026 nachts, mit dem englischen Zahlenformat): E.dez folgt der Sprache der Oberfläche (dezAuto),
                      bis es in den Einstellungen gewählt wird (E.dezWahl); Haken zahlenAus in sprache/werkzeuge/taschenrechner-editor.html.json (siehe
                      „ZAHLEN UND BETRÄGE AUF ENGLISCH“).
fragebogen-eam-reifegrad.html (seit 13.10.2026, EAM-Werkzeug Nr. 10) EAM-Reifegradcheck: 10 Bereiche (EAM_DIM wie im 8EM-Modeller: Richtung, Fähigkeiten,
                      Prozesse, Daten, Anwendungen, Technik, Schnittstellen, Governance, Planung, Mensch) × 3 Aussagen (Kurzfassung = jeweils erste), Skala 0–5,
                      Wunschbild „Gewünscht“ (Standard an), Netzdiagramm, Tabelle, drei größte Hebel. JSON format 'mc4y-eam-reifegrad' (ergebnis.bereiche {k:
                      {heute, gewuenscht}}), Zwischenstand, Drucken, Übergabe an den 8EM-Modeller (#mc4y8em=…, #org= füllt die Organisation). Selbsteinschätzung,
                      nicht validiert (Hinweis im Bogen). Stempel/Lizenz über editoren-version.py (NAMES).
                      Seit 15.10.2026: „Beispiel-Ergebnis ansehen“ (#btnBsp, Muster GmbH, frei erfundene Werte BSP_IST/BSP_SOLL je Bereich, Hinweis oben, Start/Neu leer).
fragebogen-it-erhebung.html (seit 13.10.2026, EAM-Werkzeug Nr. 1) IT-Erhebungsbogen für Fachbereiche/Systemverantwortliche: je IT-System ein Reiter
                      (Steckbrief, Verantwortung, Nutzung, Daten, Betrieb und Notfall, Schnittstellen-Tabelle, Kosten, Einschätzung nach den Kriterien des
                      Anwendungsportfolios), Zusammenfassung, Speichern/Zwischenstand/Drucken/Laden, JSON format 'mc4y-it-erhebung'. Aus dem Modeller geöffnet:
                      Vorbelegung über 'mc4y-8em-bereit'/'mc4y-8em-vorlage' (vorhandene Systeme, Kriterien), Ergebnis zurück per postMessage (ein Rückgängig-
                      Schritt, Rollen/Personen zentral angelegt, kein Doppel beim erneuten Einlesen). Namen in Freitext werden erst im Modeller zentral zugeordnet.
                      Seit 15.10.2026: „Beispiel ansehen“ (#btnBsp) lädt über laden() zwei frei erfundene IT-Systeme (Warenwirtschaft, Online-Shop) mit allen Angaben;
                      Hinweis über den Reitern mit „Leeren Bogen beginnen“ (data-act bspLeer), 8EM-Übergabe solange versteckt.
                      FASSUNG 2 DES BOGENS (20.10.2026 abends, Peters „IT-Erhebungsbogen anpassen“ = an die Fassung 19 des Modellers; Build 21:10, JSON version 2):
                      Felder wie im Modeller – stdArt (STDART = STD_ART), betriebsort/betriebsortSonst (BETRIEBSORT), abtFach/abtIT (Abteilungen als Text), Gruppe
                      „Lebenszyklus“ (lzHtml: s.lz {idee … abgeschaltet} als TT.MM.JJJJ/MM/JJJJ, LZ = LZ_PH; lebenszyklus = Phase nur, wenn sie von den Daten abweicht;
                      lzIso/lzPhaseOf wie im Modeller, lzInfo zeigt die Phase aus den Daten, unlesbare Daten und falsche Reihenfolge), Daten getrennt in daten
                      (verwaltet) und datenLesen (liest), prozesse = Abläufe/Aufgaben/Funktionen, Interfaces mit Status (SXST = SX_ST) und Richtung als
                      „sendet an/empfängt von/beides (= zwei Interfaces)“ (Werte weiter liefert an/erhält von/beides). laden() übernimmt Fassung 1 (LEBEN_ALT,
                      seit → lz.einfuehrung, bis → betrieb/abschaltung/abgeschaltet je Phase). fieldset min-width:0 + .tw (Interface-Tabelle scrollt am Handy für sich).
                      Beispiel mit Lebenszyklus-Daten, zweiter Richtung und geplantem Interface. SYNC: Listen und Lebenszyklus-Regeln gleich mit dem 8EM-Modeller halten.
                      Geprüft: Beispiel/leer/Fassung-1-Datei, lzInfo-Fälle, 375 px ohne Querscrollen, Rauchtest, DATEI-fragebogen-it-erhebung, UE-07 (neu: Lebenszyklus,
                      Standardisierung, Abteilungen, 4 Interfaces mit Status), Übernahme zweimal ohne Doppel, Vorbelegung → Bogen → Übernahme ohne Änderung.
fragebogen-werte-organisation-sd.html / fragebogen-werte-person_sd.html  (seit 01.10.2026; von Peter so umbenannt)
                      Eigenständige Spiral-Dynamics-/Graves-Fragebögen (sollen später auf der Website abrufbar und
                      an von Peter genannten Stellen in den 8EM-Modeller eingebunden werden). 10 Themen × 7 Farben × 2
                      Aussagen (a/b; Purpur…Türkis, Beige nicht abgefragt) = 20 je Farbe; Kurzfassung nur a (10 je Farbe).
                      Skala 0–5 ohne Mitte (Vorbild Schwartz PVQ; Org.-Themen angelehnt an OCAI/Cameron & Quinn, nur
                      Struktur, eigene Formulierungen), Reihenfolge je Thema zufällig. Auswertung: Anteil je Farbe =
                      Mittelwert / Summe der Mittelwerte (Summe 100 %, gleich verteilt 14,3 %); Stufen relativ dazu
                      (≥1,2 Schwerpunkt, ≥1,0 deutlich, ≥0,8 etwas schwächer, sonst wenig); Zustimmung % nur ergänzend.
                      Organisation und (seit 06.10.2026) Person optional mit Wunschbild (Heute/Gewünscht). Pflichthinweis „nicht wissenschaftlich
                      validiert“ auf Start- und Ergebnisseite. Keine externen Skripte, nichts wird übertragen; Ergebnis
                      drucken oder als JSON (format 'mc4y-werteprofil', version 2, typ, laenge lang|kurz, antworten
                      {"<thema><Farbe><a|b>": {ist, soll}}) speichern/laden. Beide Dateien teilen denselben Programmteil
                      (Kommentar „Programmteil“) – Änderungen in beiden nachziehen; nur CFG unterscheidet sich.
                      Peters Haltung: Einstufung erwünscht als Orientierung, keine Absolut-Aussagen/Etiketten.
fragebogen-u-organisation.html / fragebogen-u-person.html  (seit 01.10.2026, Entwurf zur Abstimmung)
                      Theorie-U-Fragebögen, gleiche Bauweise wie die SD-Bögen (Skala 0–5, lang/kurz, Org. und Person mit Wunschbild,
                      Pflichthinweis, JSON format 'mc4y-u-profil' version 1). Teil 1: 8 Themen × 4 Felder der
                      Aufmerksamkeit (Gewohnheit/Sachlich/Empathisch/Schöpferisch) × 2 (A<thema><feld><a|b>), Auswertung
                      als Anteile (gleich verteilt 25 %) + stärkstes Feld je Thema; Bezug zu SD = eigene grobe Zuordnung
                      (Blau/Orange/Grün/Gelb-Türkis). Teil 2: 5 U-Bewegungen × 4 (B<i><n>, kurz 2), Zustimmung % mit
                      Anregung für den schwächsten Schritt. Teil 3: Org. 4 Lernbarrieren, Person 3 innere Stimmen × 3
                      (C<i><n>, kurz 2), höher = bremst stärker. Gemeinsamer Programmteil in beiden U-Dateien.
                      Seit 06.10.2026 (Peters Hinweis) hat Teil C bei der Organisation auch „Gewünscht“ (kindsFor ohne C-Ausnahme):
                      gestrichelte Wunschbalken + rKeyC, Zeile „Heute → gewünscht“ (wunschC, größter Abstand ≥ 15 Punkte = Hebel),
                      JSON ergebnis.hindernisseGewuenscht; Erklärung TIPP.cSoll („niedriger Wert = soll wenig bremsen“). Im Kompass
                      gilt „Gewünscht“ für S, A, B, C, K und M ('SABCKM' in kindsFor) – nicht für Lage (L), Voraussetzungen (V)
                      und Warnsignale (X), dort ergibt ein Wunsch keinen Sinn. Peters Vorgabe (06.10.2026): „Gewünscht“ überall
                      einbauen, wo es passt – auch die PERSONEN-Bögen haben seitdem ein optionales Wunschbild („So möchte ich sein“;
                      hatSoll true in SD/U-Person und PERSP.person, Häkchen #mitSoll, bsp-Beispiele mit soll). Kompass-Auswertung:
                      Konflikte mit Satz „Gewünscht: …“, Innovationsdiagramm mit gestricheltem Punkt „gewünscht“
                      (innovation('soll'), quadSvg(inno, innoD, innoS)) und Text zum gewünschten Weg; JSON konfliktGewuenscht,
                      innovationGewuenscht. Neue Fragenteile: „Gewünscht“ standardmäßig vorsehen, wenn ein Wunsch sinnvoll ist.
                      Ausgewertet werden nur Aussagen der gewählten Länge (nach Längenwechsel keine Altantworten).
                      BEISPIEL-ERGEBNIS (15.10.2026 abends, Peters Auftrag „in alle Editoren und Fragebögen ein Beispiel“; Build 15:54/15:55): Knopf #btnBsp
                      „Beispiel-Ergebnis ansehen“ auf der Startseite aller fünf Bögen (Kompass: für die gewählte Perspektive; dort NUR in kompass_vorlage.html, dann
                      `py kompass_bauen.py` – seit heute mit --ohne-version für Zwischenstände). Block „Beispiel-Ergebnis“ im Programmteil (je Paar identisch):
                      bspStreu/bspWert (Profil je Dimension BSP[typ|persp][ist|soll|druck] + feste Streuung ±0,7, gerundet 0–5, bei jedem Aufruf gleich), Antworten über
                      dieselben IDs wie der Fragebogen (codes()/buildPages()/kindsFor), ausführlich + Wunschbild + „unter Druck“, Einheit „Beispiel: … (frei
                      erfunden)“; bspHinweis = gelber Kasten oben auf der Ergebnisseite, versteckt die 8EM-Übergabe; „Fragebogen beginnen“ und „Neu beginnen“
                      (ohne Rückfrage) starten danach LEER, Datei laden/Zusammenführen entfernt den Hinweis. Profile sind frei erfunden (keine Typisierung).
                      ÜBERGABE AN DEN 8EM-EDITOR (12.10.2026, alle fünf Bögen, im Kompass über kompass_vorlage.html): Block vor dateiSpeichern() –
                      BRUECKE = Kennung aus #mc4y8em=… (nur wenn window.opener), dann Knopf #btn8em „Ergebnis an den 8EM-Modeller übergeben“ vor
                      #btnSave; er setzt uebergabe = true und löst btnSave aus, dateiSpeichern() schickt dann statt der Datei {format:'mc4y-8em-fragebogen',
                      token, data} per postMessage an window.opener. #persp= wählt die Perspektive im Kompass vor, #einheit= füllt das Feld „Einheit“.
                      Ohne Kennung unverändert (auch auf der Website harmlos). Eingefügt per scratchpad bruecke.py (erkennt vorhandenen Block).
fragebogen-werte-u-kompass.html  (seit 01.10.2026) Kombinierter Fragebogen SD + Theorie U, Perspektive Organisation
                      oder Person in EINER Datei. NICHT von Hand ändern: wird mit `python kompass_bauen.py` aus
                      kompass_vorlage.html (Programm, Texte, Verbindungsmodell) + den CFG-Blöcken der vier Einzelbögen
                      (SD_ORG/SD_PER/U_ORG/U_PER) erzeugt – nach Änderungen an Aussagen in Einzelbögen neu bauen.
                      Teile: 1 Werte (S<thema><Farbe><a|b>), 2 Felder (A…), 3 U-Schritte (B…), 4 Lernbarrieren/Stimmen (C…);
                      Standard = nur a-Aussagen (120/118), ausführlich = a+b (236/232). Verbindung (eigene Einordnung,
                      im Ergebnis so gekennzeichnet): MAP Farbe→Feldgewichte ⇒ „aus den Werten erwartete“ Felder vs.
                      gelebte Felder (Passung, Abweichung in Prozentpunkten, LUECKE-Texte); WEG = nächster Entwicklungs-
                      impuls je Schwerpunktfarbe mit stützendem Feld und U-Schritt; Wunschbild ⇒ benötigtes Feld.
                      Seit Fassung 2 (01.10.2026) zusätzlich, nur in kompass_vorlage.html (EXTRA je Perspektive):
                      Teil 5 Lage (L0–2: dynamische/soziale/emergente Komplexität nach Scharmer), Becks sechs
                      Bedingungen für Veränderung (V<i><a|b>, „gegeben“ ab 60 %), Interessenkonflikte (K0–2, Kühls
                      Kritik; Hinweis „Harmonie überdeckt Konflikte“); Teil 6 Warnsignale (X…, Org = Gefahrenzone/
                      Absencing, Person = Absencing); Teil 7 nur Org: Innovationsklima nach Peters Masterthesis
                      (Euro-FH 2014, website/downloads/eBook_Masterthesis-Hoenig.pdf): M0–3 Prozessfähigkeit (CMMI 0–3),
                      M4–7 Führungsstil anordnend/informierend/partizipativ/delegativ → Quadrant (x<1,5: y≥2,5 Akzeptiertes
                      Spielen, sonst Gefahrenzone; 1,5–2,5 Gelebte Governance; ≥2,5 Stabile Linie), Idealpfad 1→3→2→1,
                      Methoden je Quadrant aus Kap. 4 der Thesis. Optional „Unter Druck“ (kind 'druck', nur Werte;
                      Regression nach Graves), Ich/Wir-Balance, „Mehrere Ergebnisse zusammenführen“ (Durchschnitt je
                      Aussage, Spannweite je Farbe/Feld; nur im Browser). Hinweis „Erprobungsfassung 2026“ in allen
                      fünf Bögen. JSON format 'mc4y-werte-u-kompass' version 2 (+ mitDruck, gruppe).
                      „Heute unter Druck“ (tatsächlich, kein Wunsch; Option mitDruck, kind 'druck') gilt im Kompass für alle
                      Verhaltensfragen: S, A, B, C, K, M (kindsFor(id) je Aussage) – nicht für Lage (L), Voraussetzungen (V)
                      und Warnsignale (X). Innovationsdiagramm zeigt dann zweiten Punkt „unter Druck“ (innovation('druck')).
                      Auch alle vier Einzelbögen haben die Option (SD: alle Themen; U: Teile 1–3). Hilfsfunktion
                      verschiebung(items, schwelle) formuliert die Veränderung (Anteile: 3 Punkte, Zustimmung: 5 Punkte).
                      Alle fünf Bögen: „Zwischenstand speichern“ (btnPause → gleiche JSON wie Ergebnis plus
                      zwischenstand:true, seite) über dateiSpeichern() = showSaveFilePicker (Ort frei wählbar), sonst
                      Download; „Gespeicherten Stand öffnen“ setzt bei zwischenstand auf der Seite fort, sonst Ergebnis.
                      beforeunload-Warnung bei ungespeicherten Antworten. Gut sichtbarer grüner Datenschutz-Kasten
                      (.box.privacy) + Zeile (.privline) auf jeder Fragenseite: keine Übertragung, nur lokale Datei.
                      ERKLÄRUNGEN (06.10.2026, Peters Wunsch, Fassung 2): Peter geht es um die BEDEUTUNG DER ANTWORTEN (gar nicht …
                      voll/sehr) im jeweiligen Zusammenhang, nicht um die Aussagen. SKALA[organisation|person][ist|soll|druck][0–5] und
                      SKALA.nur (Zeile ohne Bezeichnung). Antworttexte seit 06.10.2026 (Peters Wunsch): gar nicht, kaum, eher nicht, eher ja,
                      ziemlich, voll (Org.) bzw. sehr (Person) – vorher „ein wenig“/„etwas“ für 2/3; gespeichert werden nur Zahlen 0–5,
                      alte Dateien bleiben gültig (CFG.skala wird nur mitgeschrieben, nie gelesen). Zeigen auf eine Antwort (.scale label,
                      350 ms Verzögerung, sofort bei Fokus/Antippen) = btnHtml (#tip.klein); Zeigen auf die Bezeichnung = tipHtml: kurze
                      Definition TIPP[..][kind], alle sechs Antworten mit Bedeutung (gewählte hervorgehoben), bei Teil C Hinweis TIPP.c,
                      zuletzt Beispiel. Zeilenbezeichnungen „Heute“/„Gewünscht“/„Heute unter Druck“ sind
                      .lab[data-tip][data-tid] mit ⓘ (tipLab); Maus, Fokus oder Antippen zeigt #tip (position:fixed, eine Box für alle):
                      BEISPIEL JE AUSSAGE (seit 07.10.2026, Peters Kritik: „Beispiele sollen zu den einzelnen Fragen passen, nicht zum
                      Abschnitt“): jede Gruppe hat bsp = {'<code>': [Heute, Gewünscht, Unter Druck]} – SD-Themen Codes 'Pa'…'Tb',
                      U-Themen '1a'…'4b', U-Bewegungen/Hindernisse '0'…'3', Kompass EXTRA.*.konfliktBsp '0'–'2' und modell.bsp '0'–'7'
                      (483 Aussagen, je drei Sätze, Form „Situation → „Antwort“.“ mit Antwortwort der Skala). tipBsp(id) liefert das Beispiel
                      genau dieser Aussage; tipHtml zeigt unter „Beispiel zu dieser Aussage“ die ganze Situation – nur die eingeschalteten
                      Zeilen (tipAktiv liest die .lab[data-tid] der Aussage), gewählte Zeile hervorgehoben (ul.bx li.on), weil sich
                      Gewünscht/Unter Druck oft auf den Heute-Satz beziehen. Startseite (.tipi, ohne id): erste Beispielaussage (SD 'Ra',
                      U 'A03a', Kompass 'S0Ra') mit allen drei Zeilen. Beispiele sind erfunden (wechselnde Branchen bzw. Lebensbereiche),
                      ohne Modell-/Farbbegriffe, wertneutral; Wunsch mal höher, gleich, niedriger; bei Hindernissen (C) Wunsch meist niedrig.
                      Neue Aussagen brauchen ein eigenes Beispiel (fehlt es, erscheint nur die allgemeine Erklärung). Startseite: „Was
                      bedeuten die Antworten?“ (.tipi) an den Häkchen; ein Klick darauf schaltet das Häkchen nicht um.
mindmap-editor.html   (seit 01.10.2026) Eigenständiges Werkzeug (NICHT Teil der Website, nie hochladen). Mindmap mit
                      Zweigfarben (PALETTE, vererbt an Unterzweige, „erben“ = null), Symbolen (ICONS, Emoji, mehrere je
                      Zweig), Notizen je Zweig als HTML (contenteditable + execCommand: Absatzformat, Schriftart, Größe in
                      pt via font size=7-Trick → span, Farbe, F/K/U, Listen, Einzug, Ausrichtung, Tabellen mit Zeilen/
                      Spalten-Werkzeugen; Einfügen bereinigt per sanitize()). Datei *.mindmap.json (format 'mc4y-mindmap',
                      version 1; Knoten {id,text,color,icons,note,exp,collapsed,side,children}); Speichern per
                      showSaveFilePicker (Handle für Strg+S), Sicherung im localStorage nur gegen Absturz, Rückgängig.
                      Word-Export: eigene .docx-Erzeugung (makeZip wie Genogramm-Editor), Zweig-Format exp = auto|h1–3|
                      b1–3|n1–3|p|none (auto: Ebene 1–3 = Überschrift 1–3, tiefer = Aufzählung); Nummerierung über
                      abstractNum 1 (1. / 1.1. / 1.1.1.), neue Zählung nach jedem Nicht-Nummerierungs-Zweig; Notizen →
                      OOXML (Runs, Listen, Tabellen TableGrid, Notiz-Überschriften = Heading 4–6), optional Mindmap als PNG
                      am Anfang. In echtem Word geprüft (01.10.2026). Bild-Export SVG/PNG.
                      Tabellen wie in Word: Einfügen per Raster (10×8) oder Dialog (Kopfzeile, Rahmen, Breite);
                      Tabellentools-Leiste (#ttools) bei Cursor in Tabelle: Zeile oben/unten, Spalte links/rechts,
                      Zeile/Spalte/Tabelle löschen, Zellen verbinden/teilen (Raster-Modell tblModel/gridOf/rebuild mit
                      rowspan/colspan), Kopfzeile (data-head, TH), Rahmen (data-border all|outer|horiz|none), Schattierung,
                      Ausrichtung in der Zelle, Spalten gleich verteilen, An Inhalt / Ganze Breite (data-width full →
                      col-Breiten in %), Spaltenbreite durch Ziehen an der Zellgrenze, Zellen markieren durch Ziehen,
                      Tab/Umschalt+Tab durch Zellen, Tab in letzter Zelle = neue Zeile. Word-Export: gridSpan, vMerge,
                      tcW aus col-Breiten (1 px ≈ 15 dxa), shd, vAlign, tblHeader, tblBorders je Rahmenart.
                      Notizspalte ein-/ausblendbar (Knopf „Notizen“, Strg+M) und per Trennleiste #split in der Breite
                      (schmal: Höhe) verstellbar; Einstellung im localStorage 'mc4y-mindmap-ansicht'.
                      Seit 02.10.2026 Import (Knopf „Importieren“, Dialog #mImport: Zweige bis Ebene 1–9, tiefere
                      Überschriften als h1–3 in die Notiz; neue Mindmap oder unter ausgewähltem Zweig) aus .docx
                      (importDocx: Formatvorlagen-Kette, outlineLvl, numbering.xml → ul/ol, Tabellen mit gridSpan/vMerge/
                      shd, Titel-Vorlage → zentrales Thema), .odt (importOdt: text:h, Vorlagen, Listen, table mit
                      covered-cells) und .pdf (importPdf: eigener PDF-Leser – xref-freies Objekt-Scannen, ObjStm, Flate,
                      ToUnicode/Differences/WinAnsi, Textoperatoren mit CTM; Überschriften aus Lesezeichen, sonst Schriftgröße/
                      Fettdruck + Nummerierung, mehrzeilige Überschriften zusammengeführt, Kopf-/Fußzeilen/Seitenzahlen/
                      Verzeichniszeilen entfernt; PDF-Tabellen/Bilder nur als Text bzw. gar nicht; verschlüsselte PDFs
                      und Scans ohne OCR werden abgelehnt). ZIP-Leser readZip mit DecompressionStream('deflate-raw').
                      Gemeinsamer Sammler importCollector() + buildTree(). Export-Dialog „Exportieren“ (oFmt docx|odt|pdf):
                      exportOdt (ODF 1.3, mimetype zuerst/ungepackt, Vorlagen Heading_20_1–6, Listen L_Bullet/L_Num/
                      L_NoteNum, Tabellen mit covered-table-cell) – in Word geprüft, LibreOffice nicht installiert;
                      exportPdf = Druckfassung im unsichtbaren iframe → Druckdialog „Als PDF speichern“.
                      Rundlauf docx/odt → Import verlustfrei getestet; Word-PDF mit/ohne Lesezeichen und Masterthesis-PDF geprüft.
                      Bilder (seit 02.10.2026): Notizen dürfen <img src="data:image/…"> enthalten (sanitize lässt nur data:-
                      Bilder zu; Einfügen von Bildern aus der Zwischenablage). Import: docx über word/_rels (blip r:embed,
                      v:imagedata, Größe aus wp:extent), odt über draw:frame/draw:image, PDF über Image-XObjects (JPEG direkt,
                      Flate-Rasterbilder Gray/RGB/CMYK/Indexed inkl. PNG-Prädiktor und SMask → PNG; JPX/CCITT/JBIG2 und
                      Vektorgrafiken nicht; Logos auf ≥ 40 % der Seiten weggelassen); Formular-XObjects werden ausgewertet.
                      EMF/WMF → Hinweis „[Grafik … nicht übernommen]“. Export: prepImages() sammelt Bilder (EXP),
                      docxImgRun/odtImgFrame, Breite = style.width (max. 600 px). PDF-Tabellen: Zeile → Zellen bei großen
                      Abständen ODER senkrechten Rahmenlinien (vrules aus re/m/l + Füll-/Strich-Operatoren); Spalten =
                      Projektion der Zellen; Folgezeilen über Zeilenabstand; tableFrom() verwirft < 2 Spalten.
                      Bild-Dialog #mPic (Knopf #tPic, Doppelklick aufs Bild = bearbeiten, Drop/Strg+V ins Notizfeld öffnen ihn
                      ebenfalls): loadPicFile() wandelt alles außer PNG/JPEG/GIF per Canvas in PNG, verkleinert optional auf
                      1600 px; Breite (Standard 600, in Tabellenzellen 200), Ausrichtung = text-align des umgebenden <p>,
                      Bildunterschrift = folgendes <p data-cap="1"><i>…</i></p>, alt-Text. Einfügen über insertBlocks()
                      (eigene Absätze vor/nach dem Cursor-Absatz; insertHTML verschmolz sie). Auswahlmarkierung img.isel wird
                      beim Speichern entfernt. In Word geprüft (Größe, Ausrichtung, Bild in Tabellenzelle, Unterschrift).
                      Seit 02.10.2026 (dritte Runde): PDF-Leser löst /Length-Verweise auf (fixLens; vorher scheiterten u. a.
                      alle Scans), inflate() verträgt Überhang-Bytes, Ligaturen ﬁ/ﬂ → fi/fl, Lesezeichen nur bei ≥ 50 % Treffern,
                      Präsentationen (Querformat) = je Folie ein Zweig (größte Schrift), farbige kurze Zeilen als Überschrift,
                      reine Scans → Seitenbilder (res.scanned). Getestet mit allen öffentlichen PDFs aus website/files gegen
                      PyMuPDF (Wortzahl) sowie Word/PDF-A/„Microsoft Print to PDF“/Excel/PowerPoint-PDFs.
                      Notizen: Overlay #nOv über #notes (in #notesWrap) mit Griffen – Bild-Eckgriffe (img.isel, Breite), Tabellen-
                      Spalten (rebuild mit Breiten; volle Breite = Nachbar gleicht aus, sonst wächst Tabelle; Umschalt kehrt um)
                      und Zeilenhöhe (tr.style.height; rebuild behält Höhen; Export docx trHeight atLeast, odt min-row-height,
                      Import docx trHeight). Markiertes Bild wird nicht mehr per Range markiert, Entf → deleteImage().
                      Zeichenwerkzeug #mDraw (D, dShapeSvg, dRender; seit 02.10.2026 Fassung 2): Formenkatalog SHAPES/SH
                      (94 Formen in PowerPoint-Kategorien, je [Schlüssel, Name, Umriss(x,y,w,h)→Pfad, Zusatzflächen, Textbereich,
                      offen]; EVENODD nur Rahmen/Ring/Verbot), Textfeld, Freihand ('pen'), Bilder ('img', src), Symbole ('icon',
                      ICONS2 = 62 eigene Vektorsymbole im 24er-Raster, Farbe = fill) und Verbindungen ('conn': route straight|
                      elbow|curve, ah, c1/c2 = {id, site} mit 8 Andockpunkten SITES je Objekt; sitePt() sucht den Umriss per
                      isPointInFill auf #dProbePath; connEnds/connPts berechnen Endpunkte und Verlauf bei jedem Zeichnen →
                      Linien folgen beim Verschieben). Andocken in dDockAt (nahe Punkt oder Seite Richtung anderes Ende);
                      lösen per Endpunkt wegziehen, ganze Linie verschieben oder „⛓ lösen“ (dDetach); Objekt löschen lässt
                      Linien stehen. Eigene Symbole in IndexedDB 'mc4y-mindmap-symbole'. Alte Zeichnungen (v1 line/arrow)
                      → dNorm. Ergebnis PNG (2×) als <img data-drawing="encodeURIComponent(JSON {v:2,w,h,shapes})">; erneut
                      öffnen per Doppelklick, Rechtsklick-Menü im Notizfeld oder markieren + „✏ Zeichnung“; normale Bilder
                      per Rechtsklick „in der Zeichnung bearbeiten“ (openDraw(img, true) = Bild als Objekt).
                      Fremde GIF-Bibliotheken bewusst NICHT eingebaut (Lizenzen) – Peter kann eigene Sammlungen laden. Mindmap: Mehrfachauswahl (multi, topSel, toggleMulti; Strg/Umschalt+
                      Klick), moveNodes (Ziehen mehrerer, Strg = kopieren), shiftLevel (Alt+Umschalt+→/←), clip (Strg+C/X/V/D/A;
                      Fremdtext → nodesFromText als Gliederung), Kontextmenü #ctx (Zweig/Fläche/Anmerkung). Freie Elemente
                      doc.floats [{id,kind:'note'|'img',x,y,w,text,color|src}] in #floats (unter den Zweigen), verschieben/
                      Größe/Doppelklick/Entf, auch in buildSvg und bounds(). Zweig-Felder start/end (YYYY-MM-DD), res[];
                      doc.resources = Register (allResources() = Register ∪ benutzte); Seitenleiste #pStart/#pEnd/#pRes/#resPop
                      (ankreuzen, umbenennen, löschen; gilt für alle markierten Zweige); Anzeige .meta am Zweig; Export-Option
                      oMeta → kursive Zeile „Zeitraum … · Ressourcen …“ (docx/odt/pdf) und im Mindmap-Bild.
                      Zoom: Knöpfe lösen kein Verschieben mehr aus (pointerdown ignoriert .zoom/.banner), Mausrad proportional
                      zur Drehung (exp(-deltaY·0,0015), je Ereignis gedeckelt), Bereich 5–400 %, Prozentanzeige #zPct (Klick = 100 %).
                      Tasten (Stand 04.10.2026): Einfg oder Tab = Unterzweig am markierten Zweig (addChild; ohne Auswahl am
                      zentralen Thema), Enter = gleichrangig, Entf/„Del“/Rücktaste = markierte Zweige löschen (Rückfrage bei
                      Unterzweigen), F2 umbenennen. Gilt nur, wenn der Fokus nicht in Notiz/Eingabefeld liegt (dort normales Editieren).
                      Strg+V/Strg+Umschalt+V (04.10.2026): keydown setzt pasteMode ('into'|'after') und pasteSeen=false; kommt kein
                      natives paste-Ereignis (Fokus auf #viewport-div!), fügt ein Timer (80 ms) selbst ein – clip zuerst, sonst
                      navigator.clipboard.readText(). pasteAt(mode) → pasteInto (Unterzweig) bzw. pasteAfter (dahinter, gleiche Ebene;
                      am zentralen Thema = Hauptzweig). Ziehen: oberes/unteres Drittel eines Zweiges = drag.where before/after →
                      moveNodesRel (Linie .drop-before/.drop-after), Mitte = into → moveNodes (Rahmen .drop), Strg beim Loslassen = kopieren.
                      Kontextmenü: „Einfügen als Unterzweig“, „Einfügen dahinter (gleiche Ebene)“.
                      Bekannt: unter ca. 700 px Fensterbreite bekommt die Mindmap-Fläche keine Höhe (Spaltenlayout prüfen).
                      Seit 04.10.2026 (abends): FREI VERSCHIEBEN – Zweig auf freie Fläche ziehen = n.off {x,y} (verschiebt Zweig samt
                      Unterzweigen in layout(), Kanten wählen die Seite nach tatsächlicher Lage), Hauptzweig wechselt dabei ggf. n.side
                      (placeFree; Strg = Kopie). Beim Umhängen/Einfügen/Ebenenwechsel wird off gelöscht. Kontextmenü „Frei verschobene
                      Lage zurücksetzen“ / „Alle Zweige wieder automatisch anordnen“ (resetOff). Gezogene Zweige folgen der Maus
                      (transform, pointer-events:none für die Zielsuche).
                      UMHÄNGEN AN DER VERBINDUNGSLINIE (05.10.2026, Peters Wunsch – Ziehen am Zweig selbst war ihm zu ungenau):
                      drawEdges merkt edgeGeo {id,x1,y1,mx,x2,y2}; edgeAt() trifft die Bézierkurve (24 Stützpunkte, Toleranz 8 px/k);
                      ohne Maustaste hoverEdge() = gelber Schein (path.ehov) + Tooltip. pointerdown auf Linie → drag.mode 'edge'
                      (select(id, keep, noScroll) – Ansicht springt nicht), Gummiband #eBand/#eBandDot rastet am Zielzweig ein (grün,
                      .drop-Rahmen, Treffer irgendwo auf dem Zweig), Loslassen → moveNodes (Strg = Kopie; markierte Gruppe wandert mit;
                      gleicher Elternzweig/gleiche Seite = keine Änderung), ohne Ziel oder Esc = nichts. Ziehen am Zweig selbst
                      (Einordnen davor/dahinter, freie Lage) unverändert.
                      VERWEISE (05.10.2026): doc.links [{id, from, to, both, label}] (einseitig from→to oder beidseitig); render()
                      entfernt Verweise auf gelöschte Zweige. drawLinks() zeichnet gestrichelte quadratische Bögen (LINK_COL #8A6420,
                      Pfeil-Polygone ohne <marker>, damit SVG/PNG-Export sie enthält; eingeklappte Ziele → nächster sichtbarer Vorfahr,
                      gepunktet), linkGeo + linkAt() zum Treffen; bounds() schließt die Bögen ein. Setzen: startLinkPick() (Seitenleiste
                      „🔗 Verweis setzen …“, Kontextmenü) → Hinweisbalken #linkBar mit Richtung, Zielzweige anklicken, Esc/Fertig;
                      bei Mehrfachauswahl „Verweise zu den übrigen markierten Zweigen“. Klick markiert (selLink, Entf löscht),
                      Doppelklick = labelLink, Rechtsklick = Richtung (setLinkDir one|both|rev|cycle). Seitenleiste #pLinks (updLinks).
                      Export (Option oLinks/psLinks): refsOf(id) = eigene einseitige + alle beidseitigen; nur zu exportierten Zweigen
                      (exportSet/linkTargets). Word: versteckte Lesezeichen _mm<id> + w:hyperlink w:anchor; ODT: text:bookmark +
                      text:a href="#_mm<id>"; PDF: <a id="mm<id>">; PPTX: hlinkClick ppaction://hlinksldjump zur Folie des Ziels
                      (eigene Folie, sonst Folie mit dem Aufzählungspunkt); ODP: Seiten „FolieN“, Link als <text:span><text:a
                      href="#FolieN">…</text:a></text:span> (umgekehrte Verschachtelung liest PowerPoint nicht!). MindManager:
                      ap:Relationships nach OneTopic (ConnectionGroup 0/1 mit ObjectReference OIdRef, ConnectionStyle NoArrow/Arrow,
                      Beschriftung als FloatingTopics der Beziehung); Import wertet Standard (Anfang NoArrow, Ende Arrow) aus.
                      Geprüft: Word (docx + odt: alle Links lösen auf), PowerPoint (pptx + odp: Folienlinks), MindManager (Linien,
                      Pfeile, Beschriftung, Rückimport), PDF-HTML (Anker).
                      ANKERPUNKTE (05.10.2026, Peters Wunsch): Verweise sind kubische Bögen (linkShape → linkGeo {x1,y1,ax,ay,bx,by,x2,
                      y2,A,B,own}); Standard = bisheriger Bogen (Steuerpunkte 2/3 zum seitlichen Mittelpunkt). Frei: l.c1/l.c2 =
                      Steuerpunkt relativ zur Mitte von Ausgangs-/Zielzweig (px), l.e1/l.e2 = Andockstelle {u,v} auf dem Rand (Vielfache
                      der halben Breite/Höhe + 4 px) → Form wandert mit den Zweigen. Markierter Verweis zeigt in #lhand (eigene SVG-Ebene
                      über den Zweigen, NICHT im Bild-Export) Kreise c1/c2 und Quadrate e1/e2; Ziehen = drag.mode 'lh', Ziehen an der
                      Linie = 'lbody' (verschiebt beide Steuerpunkte), ein Undo-Schritt je Zug; Doppelklick auf Ankerpunkt bzw.
                      Kontextmenü „Form zurücksetzen“ = resetLinkShape. Eingeklappte Enden: Standardform ohne Ankerpunkte. MindManager-
                      Export lässt die Linienform weiter von MindManager berechnen (AutoRouting).
                      EINSTELLUNGEN (⚙, #mSet): Projektordner = FileSystemDirectoryHandle in IndexedDB 'mc4y-mindmap-einstellungen'
                      (Store kv, Schlüssel 'projekt'), Flags in localStorage 'mc4y-mindmap-einstellungen' {exportSub, listOnOpen}.
                      pickSave/pickOpen setzen startIn (Exporte ggf. Unterordner „Exporte“), „Öffnen“ zeigt #mOpen mit *.mindmap.json/
                      *.mmap des Projektordners (2 Ebenen tief, neueste zuerst); „Neuen Projektordner anlegen“ = übergeordneten Ordner
                      wählen + Name. Browser können keine getippten Pfade nutzen (Hinweis im Dialog). Getestet mit OPFS als Ersatzordner.
                      MINDMANAGER (.mmap, importMmap/exportMmap/openMmap): Format aus MindManager 25 per COM ermittelt (Probe-Dateien).
                      WICHTIG: Topic-Kinder müssen in der xsd:sequence-Reihenfolge stehen (SubTopics, FloatingTopics, OneBoundary,
                      OneImage, TopicViewGroup, Text, Color, NotesGroup, Offset, IconsGroup, …, Hyperlink, Task) und Map/Topic brauchen
                      OId (sonst „Unable to create document“). Notizen = NotesXhtmlData mit <html xmlns=xhtml>, Bilder als
                      mmnotes://n.ext + NotesData → mmarch://bin/GUID.bin; Task: TaskPriority urn:mindjet:PrioN, TaskPercentage,
                      StartDate, DeadlineDate, Resources „A, B“; Hauptzweig-Seite = Vorzeichen Offset CX; Symbole MM_ICONS (Emoji ↔
                      urn:mindjet:…, ①–⑨ = Priorität, ✅◕◑◔○ = Fortschritt, Rest als Textpräfix, beim Import zurückgewandelt).
                      Freie Anmerkungen ↔ FloatingTopics (Import als Hauptzweige). In MindManager geöffnet und zurück importiert geprüft.
                      PRÄSENTATION (exportDeck → deckPptx/deckOdp, Modell deckModel): Titel-, Überblicks- (PNG), Inhaltsfolie, je Zweig
                      bis Ebene psDepth eine Folie, tiefere als Aufzählung, Umbruch auf „(Fortsetzung)“ per Höhenschätzung psEst,
                      Notizen als Sprechernotizen (notesMaster/notesSlides) oder eigene Folien. PPTX mit echten Platzhaltern (Master +
                      3 Layouts), ODP mit presentation:class title/outline/notes; Titelfolien-Hintergrund in ODP als Rechteck (PowerPoint
                      ignoriert den Seitenhintergrund). Beide in PowerPoint geöffnet und als Bild geprüft; LibreOffice nicht installiert.
                      BEISPIEL (15.10.2026, Peters Auftrag „zu allen Editoren, falls noch nicht vorhanden, ein Beispiel einbauen“ – der Mindmap-Editor war
                      der einzige Editor ohne; Build 15:11): Knopf #bExample „Beispiel“ nach „Neu“ und beim Start (leere Mindmap, kein Wiederherstellen-
                      Angebot) Hinweisbalken mit „Beispiel ansehen“ (#bnBsp; nutzt #restore, dataset.art start|hinweis|wieder; render() blendet den Start-
                      Hinweis aus, sobald es Zweige gibt). exampleDoc() = frei erfundenes „Teamworkshop planen“ wie das Beitragsbild tool-mindmap.jpg (Ziele,
                      Ablauf, Teilnehmende rechts; Organisation, Ideen links), Termine relativ zu heute (iso(Tage)), Notizen mit Liste und Zeitplan-Tabelle,
                      Ressourcen, Symbole, eingeklappter Zweig „Später klären“, zwei Verweise mit eigener Form außen herum (e1/e2/c1/c2; einer einseitig mit
                      Beschriftung, einer beidseitig links) – die Standardform lief quer durch die Zweige. loadExample(): Rückfrage nur bei ungespeicherter,
                      nicht leerer Mindmap, vorige Mindmap als Rückgängig-Schritt, freie Anmerkung nach dem ersten Zeichnen unter die Mindmap (bounds()),
                      banner() = kurze Mitteilung. Hilfe-Absatz „Beispiel“. Geprüft im Browser: Laden per Knopf (auch mit der Maus) und Start-Hinweis,
                      Rückgängig/Wiederholen, Bild angesehen, Exporte docx/odt/pptx/odp/mmap (XML wohlgeformt, Inhalte vorhanden, 2 Beziehungen in .mmap).
besucher-auswertung.html  Lokale Auswertung der STRATO-Logdateien (NICHT hochladen): Aufrufe je Seite,
                      Besucher (Schätzung IP+Browser je Tag), Einstiege, Herkunft, Downloads, 404, Bots/KI-Crawler.
                      Liest .zip (eigener ZIP-Leser) und .gz direkt (DecompressionStream), Format per Dateikopf;
                      STRATO: Kunden-Login › Datenbanken und Webspace › Statistik › Logfile Download (6 Wochen, ab Hosting Basic).
                      Apache Combined mit optionaler Domain vorn;
                      überlappende Downloads werden zeilenweise dedupliziert. Bewusst kein Zähler auf der
                      Website (keine Tracking-Änderung, Datenschutzerklärung bleibt gültig).
editoren-version.py   (seit 02.10.2026) `py editoren-version.py` nach JEDER Änderung an einem *-editor.html oder
                      fragebogen-*.html laufen lassen (außer mc4y-editor.html). kompass_bauen.py ruft es am Ende selbst auf.
                      Fragebögen haben kein Hilfe-Fenster: MC4Y-INFO steht dort am Ende der <footer>, der Knopf scrollt
                      dorthin; Logo/Knopf sitzen in <header class="top"><div class="in"> (justify-content:flex-start). Setzt Lizenzkopf (GPL-3.0-or-later, Kommentar MC4Y-LIZENZ + <meta name=
                      "mc4y-build">) und im Hilfe-Fenster (helpDlg bzw. mHelp, vor dem Schließen-Knopf) den Block
                      MC4Y-INFO: Build-Version, Lizenz, Haftungsausschluss. In der Kopfzeile (<header>) zusätzlich
                      MC4Y-LOGO (Logo website/assets/img/logo-mc4y-120.jpg als data:-Bild, rund, Link zur Website) und
                      MC4Y-VER (Knopf #mc4yVer „Build … · GPL-3.0 · Nutzungshinweise“, öffnet das Hilfe-Fenster am
                      Hinweisblock). Die Blöcke zählen nicht zum Inhalt (normalized()). Build-Version = Datum/Uhrzeit, neu nur wenn sich
                      der übrige Inhalt ändert (Hash in editoren-versionen.json, frühere Hashes in „frueher“). Kopien in
                      website/downloads und website/files werden nur ersetzt, wenn sie einer früher gestempelten Fassung
                      entsprechen (Sicherung in editor-backups/<zeit>_editoren); abweichende bleiben unangetastet.
                      Seit 07.10.2026 zusätzlich Block MC4Y-LINKS vor </body> (links_block, in normalized() ausgenommen):
                      Wird das Werkzeug von einer Website unter …/downloads/ oder …/files/ geladen, schreibt ein Klick-Listener
                      Links auf https://www.my-coaching4you.de/… auf dieselbe Website um (Testdomain → Testversion; Peter landete
                      sonst über Logo/Impressum/Datenschutz/Kontakt der Fragebögen auf der Produktivseite). file: und Live
                      unverändert. COPIES umfasst seitdem auch website-test/downloads und website-test/files.
                      Seit 15.10.2026 zusätzlich Block MC4Y-STAMMDATEN vor </body> (sd_block aus stammdaten-quellen/anschluss.js, in normalized()
                      ausgenommen, nicht für SD_OHNE = stammdaten-editor.html) – der wahlweise Stammdaten-Anschluss (siehe stammdaten-editor.html). Am 15.10.2026
                      dadurch alle passenden Kopien in website/downloads, website/files, website-test/… mitgezogen (Sicherungen editor-backups/*_editoren).
                      website/files/genogramm-editor.html (von der Seite genogramm-editor verlinkt) wurde am 02.10.2026
                      auf Peters Wunsch durch die aktuelle Fassung ersetzt und wird seitdem mitgezogen. Lizenz/Haftung der Werkzeuge
                      steht auch im Impressum (pages_info.impressum). CSP: img-src enthält blob: (Mindmap/8EM nutzen
                      Blob-Bilder für Bild-Export, Zeichnung, Bild-Einfügen).
                      Hinweis: Genogramm-, Atom- und Mindmap-Editor stellt Peter selbst über website/downloads bzw.
                      website/files auf die Website (das „nie hochladen“ oben heißt: nicht eigenmächtig dorthin kopieren).
                      Seit 16.10.2026 (Downloads in Unterordnern, siehe 3b) sucht kopien(f) Kopien per os.walk in ALLEN Unterordnern
                      von COPIES (website[-test]/downloads, website[-test]/files); Sicherung unter editor-backups/<zeit>_editoren/<rel>.
Website-bauen.bat / Website-bauen-TEST.bat (+ .command für Mac)
SFTP-Helfer.bat       Startet generator/sftp_helfer.py für den Editor-Reiter „Server“ (siehe Abschnitt Editor)
sftp/                 einstellungen.json (ohne Passwörter), stand-test|live.json, protokolle/ – vom Editor/Helfer angelegt
server-kopien/        Downloads vom Server (je Übertragung ein neuer Ordner) – nicht hochladen
../../freehtml-editor/  (seit 06.10.2026, Ordner WebSite_Neu\freehtml-editor) FreeHTML-Editor: allgemeiner Website-Baukasten für
                      ANDERE Nutzer nach Peters Seitenstruktur (leere Seiten, freie Farben/Schriften/Logo, nur Browser, kein
                      Python, kein SFTP). Eigene CLAUDE.md und ANLEITUNG.md dort. Unabhängig von generator/ und mc4y-editor.html –
                      nichts zwischen beiden übertragen ohne Auftrag. Test-Server: launch.json „freehtml-editor“ (Port 8323).
                      Seit 16.10.2026 (Peters Auftrag, Umbau Downloads + „HTML-PY-Editor mit Upload, Windows/Linux/Apple“): QUELLE ist
                      freehtml-editor/quellen/ (editor.html mit Marken @PY/@FREE, py/ = Python-Programm und Startskripte), Bauen mit
                      `py bauen.py` dort → freehtml-editor.html (nur Browser) und html-py-editor/ + html-py-editor.zip (HTML-PY-Editor:
                      gleicher Editor + Reiter „Hochladen“ per SFTP (paramiko) / FTPS / FTP über htmlpy_helfer.py auf 127.0.0.1).
                      Downloads dort mit derselben Logik wie hier (experimentell/ressourcen, Anzeigenamen, Verschieben mit Link-Anpassung
                      und 301). Einzelheiten und Teststand in freehtml-editor/CLAUDE.md. Beide nicht eigenmächtig nach website/ kopieren.
                      MEHRSPRACHIG seit 20.10.2026 (Peters Auftrag „HTML-Editoren … mehrere Sprachversionen, die in Abhängigkeit der Browser-
                      Einstellungen angezeigt werden: Deutsch, Englisch, Italienisch, Spanisch, Portugiesisch, Französisch, Niederländisch, beliebig
                      erweiterbar“; Peters eigene Website übernahm die Sitzung „website-englisch“): Ausgangssprache in der Wurzel, weitere Sprachen
                      in /<kürzel>/, Fassungen je Seite/Beitrag (Entwurf/veröffentlicht, „veraltet“), Menü/Fußzeile/Texte je Sprache, hreflang +
                      x-default, Sitemap mit Alternativen, Sprachumschalter, Startseiten-Weiche nach Accept-Language in der .htaccess (ohne Cookies,
                      Referer-Ausnahme, Vary) bzw. nginx-Vorlage im Bericht; einsprachige Projekte erzeugen byte-gleich dieselben Seiten. Kein neuer
                      Browser-Speicher (Datenschutzerklärung unverändert). Blog-Entwurf 2026-10-16-tool-html-py-editor.md ergänzt. TESTPLAN FH-06 … FH-09.
                      DE | EN UND NEUANLAGE IN ALLEN SPRACHEN seit 21.10.2026 (Auftrag über „8EM-Modell Tool“, Build 2026-10-21 13:47, mit der Laufzeit vom 14:05
                      bzw. 15:00 neu gebaut 14:21 und 15:18 – der FreeHTML übernimmt sprache/sprache.js nur beim Bauen, also nach Änderungen dort neu bauen): Bedienung beider
                      Editoren umschaltbar (gemeinsame Laufzeit sprache/sprache.js als Kopie in freehtml-editor/quellen/sprache/, eigenes Wörterbuch dort –
                      sprache/ hier wird nur gelesen), Startskripte und Python-Konsole zweisprachig, README.txt im Paket; neue Seiten, Beiträge und Menüpunkte
                      entstehen in jeder Arbeitssprache und als „Übersetzung offen“ in allen übrigen (gleiche Regeln wie im MC4Y-Editor). Seit Build 16:12
                      (Peters „Ziehe die Editoren gleich und verwende Übersetzung überprüfen“) heißt die frühere Anzeige „veraltet“ in beiden Editoren
                      „Übersetzung überprüfen“ (EN “Review translation”), intern unverändert st.alt; Datumsangaben der Bedienoberfläche folgen deren Sprache (uiLoc).
                      Datenschutzerklärung unverändert (Zeile „Sprache der Werkzeuge“ gilt). TESTPLAN FH-07, FH-11 … FH-13.
unterlagen-privat/    Private Zeugnisse/Zertifikate und urheberrechtlich geschützte Fachunterlagen –
                      nie veröffentlichen, nicht lesen ohne Anlass (Regeln s. „Technische Regeln“)
editor-backups/       Automatische Sicherungen (Editor und Build)
ANLEITUNG.md          Bedienungsanleitung für Peter (englisch: ANLEITUNG.en.md, seit 21.10.2026 – bei Änderungen mitziehen)
editor-sprache.py     (seit 21.10.2026) setzt die Sprachwahl DE | EN in mc4y-editor.html und besucher-auswertung.html ein (siehe
                      „Mehrsprachigkeit der Website“ › VOLLSTÄNDIG ZWEISPRACHIG); nicht hochladen
mc4y-suite/           MC4Y-Produktivitätssuite (PHP-Server, Toolbox, Rechte, Sperren, Server-Ablage) – nie eigenmächtig hochladen;
                      Einzelheiten unter „Offene Punkte“ › MC4Y-PRODUKTIVITÄTSSUITE › STUFE 1 und in mc4y-suite/LIESMICH.md
konzepte/             Word-Kopien von Konzepten (Momentaufnahmen) – nicht hochladen
tests/                (seit 17.10.2026) TESTPLAN.md + Prüfstand für alle Module – nicht hochladen, nicht ins Update-Zip; siehe „Testplan“
gehirn_bauen.py       (seit 27.10.2026) `py gehirn_bauen.py` erzeugt das öffentliche „Gehirn“ (bereinigte CLAUDE.md, sensible Angaben als
                      Platzhalter, Abschnitte Auftraggeber/Server entfernt) in website[-test]/downloads/ressourcen/mc4y-suite-gehirn.md – vor
                      jedem Deploy laufen lassen; nicht hochladen. statisch_pruefen.py (Stufe A) meldet veraltetes/nicht bereinigtes Gehirn.
sprache/              (seit 20.10.2026) Sprachwahl Deutsch/Englisch aller gestempelten Werkzeuge (Laufzeit, Übersetzungsspeicher, Regeln) –
                      nicht hochladen, nicht ins Update-Zip; siehe „Sprachen der Werkzeuge“ und sprache/LIESMICH.md
```

## Spiele-Sammlung (Unterhaltung, seit Oktober 2026)

Neun eigenständige Browser-Spiele (NICHT eigenmächtig hochladen; wie alle Werkzeuge über editoren-version.py gestempelt: GPL,
Build, Logo, MC4Y-INFO, Sprachblock). Registriert in editoren-version.py SPIELE + NAMES und mc4y-suite/bauen.py SPIELE +
WERKZEUGE (ablage 'ohne', suite=True, oeffnen=False, Kategorie GA="Games") + INFO (Symbol + Beschreibung je Spiel; beide
Pflicht). Keine Datenablage, keine Schnittstellen. Prüfstand: tests/pruefserver.py _spiele() (liest editoren-version.py SPIELE)
nimmt sie in die Werkzeugliste auf; tests/pruefstand/werkzeuge.json führt jedes mit {beispiel:"keins", export:false} (sonst
benigne „Kein Beispiel-Knopf“-Warnung). Alle ZWEISPRACHIG (DE/EN) über sprache/sprache.js. Blog je Spiel DE+EN in der neuen
Kategorie „Games“ (pages_blog.CATEGORY_LINKS "Games" → ressourcen.html), dazu der ausführliche Erklärbeitrag
2026-10-27-vernetztes-denken-erklaert (DE+EN) zum Umwelt-Spiel (Rückkopplungen, Verzögerungen, Nebenwirkungen, World3/
Tragfähigkeit/demografischer Übergang/IPAT/Vester für Laien). Live sind die Spiel-Blogbeiträge Entwürfe; im Test veröffentlicht
(generator/test/blog[/en] + _teststand.json basis). Beitragsbilder website/blog/files/game-<slug>.jpg + vernetztes-denken.jpg.

- 3d-punktefresser.html „3D-Punktefresser“ (Pac-Man-ähnlich, WebGL; nur beschreibend).
- kloetze-stapeln.html „Klötze stapeln“ (fallende Blöcke, Tetris®-ähnlich – Tetris Holding, LLC, nur beschreibend).
- 3d-schach.html „3D-Schach“ gegen den Computer (volle Regeln inkl. Rochade/En-passant/Matt/Patt, 5 Stufen, eigene Engine, WebGL).
- 3d-dame.html „3D-Dame“ gegen den Computer (deutsche Regeln 8×8, Schlagzwang, Mehrfachsprünge, fliegende Damen, 5 Stufen, WebGL).
- umwelt-simulationsspiel.html „Umwelt-Simulationsspiel“ (vernetztes Denken: 12 Jahre, 4 Hebel Sanierung/Produktion/Aufklärung/
  Lebensqualität + 4 Folgegrößen über Rückkopplungen; 3 Ländertypen, Varianten-Editor, Wirkungsnetz, Verlaufskurven; angelehnt an
  World3/Die Grenzen des Wachstums, demografischen Übergang, logistisches Wachstum/Verhulst, IPAT, Vester/Ökolopoly – nur beschreibend).
- weltraum-abwehr.html „Weltraum-Abwehr“ (Space-Invaders-ähnlich, 2D-Canvas; Space Invaders™ = Taito, nur beschreibend).
- solitaer.html „Solitär“ (Klondike, 1 oder 3 Karten ziehen, Doppelklick-Autoablage, Rückgängig, localStorage Modus/Bestwert nur bei Änderung).
- skat.html „Skat“ (3 Spieler gegen KI; EIN-Planer naechsterZug() mit kiPending/humanDran gegen Doppelzüge; Matadore aus spiel._d12 = Originalkarten).
- schafkopf.html „Schafkopf“ (4 Spieler gegen KI; Sauspiel/Wenz/Solo, Teams/Laufende, Nullsummen-Wertung; gleicher Zugfluss wie Skat).

SPRACHE-BESONDERHEIT (wichtig für künftige Spiele/Werkzeuge): sprache.py kandidat() stuft Bindestrich-Wörter OHNE Umlaut über die
CODE-Regex `^[a-z]+(-[a-z]+)+$` (re.I) als Kennung ein → solche Titel werden NICHT automatisch übersetzt (betraf „Weltraum-Abwehr“,
„Umwelt-Simulationsspiel“, „Varianten-Editor“; „3D-Punktefresser“ entgeht wegen der führenden Ziffer). „Klötze stapeln“ fehlte schlicht
im Wörterbuch. Zur Laufzeit zusammengesetzte Statuszeilen der 3D-Spiele (z. B. „Weiß am Zug (Sie)“, „Schlagzwang – …“, „Schach! …“,
„Schachmatt – …“, „Patt – unentschieden“, „Remis (…)“) tauchen als ganze Knoten auf und müssen als vollständige Zeichenketten
vorliegen. LÖSUNG: Vorrang-Übersetzungen in sprache/werkzeuge/<datei>.json „texte“ (umgeht kandidat; woerterbuch() merged texte direkt
ins Wörterbuch) – angelegt für kloetze-stapeln, weltraum-abwehr, umwelt-simulationsspiel, 3d-dame, 3d-schach. Schafkopf-Ansagen als
{0}/{1}-Muster („{0}: Sauspiel auf die Schellen-Sau“, „{0} sagt weiter.“, „Aktuell: {0} – {1}. Ihr Spiel?“, „{0} bekommt den Stich.“,
Ergebnis) – die Laufzeit übersetzt eingesetzte Werte erneut; Sitzname „West“ bleibt „West“ (korrektes Englisch, im i-/unchanged-Block).
Der Prüfstand-Restefinder meldet „West“ dennoch – benigne, wie Personen-/Firmennamen, Adressen, Pfade und Modelldaten der übrigen
Werkzeuge. Regel: Spiele/Werkzeuge mit Bindestrich-Titel oder laufzeitgebauten Texten dort „texte“ ergänzen und per Rauchtest DE+EN prüfen.

GEPRÜFT + DEPLOY 27.10.2026: Stufe A 0 Fehler/0 Warnungen; Rauchtest DE 61 geprüft/0 Fehler (nur benigne „Kein Beispiel-Knopf“,
unterdrückt); Rauchtest EN 61/0 (alle verbliebenen Warnungen benigne); Stufe D1 api_test 707/0; Stufe E Schnittstellen 78 geprüft,
69 ok, 0 Fehler/0 Warnungen (9 n/a = Spiele ohne Schnittstelle); Test-Build zweimal „keine defekten internen Links“ (382 Seiten).
Deploy auf die Testdomain <test-domain> über den selbst gestarteten und wieder beendeten SFTP-Helfer: 408 Dateien, 0 Fehler,
0 Löschungen, 96,6 s; online byte-genau (skat/3d-dame/game-skat.jpg). Der Live-Build hat nur die VORBESTEHENDEN defekten Links der
Editor-Tool-Blogbeiträge (19 Tools × DE/EN; liegen in website-test statt website/downloads – Peters offene Aufgabe), KEINE
spielbezogenen; Live wurde NICHT deployt (Deploy-Ziel war die Testdomain).

ALLE BLOG-BEITRÄGE AUF TEST VERÖFFENTLICHT (27.10.2026, Peters Auftrag „Veröffentliche alle noch nicht veröffentlichten Blog-Einträge und
deploye sie auf die Testdomain“): Von den 83 Beiträgen waren 82 im Test-Build bereits veröffentlicht (Live-Entwürfe mit Test-Overlay
veröffentlicht); einziger Rest war 2026-10-03-tool-ressourcengarten (DE+EN, bewusst ohne Testfassung). Dafür Test-Overlays
generator/test/blog[/en]/2026-10-03-tool-ressourcengarten.md mit status veröffentlicht angelegt + _teststand.json basis (Live-Dateien
unverändert = test-first; generator/blog NICHT angefasst). Die 34 Tools-/Games-Beiträge bleiben in den LIVE-Quellen Entwürfe (alle
ki_hinweis Claude, keine ChatGPT-/Rechtstexte) – Veröffentlichen auf der Live-Seite ist Peters Schritt (Werkzeuge nach website/downloads
kopieren + Live-Deploy). Test-Build danach 384 Seiten, 83 von 83 Blogbeiträgen, keine defekten internen Links (zweimal). Deploy auf
<test-domain> über den selbst gestarteten/beendeten SFTP-Helfer: 384 Dateien, 0 Fehler, 0 Löschungen, 86,4 s; online byte-genau
geprüft (blog/tool-ressourcengarten.html DE+EN = 200). Hinweis: Der Pipe-Abbruch durch `Select-Object -First N` hinter einem laufenden
`py …`-Deploy killt den Client, bevor „start“ gesendet wird (kein Upload) – Deploy-Ausgabe nie früh abschneiden.

BEIDE DOMAINS ANGEGLICHEN + LIVE-DEPLOY (27.10.2026, Peters Auftrag „Testdomäne und Produktivdomäne haben den gleichen Zustand, Inhalt und
Downloads … alle Blog-Beiträge sind auf beiden Domänen freigegeben“; Live-Passwort einmalig von Peter, nur als flüchtige Umgebungsvariable,
nirgends gespeichert). Vorbereitung: alle 34 Tools-/Games-/Website-Testfassungen in die LIVE-Quellen übernommen (generator/blog[/en] + seiten +
downloads.json/.en.json; website/downloads aus website-test gespiegelt, 16 → 71 Dateien; Gehirn neu; SEO: Beschreibungen ≤ 160, EN-Titel ≤ 54,
Tippfehler experimentell-Feedback-Mailto NICHT geändert = Peters offener Punkt) → beide Builds byte-gleich, 384 Seiten, 83 von 83 Beiträgen, keine
defekten Links. Voller Testdurchlauf grün (A 0/0; B DE 61/61, EN 0 Fehler; C 0/0; D1 707/0; E 69 ok). Test-Deploy <test-domain>: 386 Dateien,
0 Fehler, 0 Löschungen. LIVE-Deploy www.my-coaching4you.de (user <sftp-benutzer>, GLEICHER STRATO-Host wie Test <sftp-host>):
512 Dateien hochgeladen (413 neu – komplette EN-Seite, neue Blog-/Tool-/Spiele-Beiträge, neue Download-Struktur –, 99 geändert), 0 Fehler
(sftp/protokolle/2026-10-27_15-56-16_live_hoch.txt). 18 alte Dateien auf Live gelöscht (feste geprüfte Liste in scratchpad delete_old.py: alte
flache downloads/*.html + 2 eBooks → jetzt per 301 auf downloads/ressourcen|experimentell, 1 Werkzeugkopie im Wurzelverzeichnis, verwaistes
assets/img/pm1.jpg, .sync-exclude.lst; images/ und blog/files/ geschützt, nicht angefasst). 301-Weiterleitungen alt → neu online geprüft.
WICHTIG – AUTO-MODUS-SPERRE: Das Löschen auf dem Produktivserver über den Helfer-Plan (loeschen:true) wurde vom Auto-Modus-Classifier als
„Unverifiable Deletion Scope“ geblockt; erst ein EIGENES Skript mit FEST verdrahteter, geprüfter 18er-Pfadliste (und Peters „Option 1“) lief durch.
Lehre: Produktiv-Löschungen nur mit expliziter, sichtbarer Pfadliste – nicht über die berechnete Plan-Auswahl des Helfers. SEO-FIX (gleicher Tag,
nach dem Deploy entdeckt): Das JSON-LD-Feld „image“ (ProfessionalService/Person) auf JEDER Seite zeigte auf
https://www.my-coaching4you.de/images/LichtbildHoenig---ueberarbeitet_120x173.jpg → 404 (die Datei liegt nicht im Projekt; website/images/ ist leer,
„images“ nur in der Schutzliste gegen Löschen). Das echte Porträt liegt in website/assets/img/lichtbildhoenig-ueberarbeitet-120x173.jpg (= lichtbild-<benutzer>.jpg,
4585 B, 200). Behoben in generator/core.py (JSON-LD image) und generator/pages_main.py (PORTRAIT) → beide zeigen auf assets/img/lichtbildhoenig-
ueberarbeitet-120x173.jpg; der sichtbare `<img>` auf ueber-<benutzer>-hoenig nutzt schon assets/img/lichtbild-<benutzer>.jpg (unverändert). Beide Fassungen neu
gebaut (0 Reste der alten URL) und erneut deployt (Test 384, Live 384 geänderte HTML, je 0 Fehler; sftp/protokolle/2026-10-27_16-09-22_test_hoch.txt,
…_16-11-22_live_hoch.txt); neue URL auf beiden Domains im Schema, Bild 200. OFFEN FÜR PETER: (1) Das TLS-Zertifikat von www.my-coaching4you.de ist am
27.10.2026 00:59 Uhr ABGELAUFEN (Sectigo DV über STRATO) – Besucher sehen eine Sicherheitswarnung; im STRATO-Kundenbereich erneuern (kann ich nicht,
keine Hoster-Adminrechte). (2) Live-Blogquellen der 34 Tools-/Games-Beiträge bleiben Entwürfe = sie sind über die Test-Overlays veröffentlicht und
auf BEIDEN Domains online, aber generator/blog bleibt Entwurf (test-first; endgültige Live-Freigabe der Quellen wäre Peters Schritt, ändert online nichts).

TASKLEISTE: ALLE KATEGORIEN BEIM ZUORDNEN/VERSCHIEBEN VON PROGRAMMEN (27.10.2026, Peters Fund direkt nach dem Live-Deploy „Warum werden für
das Zuordnen und Verschieben von Programmen nicht alle Kategorien zur Auswahl angeboten"; Taskleiste 1.4.2 → 1.4.3, SUITE_VERSION 0.4.11 →
0.4.12). URSACHE: In mc4y-suite/taskleiste/mc4y_taskleiste.py filterten das Rechtsklick-Untermenü „➜ In andere Kategorie" (programm_menue) UND
das Kategorie-Auswahlfeld im Programm-Dialog (programm_dialog) mit `k["art"] in ("sys","eigen")` → die allgemeine Gruppe „💻 Programme" (art
'lokal') fehlte: ein Programm ließ sich nicht in die Programme-Gruppe zurückschieben, und beim Anlegen war sie nicht wählbar. Zusätzlich verwarf der
Speicher-Guard `if not key` einen gewählten LEEREN Schlüssel (""=Programme-Gruppe). BEHOBEN: Untermenü bietet jetzt alle übrigen Kategorien + „💻
Programme" (nur wenn das Programm derzeit in einer echten Kategorie liegt, Guard `(p.get("kat") or "") in echte`); Dialog-Dropdown hat ("", „💻
Programme") als erste Wahl; beide fertig()-Guards auf `if key is None` geändert (""=gültig, None=abgebrochen); programm_kategorie(p, "") schiebt in die
allgemeine Gruppe. REGRESSIONSTEST NEU tests/taskleiste_kategorien_test.py (TL-K27 … K30b): baut die echte Taskleiste mit vorgegebener Toolbox ohne
Suite/Netz (oberflaeche_starten(testdaten=…) gibt die App OHNE mainloop zurück), liest die ECHTEN Menü-/Dialog-Einträge aus (tk.Menu/OptionMenu) und
prüft, dass alle Kategorien inkl. „💻 Programme" angeboten werden, das aktuelle Kategorie korrekt ausgelassen ist und das Speichern mit leerem
Schlüssel wirklich ablegt (kat==""). 7/7 grün. LEHRE (Peters berechtigte Kritik am Testen): Der Rauchtest prüfte nur „lädt ohne Absturz" – Bedienpfade
wie Zuordnen/Verschieben wurden nie durchgeklickt; solche Menüs/Dialoge gezielt gegen die echten Widgets testen. Versions-Kaskade (Q-30): kern.php
SUITE_VERSION, installer.html Demo-Version und die „MC4Y-Suite-0.4.11"-Beispielpfade in install/windows|macos (README/LIESMICH) und in den vier
Blog-Beiträgen tool-mc4y-suite-installieren (DE/EN, live+test) auf 0.4.12 gezogen; `py mc4y-suite/bauen.py` + `paket_bauen.py` in website UND
website-test (mc4y-suite.zip Fassung 0.4.12, 59 Werkzeuge). Stufe A danach 0 Fehler/0 Warnungen. Deploy beider Domains und Neuinstallation der
Taskleiste auf Peters PC: siehe Protokolle des 27.10.2026 (zweiter Durchgang). Der gelöschte assets/img/pm1.jpg war NICHT das Blog-Bild (das ist
blog/files/pm1.jpg, geschützt) – 0 Verweise in gebauten Seiten, keine Regression.

PHP-SICHERHEITSUPDATE + AUTO-PHP IM INSTALLER (27.10.2026, Peters Frage zu den PHP-RCE-Lücken vom 21.10.2026; SUITE_VERSION 0.4.12 → 0.4.13).
ANLASS: CVE-2026-109618 / CVE-2026-109619 (unauthentifizierte RCE, CVSS 9.2) betreffen PHP bis 8.5.11; behoben in 8.5.12 (Windows-Rebuild
8.5.12-1 mit Brotli-Fix CVE-2026-111497). Betroffen waren die lokalen PHP-Installationen, die ich nutze: C:\PHP (PC, 8.5.11) und E:\PHP
(<server>, 8.5.11). Strato (Website) ist davon NICHT betroffen (dort fixt der Hoster; schon erledigt). <server> ist netz-erreichbar (IIS/
FastCGI über HTTPS) = eigentliches Risiko; der PC lauscht nur auf 127.0.0.1:8090 (localhost, geprüft) = geringes Risiko. <server> AKTUALISIERT:
PHP 8.5.11 → 8.5.12 am 27.10.2026, Sicherung E:\PHP-alt-20261027-173013; danach HTTPS 200/301/403, Zertifikat ok (suite_installation_pruefen
--server 0 Fehler). Die Suite-ANWENDUNG auf <server> bleibt 0.4.11 (nur PHP getauscht) – daher die bekannte Warnung „Server 0.4.11 < Projekt".
NEUES WERKZEUG mc4y-suite/install/windows/php-aktualisieren.ps1 (reusable, PS5.1/ASCII, Parser-geprüft): holt – ohne -PhpZip – die neueste PHP-Reihe
-Zweig (Standard 8.5) als NTS-VS17-x64-Build von php.net und VERIFIZIERT per SHA-256 aus downloads.php.net/~windows/releases/sha256sum.txt
(Format „<sha> *<datei>"; höchste Version inkl. -N-Rebuild); lokal ODER per WinRM (-Server, Get-Credential, UPN/OpenTimeout wie
auf-server-installieren.ps1). Tausch reversibel: App-Pool/Aufgabe anhalten, verbliebene php(-cgi).exe aus dem Ordner beenden, Ordner nach
<Ordner>-alt-<Zeit> sichern, neuen Build einsetzen (vorhandene php.ini bleibt; auf dem Server gibt es keine – Konfig via PHPRC), starten,
`php -r PHP_VERSION` prüfen; stimmt die Version nicht, automatischer Rückroll. IDEMPOTENT (schon aktuell → kein Download, kein Tausch). NIE eine
ungeprüfte Datei (ohne SHA-Treffer/-Angabe = Abbruch). -Probelauf zeigt nur den Plan. INSTALLER-ANBINDUNG: installieren.ps1 (Einzelplatz, ruft es mit
-PhpOrdner $PhpZiel -Aufgabe "MC4Y-Suite") und installieren-server.ps1 (Server, -PhpOrdner $PhpDir -AppPool "MC4Y-Suite") führen nach dem Start/
der Prüfung der Suite „PHP auf die neueste 8.5-Version" aus (abschaltbar mit -OhnePhpUpdate; in -Probelauf nur Hinweis). Damit bringt künftig JEDES
-Aktualisieren automatisch das neueste gepatchte PHP, auch der grafische Installer (ruft diese Skripte). GEPRÜFT: php-aktualisieren.ps1 lokaler
Probelauf gegen C:\PHP (findet 8.5.12-1, erkennt 8.5.11, Plan, nichts geändert) und Auto-Neueste (lädt+verifiziert); <server> echt aktualisiert;
installieren.ps1 -Aktualisieren -Probelauf zeigt den neuen Schritt und endet sauber; beide Installer Parser-OK. MACOS: KEIN sicheres Auto-Update –
static-php.dev veröffentlicht KEINE Prüfsummen und kein Versions-Manifest; eine unverifizierte Binärdatei wird nicht geladen. Der Mac-Installer bleibt
beim angepinnten, per SHA-256 geprüften static-php-Build (MAC_PHP_VERSION), der je Sicherheitsrelease von Hand hochzuziehen ist (wenn ein gepatchter
8.4-Build ≥ 8.4.27 mit verifizierbarer Prüfsumme vorliegt). OFFEN: Peters Notebook später gemeinsam updaten (sofern nicht über die Installer-Routine);
der PC (C:\PHP, Einzelplatz-Aufgabe) wird beim nächsten `installieren.ps1 -Aktualisieren` automatisch mitgezogen – oder vorab per
`php-aktualisieren.ps1 -PhpOrdner C:\PHP -Aufgabe MC4Y-Suite` (mit Peters OK/UAC). Die INSTALLIERTEN Suiten (PC/<server>) tragen diese Installer-
Änderungen erst nach einem Suite-Update auf 0.4.13.

TASKLEISTE: AUCH SPIELE/WERKZEUGE IN ALLE EIGENEN KATEGORIEN (27.10.2026, Peters Fund „ein Spiel in eine andere Kategorie verschieben – nicht
alle Kategorien tauchen auf"; Taskleiste 1.4.3 → 1.4.4, SUITE_VERSION 0.4.13 → 0.4.14). URSACHE: Mein Kategorie-Fix vom selben Tag betraf nur LOKALE
PROGRAMME (programm_menue/programm_dialog). SPIELE sind Katalog-WERKZEUGE; deren Rechtsklick-Menü ist zeile_menue (zeile() ruft es), ein ANDERER
Codepfad. Dort war die Liste der Ziel-Kategorien auf `[:8]` gedeckelt → bei mehr als 8 eigenen Kategorien fehlten welche. BEHOBEN: `[:8]` entfernt
(zeile_menue bietet jetzt ALLE eigenen Kategorien an, in denen das Werkzeug noch nicht liegt, ohne die aktuelle; zusätzlich „➕ In neue eigene
Kategorie …"). Hinweis zum Datenmodell: Ein Katalog-Werkzeug kann NUR in EIGENE Kategorien aufgenommen werden (werkzeug_in → L["eigene"]); SYS-/
Katalog-Kategorien sind serverseitig definiert und erscheinen daher im Werkzeug-Menü bewusst nicht (anders als bei Programmen, die ein einzelnes
`kat` haben und auch in SYS-Kategorien dürfen). Gleiche Deckelung `[:10]` bei der Liste der wieder einzublendenden Kategorien (kat_menue, griff_menue)
ebenfalls entfernt. REGRESSIONSTEST erweitert: tests/taskleiste_kategorien_test.py TL-K31/K31b baut 10 eigene Kategorien, legt ein Spiel in eine und
prüft, dass das echte zeile_menue alle 9 übrigen + „neue eigene Kategorie" anbietet (9/9 grün). LEHRE (erneut, [[rauchtest-ist-kein-bedientest]]):
Sibling-Pfade mittesten – Programme UND Werkzeuge haben getrennte Menüs. Versions-Kaskade wie bei 0.4.12/0.4.13; Deploy beider Domains + Neuinstallation
der Taskleiste auf Peters PC (1.4.4) im zweiten Durchgang des 27.10.2026.

TASKLEISTE: VÖLLIG FREI KONFIGURIERBAR + VOLLTEST (27.10.2026, Peters Auftrag „eine völlig frei konfigurierbare Taskleiste, die jeder Anwender nach
seinen Wünschen zusammenstellt" und „vernünftige Testplanung … auf ALLE Module anwenden, Fehler selbstständig beheben, GEO/SEO, Gehirn, Deployment
Test und Live"; Taskleiste 1.4.4 → 1.4.5, SUITE_VERSION 0.4.14 → 0.4.15). JEDES Werkzeug (auch Suite-Werkzeuge und Spiele) lässt sich jetzt in
BELIEBIGE Kategorien verschieben (Suite-, eigene oder die neue Catch-all-Gruppe „Weitere"), umbenennen und aus der Leiste nehmen (per Suche
wiederfindbar); der Katalog ist nur die Startkonfiguration. Peters Entscheidungen (AskUserQuestion): je Konto (Overrides mit dem Konto synchronisiert;
Werkzeug bleibt in der Suite, „entfernen" = nur aus der Leiste); Desktop UND Portal; neue Werkzeuge → „Weitere"; Prüfstand über ALLE Module erweitern.
SERVER (app/konto.php leiste_lesen): benutzer.ansicht.leiste zusätzlich werkzeuge [{id, kat '' | 'k<id>' | 'e<id>' | 'weitere', name, aus}] = Override
je Werkzeug und bekannt [ids] = Schnappschuss aller Katalog-Werkzeuge beim ersten Umbau (umgebaut = bekannt.length>0); leiste_lesen validiert (id-Regex
`^[a-z0-9][a-z0-9_-]{0,63}$`, kat aus {'', weitere, k\d{1,9}, e<vorhandene>}, name = leiste_text 60, Einträge mit ≤1 Schlüssel fallen weg, max 2000),
abwärtskompatibel (alte Clients ignorieren die neuen Felder). WICHTIG: leiste_lesen initialisiert jetzt ALLE fünf Schlüssel (reihenfolge/aus/eigene/
werkzeuge/bekannt) – vorher lieferte der frühe `return $out` bei !is_array nur drei (hatte im api_test 5 Fehler erzeugt, bis alle fünf oben gesetzt wurden).
PORTAL (portal.js, neu geschrieben): ovHole/ovSetzen (leert kat/aus/name, verwirft Einträge mit ≤1 Schlüssel), bekanntSetzen, heimKat(L,id) = Override.kat
→ Eintrag einer eigenen Kategorie → Katalog → 'weitere'/''; kategorienGeordnet baut die Gruppe „Weitere" (✨) nur wenn nicht leer; werkzeugVerschiebenKat/
-Umbenennen (leerOk)/-AusLeiste/-Standard; Rechtsklick-Menü kMenueAuf bietet alle Zielkategorien (sys/eigen/Weitere außer der Heimat) + „✎ Umbenennen",
„↺ Zurück zum Standard", „✕ Aus der Leiste nehmen"; ALLE Titel-Anzeigen (Kacheln, Zeilen, Flyout, Suche, „Zuletzt bearbeitet") über wzTitel(id) – sonst
zeigte das Umbenennen weiter den alten Namen. Ende-zu-Ende im Browser gegen eine Wegwerf-Suite geprüft (Verschieben/Umbenennen/Entfernen/neue Kategorie,
je mit Neuladen-Persistenz, 0 Konsolenfehler). DESKTOP (mc4y_taskleiste.py 1.4.5): EN-Wörterbuchblock neu (`py sprache/taskleiste.py schreiben`).
PRÜFSTAND: tests/taskleiste_kategorien_test.py erweitert (… TL-K37 neues Katalog-Werkzeug landet in „Weitere", TL-K38 Spiel-Menü bietet Suite- UND
eigene Kategorien + Umbenennen/Entfernen – gegen die ECHTEN tk.Menu/OptionMenu-Einträge, 16/0); api_test Override-Block (712/0).
BILINGUALER FUND (Klötze stapeln / Games): die Spiele-Beschreibungen erschienen im englischen Portal UND in der englischen Desktop-Taskleiste deutsch;
behoben durch Einträge in sprache/texte/0110.json (woerterbuch merged texte direkt) – `Klötze stapeln` → „Stack the Blocks" usw. Lint-Hinweise „Games"
(Kategorie – in beiden Sprachen gleich) und „Startmenü ›" (Zeile 1179, nur interner .startswith-Schlüsselvergleich; „Startmenü › {0}" ist in
taskleiste-en.json übersetzt) sind Falschmeldungen.
IN-W09 / PHP-REBUILD-SUFFIX (echter Produktionsfehler, 27.10.2026): Der grafische-Installer-Helfer installer-dienst.ps1 Get-PhpRelease verglich
PHP-Versionen mit `[version]$r.version`. php.net liefert den neuesten Windows-Build jetzt mit Rebuild-Suffix (8.5.12-1, Brotli-Fix); `[version]"8.5.12-1"`
WIRFT, der umgebende `catch{}` verschluckte es → Get-PhpRelease lieferte null → der grafische Installer bot bei Neuinstallation KEIN PHP an
(Windows/Server). Fix: Vergleich auf die um `-N` bereinigte Version (`[version](([string]$r.version) -replace '-\d+$','')`), $beste trägt die bereinigte
`vn` zum Vergleich und die volle Zeichenkette (8.5.12-1) zur Anzeige; Download-URL/SHA kommen aus dem JSON-Eintrag, unberührt. Nur der Helfer war betroffen
(die Haupt-Installer nutzen das rebuild-bewusste php-aktualisieren.ps1). installer_test IN-W09 fand ihn (Fernwartungs-Planung ohne php_fern); jetzt 67/0.
VOLLTEST-PROTOKOLL: A statisch 0 Fehler/0 Warnungen (70 bekannte Blog-Hinweise = Peters alte 2024/25-Beiträge), B Rauchtest 61/61 (DE-Laden + Ansichten
+ Exporte + 375 px + englische Reste in EINEM Lauf, 0 Warnungen/0 Fehler), C Website-Links live/test 0/0, D1 api_test 712/0, D7 installer_test 67/0,
Taskleiste Selbsttest 7/0 · Verbindung 35/0 · Kategorien 16/0 · Sprache 29/0 · Bildschirm 19/0, E Schnittstellen 78 → 69 ok/0 Fehler/9 n/a (Spiele ohne
Schnittstelle). taskleiste_oberflaeche_test (klickgesteuerter GUI-Test) läuft in DIESER Agent-Umgebung NICHT durch: „Zeitüberschreitung vor s4" –
die synthetischen Klicks (druecken() → event_generate) erreichen die Tk-Handler hier nicht zuverlässig (`len(aufrufe)>0` wird nicht wahr), variable
Fortschritte (23/30 von 52) = Umgebung, keine Regression (Taskleisten-Änderung war wörterbuch-only; dieselbe App besteht den widget-basierten
Kategorien-Test). Auf Peters echtem Desktop war der Test 52/52 – dort bei Bedarf erneut bestätigen; NICHT den Timeout erhöhen, um ihn grün zu zwingen.
Pakete 0.4.15 neu (bauen.py: 59 Werkzeuge, Client-Zip; paket_bauen.py: mc4y-suite.zip 208 Dateien, taskleiste/werkzeuge/erkennung/homeassistant).
Dabei entfernte bauen.py eine Nextcloud-Konfliktkopie aus public/werkzeuge (umwelt-simulationsspiel …); sonst keine Konfliktkopien in Build-Bereichen.
SEO/GEO: diese Sitzung legte KEINE neuen Seiten/Beiträge an → nichts zu optimieren (die 70 A-Hinweise sind Peters alte Beiträge).

FASSUNG 0.4.11 + VERSIONS-/PAKETTEST (27.10.2026, SUITE_VERSION '0.4.11 (Stufe 4)', Schemata unverändert): Der Benutzername-Fix
(<domaene>\<benutzer> → <benutzer>@<domaene> in install/windows/auf-server-installieren.ps1, NetBIOS-Form → UPN der Server-DNS-Domäne)
und die Spiele im Installer-Katalog (Kategorie „Games“) waren zuvor unter der UNVERÄNDERTEN Nummer 0.4.10 ausgeliefert – zwei
verschiedene 0.4.10, an der Nummer nicht unterscheidbar (Peters Fund). Darum Version hochgezählt und ALLE Versionsangaben angeglichen:
mc4y-suite/app/kern.php (maßgeblich), installer.html-Demoversion (paket.version), die „MC4Y-Suite-<Fassung>“-Beispielpfade in
install/windows|macos/README+LIESMICH und in den Blog-Beiträgen tool-mc4y-suite-installieren (DE/EN, live + test). NEUER PRÜFSCHRITT
tests/statisch_pruefen.py Stufe A „7. Versionen und Pakete“ (versionen_pruefen) fängt genau diese Fehlerklasse: Installer-Demoversion =
SUITE_VERSION; keine veralteten MC4Y-Suite-<Fassung>-Beispiele in mc4y-suite/** und generator/blog/**; website-test/.../mc4y-suite.zip
auf dem Stand der Quellen (Wurzel MC4Y-Suite-<Version>, kern.php-Version im Paket, app/kern.php + app/katalog.json + install/** wörtlich
mit {VERSION}-Ersetzung wie paket_bauen.py, Katalog-Kategorie „Games“ und gleiche Spiele-Anzahl); Anzeigenamen der Spiele und der drei
Suite-ZIPs in downloads.json/downloads.en.json; „Gehirn“ neuer als CLAUDE.md und frei von sensiblen Angaben (DENY-Liste). Dazu die drei
Suite-ZIPs mit deutschem Anzeigenamen in generator/test/downloads.json. REGEL: Ändert sich der Inhalt des Suite-Pakets (Werkzeuge, Spiele,
Skripte, Fixes), SUITE_VERSION in kern.php hochzählen UND neu paketieren (py mc4y-suite/bauen.py + paket_bauen.py) – sonst tragen zwei
verschiedene Pakete dieselbe Nummer. Testprotokoll in tests/TESTPLAN.md Abschnitt 9; Testfälle Q-30 (Versions-/Paketabgleich), Q-31 (Gehirn).

SUITE-PAKETE MIT DEN SPIELEN (27.10.2026, Peters Fund „im Installer keine Spiele“): Beim Einbauen der Spiele am 27.10. wurden zwar
editoren-version.py, mc4y-suite/bauen.py (→ app/katalog.json Kategorie „Games“ + public/werkzeuge/<spiel>.html) und der Website-Deploy gemacht,
ABER `paket_bauen.py` NICHT erneut gelaufen – das herunterladbare mc4y-suite.zip/mc4y-werkzeuge.zip (Fassung 0.4.10, 26.10.) hatte daher KEINE
Kategorie „Games“ und 0 Spiele, der grafische Installer (liest app/katalog.json aus dem Paket-Wurzelordner, installer-dienst.ps1 Zeile ~263) zeigte
sie nicht. Am 27.10. behoben: `py mc4y-suite/bauen.py` (räumt den „weicht vom Einzel-Download ab“-Hinweis bei 6 Spielen) + `py mc4y-suite/paket_bauen.py`
→ mc4y-suite.zip jetzt 59 Werkzeuge, Kategorie „Games“ + 9 Spiele + 9 Spiel-HTML; mc4y-werkzeuge.zip Startseite mit „Games“ + 9 Spiel-HTML (geprüft im
ZIP). LEHRE: Neue Werkzeuge/Spiele erscheinen im Installer erst nach `paket_bauen.py` UND Deploy des Pakets – der Website-Deploy (Blog) reicht nicht.
Deploy auf <test-domain> am 27.10.2026 (nur geänderte, bauen:False – nur die Pakete änderten sich): mc4y-suite.zip + mc4y-werkzeuge.zip,
2 Dateien, 0 Fehler, 0 Löschungen (Protokoll sftp/protokolle/*_test_hoch.txt). Online byte-genau geprüft: Installer-Paket Kategorie „Games“ + 9 Spiele
+ 9 Spiel-HTML, Browser-Paket Startseite „Games“ + 9 Spiel-HTML. Das Paket trägt zugleich den Benutzername-Fix (<domaene>\name → UPN) in
auf-server-installieren.ps1 (s. Stufe 4, 27.10.2026). Installierte Suiten (PC, <server>) NICHT angefasst. mc4y-taskleiste/-erkennung/-homeassistant.zip
waren byte-gleich → nicht neu hochgeladen.

SPIELE IN DEN DOWNLOADS MIT ANZEIGENAMEN (27.10.2026, Peters Klarstellung „die Spiele gehören selbst in Downloads auf die Testdomain“): Die 9 Spiel-HTML
lagen schon in website-test/downloads/experimentell/software und waren online (seit dem Deploy 27.10.), aber im Download-Overlay OHNE Anzeigenamen – die
experimentell-Seite zeigte sie roh abgeleitet („3d schach“, „Umwelt simulationsspiel“, „Kloetze stapeln“). Jetzt Anzeigenamen je Spiel in generator/test/
downloads.json (z. B. „3D-Schach (Spiel)“, „Skat (Spiel)“, „Umwelt-Simulationsspiel (Spiel)“) und generator/test/downloads.en.json (z. B. „3D Chess (game)“,
„Stack the Blocks (game)“, „Environmental Simulation Game“). Test-Build zweimal (384 Seiten, 83/83, keine defekten Links), ganzer Teststand neu auf
<test-domain> (384 Dateien, 0 Fehler, 0 Löschungen, 97 s); online DE+EN geprüft: alle 9 Spiele mit sauberem Namen unter Experimentell › Software,
Rahmenseiten werkzeuge/<spiel>.html und die Spiele selbst erreichbar. Weiter kein Live-Deploy (Peters Schritt). LEHRE (Peters Rüge): bei solchen klaren
Handgriffen nicht Schritt für Schritt nachfragen; etwaige Rückfragen VOR einem Deploy bündeln und das Deployment nicht minütlich wiederholen.

## Testplan und Prüfstand (seit 17.10.2026)

Peters Auftrag: „Erstelle einen Testplan für das Testen aller Module, den Du immer wieder aktualisierst und nach dem Du auf Aufforderung
alles testet und ggf. gefundene Fehler korrigierst.“ → `tests/TESTPLAN.md` (Ablauf, Modulkatalog mit Testfall-IDs, nur durch Peter prüfbar,
bekannte Meldungen, Testprotokoll). Auf „Teste alles“ (Volltest A–J) bzw. „Schnelltest“ (A, B ohne Handy, C, D1–D3, E) danach vorgehen und das
Protokoll fortschreiben. PETERS VORGABE (17.10.2026): „Genau die Schnittstellentests wären wichtig und natürlich sollen gefundene Fehler behoben
werden.“ → Stufe E (Schnittstellentests) ist in JEDEM Test Pflicht, und gefundene Fehler werden im selben Durchgang behoben (Sicherung, Quellen,
bauen, betroffene Tests + Schnelltest wiederholen, CLAUDE.md/Protokoll nachziehen); an Peter gehen nur inhaltliche/rechtliche Entscheidungen,
Dinge außerhalb des Projekts und alles mit Administratorrechten.
BEI JEDEM NEUEN WERKZEUG/JEDER NEUEN FUNKTION den Plan nachziehen (Abschnitt 6, Sonderfall in tests/pruefstand/werkzeuge.json, Browser-Speicher
in tests/pruefstand/speicher-bekannt.json, neue Übergabe/neues Dateiformat als Schnittstellentest) – wie Datenschutzerklärung und Blog-Entwurf.
- A `py tests/statisch_pruefen.py [--kurz]`: Python-Syntax + 3.8-f-String-Regel (ast; mc4y-suite/taskleiste/*.py (bis 18.10.2026 client/) und uebertragungs-assistent.py
  ganz ohne f-Strings – OHNE_FSTRING), php -l, JSON (auch manifest.webmanifest), Skripte (.sh/.command
  nur LF, .ps1 nur ASCII und fehlerfrei im PowerShell-Parser (ps_parser_pruefen, Ausgabe über Index wegen Umlauten im Pfad), .bat mit LF nur bei
  Sprungmarken kritisch, Suite public/web.config wohlgeformt, public/handbuch.html ohne Skript, mc4y-suite/public/client/mc4y-taskleiste.zip =
  aktuelle Dateien aus client/ – skripte_pruefen), Stempel-Trockenlauf über editoren-version.py
  (Inhalt geändert / Blöcke veraltet / Kopien), NAMES/WERKZEUGE/INFO (Suite-AUSSCHLIESSEN beachtet), Aktualität Quellen → Werkzeug (gestempelte
  Werkzeuge als Quelle zählen mit ihrer Build-Zeit aus editoren-versionen.json, nicht mit der Dateizeit – Stempelläufe schreiben alle Dateien neu;
  TMK-Export-Teil in den Kern-Werkzeugen und Figuren-/Modell-Teil des 3D-Systembretts in den 3D-Werkzeugen werden INHALTLICH verglichen – teile_pruefen)
  und Werkzeug → Suite-Kopie, Blog-Köpfe (Entwurf = Warnung, veröffentlicht = Hinweis; Bilder mit absoluter Adresse nur Hinweis).
- B Rauchtest: Vorschau „pruefstand“ (tests/pruefserver.py, Port 8330, liefert NUR LESEND aus, sperrt private Ordner) → /__pruef/?start=alle
  [&nur=Regex][&mobil=0][&export=0][&ansichten=0]. Je Werkzeug: Stempel, Laden mit haken.js (?pruef=1: Fehler/Promise/console.error, alert/confirm/
  prompt/print/open/Dateidialoge/mailto/Downloads entschärft), Beispiel (werkzeuge.json), NaN/undefined, Überlauf, Ansichten ([data-ansicht],
  [data-tab], [data-act^=ansicht], [data-act^=view:], [role=tab]), Export-Teil (ZIP selbst entpackt, jede XML geparst), 375 px, Browser-Speicher nach
  dem Öffnen (speicherStart) und am Ende (speicher) gegen speicher-bekannt.json. Ergebnis tests/ergebnisse/letzter-rauchtest.json. Verdeckter
  Vorschau-Tab läuft gedrosselt (Volltest dann 30+ Min.).
- C `py tests/website_pruefen.py [--einmal] [--bekannt-aktualisieren]`: Live/Test/Live/Test, Fehlalarm im 2. Lauf = Fehler, fehlende Ziele gegen
  tests/bekannt.json (neue Seite = Fehler, neuer Download = Warnung, bekannt = Hinweis).
- D1 `C:\PHP\php.exe mc4y-suite\tests\api_test.php` (17.10.2026: 400 Prüfungen). D2 `… api_test.php --programm="C:\Program Files\MC4Y-Suite"` =
  dieselben Prüfungen mit den INSTALLIERTEN Programmdateien (app/ und public/ werden nach %TEMP% kopiert, eigener Server, Wegwerf-Daten, ohne
  konfig.php) – Abweichungen = Installation veraltet. D3 `py tests/suite_installation_pruefen.py`: SHA-256-Abgleich Installation ↔ Projekt
  (app, public, install, LIESMICH.md, konfig.beispiel.php) + NUR LESENDE Abfragen an Port 8090 (Status/Version, Portal, ausgelieferte Skripte).
  NIE `api_test.php --ziel` gegen eine eingerichtete Installation (legt Konten an, schreibt Daten). D4 `py tests/suite_installation_pruefen.py
  --server https://<server>` = Server-Installation NUR LESEND (Status über https, http → 301, API über http → 403 „https“,
  app/kern.php + konfig.php ohne Inhalt, zertifikat/mc4y-suite.cer; nicht vertrautes Zertifikat = Warnung). D5 = Installer-Probeläufe (TESTPLAN SU-S03).
  Desktop-Taskleiste (TL-V01…V10, TL-A01): `py tests/taskleiste_verbindung_test.py [--server https://<server>/]` – Wegwerf-Einstellungen
  (MC4Y_TASKLEISTE_ORDNER, bricht beim echten bzw. fremden Ordner ab), nachgebaute Suiten, öffnet kurz Fenster; --server = nur POST status (< 2 s).
  D6 (seit 18.10.2026) zusätzlich gegen eine WEGWERF-Suite (tests/wegwerf_suite.py: Suite des Projekts, leerer Datenordner in %TEMP%, freier Port,
  Konto admin mit Zufallskennwort, räumt ab): `py tests/taskleiste_selbsttest.py` (SU-G04, --selbsttest 7/7) und `py tests/taskleiste_oberflaeche_test.py
  [--ohne-bilder]` (SU-G05/L04/L05: TL-O01…24 Leiste, TL-K01…19 Kategorien/Programme; Programmstart/Browser abgefangen, Bilder der eigenen Fenster nach
  tests/ergebnisse/taskleiste-bilder/). Keine fremden Kennwörter, nie die Entwicklungs-Suite oder Peters Installation. D5 umfasst SU-P01 (Pakete in einen
  Scratchpad-Ordner bauen, mit den veröffentlichten vergleichen, Probeläufe aus dem entpackten Paket, entpacktes public mit leerem Datenordner).
  D7 (seit 18.10.2026) `py tests/installer_test.py`: grafischer Installer (Helfer Windows + Linux/macOS-Helfer unter Windows) – installiert nur „Nur im
  Browser“ in einen Wegwerf-Ordner, sonst Planen/Probelauf; dazu Vorführung installer.html#demo=… im Prüfstand.
- E Schnittstellentests (Pflicht): Vorschau „pruefstand“ → /__pruef/schnittstellen?start=alle[&nur=Regex] (tests/pruefstand/schnittstellen.html,
  schnittstellen.js, schnittstellen_uebergaben.js; Ergebnis letzter-schnittstellen.json). haken.js stellt bei window.top.__pruefFS ein Dateisystem im
  Arbeitsspeicher bereit (showSave/OpenFilePicker, Dateifeld per DataTransfer, Downloads) – Werkzeuge werden in Rahmen gleicher Herkunft über ihre
  Knöpfe bzw. contentWindow.eval auf interne Funktionen bedient. DATEI-<werkzeug> (Speichern → Öffnen → Speichern ohne Verlust, normalize
  idempotent; werkzeuge.json › datei.vorbereiten/fluechtig), 2D3D-<paar>-ab2d/-ab3d (Inhaltsvergleich: Kennungen → Beschriftungen, Lage/Reihenfolge
  egal), UE-01…07 (PM → BH, Vertrieb → KN/BH/PM, KN → BH, KN → Vertrieb, 8EM ↔ PM, 8EM → UML, Fragebögen → 8EM), SA-01 (Stammdaten-Anschluss
  Kanban). Dauer 20–40 Min. (verdeckter Tab gedrosselt). Rauchtest und Schnittstellentest NICHT gleichzeitig laufen lassen (gemeinsamer
  Browser-Speicher des Prüf-Ursprungs).
- Installierte Suite aktualisieren: installieren.ps1 -Aktualisieren sichert seit 17.10.2026 vorher system/suite.sqlite (+ -wal/-shm; Datenordner
  aus der installierten konfig.php) nach <Datenordner>\_update-sicherung\<Zeit>_vor-<Version>, behält die letzten -Sicherungen n (Standard 10),
  bricht ab, wenn die Sicherung fehlt; -Probelauf zeigt es ohne Änderung. Nur mit Peters OK (Administrator, Windows-Rückfrage).
Ergebnisse in tests/ergebnisse/ (darf gelöscht werden).
FUNDE UND KORREKTUREN DES ERSTEN DURCHGANGS (17.10.2026, Sicherungen editor-backups/2026-10-17_*): (1) Handybreite: 31 Werkzeuge liefen bei
375 px rechts über (Grid mit festen Spalten) → Block @media (max-width:600px) vor dem letzten </style> (eine Spalte: Kopf, Zeichenfläche 70vh,
linke und rechte Leiste darunter, Trennleisten/Laschen aus) in den 12 2D-Coaching-Werkzeugen, werkzeug-kern/kern.html, werkzeug-kern-3d/kern3d.html,
3d-systembrett-editor.html; Mindmap-Werkzeuggruppen umbrechen und Eigenschaftenleiste .props grid-template-columns auto minmax(0,1fr),
Lebensrad-Auswahl max-width, Ergebnistabellen der Fragebögen EAM-Reifegrad/IT-Erhebung waagerecht scrollbar. (2) Stammdaten-Editor: Ansicht „Anschluss“ legte in der Beispielansicht eine leere Datenbank an →
s6_start.js werkzeugListeLesen() (nicht in ui.nurAnsicht, nur wenn die Datenbank schon existiert). (3) Kunden-Navigator übernahm die Belegnummer
einer übernommenen Eingangsrechnung nicht (kc_dokumente.js beStatusLaden wertet jetzt o.belegNr aus). (4) 3D → 2D → 3D verlor Bildname/-notiz
und das Aussehen der Gehfigur → k3_c4.js: export2D hängt extra3d {v, cur, scenes [{id, name, note, walker (WALKER_KEYS)}]} an, parseFile wendet
es nach from2D an (extra3dAnwenden: Zuordnung über Bild-id, bei einem Bild über cur, sonst Reihenfolge; Notiz nur, wenn leer); die 2D-Werkzeuge
(TMK, Bodenanker, Soziales Atom, Lebenslinie, Inneres Team, kern_b.js) bewahren extra3d in normalize. (5) api_test.php ließ nach einem Abbruch
den Wegwerf-Datenordner liegen (offene PDO-Verbindung) → Shutdown schließt PDO-Verbindungen und löscht mit Wiederholung; bei --programm meldet ein
Abbruch „Installation veraltet“. Ergebnis danach: A 0/0, B 52 ok, C 0/0, D1 403/403, E 69/69 (Protokoll in tests/TESTPLAN.md Abschnitt 9).
ZWEITER VOLLTEST (18.10.2026, Peters „Teste jetzt alle Module noch einmal intensiv …“; Sicherungen editor-backups/2026-10-18_*): A 0/0, B 52/52 ok, C 0/0,
D1/D2 503/503, D3 Installation veraltet (8 Dateien), D4 <server> nur der bekannte .cer-Fehler, D5 + SU-P01 ok, D6 7/7 · 36/36 · 43/43, E 69/69; Mausprüfungen
im Suite-Portal (Wegwerf-Suite über einen vorübergehenden launch.json-Eintrag „suite-wegwerf“, Port 8098, Datenordner im Scratchpad) und in UML, PM, Kanban,
Mindmap, Systembrett 2D/3D, Logische Ebenen, Website. Behoben: (1) Browser-Taskleiste `max-width/max-height: calc(100% − 8px)` statt 100vw/100vh (bei fixed =
Fenster ohne Bildlaufleiste; vorher lag die Uhr 7 px unter der Seiten-Bildlaufleiste); (2) leisteLage() setzt Klasse `eng` (nur Symbole, Name im Tooltip),
wenn die Leiste waagerecht mit Namen breiter als das Fenster wäre – Einstellung bleibt „Namen“; (3) `.tl-flyout > input` Breite calc(100% − 4px) (Suchliste
hatte eine waagerechte Bildlaufleiste); (4) hashchange schließt offene Liste und Rechtsklick-Menü der Taskleiste; (5) portal.js modulLaden() merkt das
laufende Laden je Modul (MODUL_LAEDT) – vorher lud ein doppelter Aufruf (direkt nach Einrichten/Anmelden) das Modul-Skript zweimal, die zweite Instanz
zeichnete ohne Seite („Cannot set properties of null“ in arbeitsplatz.js); (6) Mindmap ≤ 900 px: `#side{overflow-y:auto}` und `#notesWrap{flex:1 0 180px}`
(vorher 0–24 px Notizfeld, weil Eigenschaften + Werkzeugleisten die 320 px der unteren Leiste füllten); (7) Mindmap-Tabellen-Raster klappt nach oben auf,
wenn unter dem Knopf kein Platz ist. Leer-Hinweis der 3D-Werkzeuge lag bei 800 px teils unter der Ansichtsleiste – auf Peters „ja“ behoben (Build 11:39, 3D-06).
Neue Prüfskripte und Testfälle siehe D6 sowie MM-05, SU-T19 … T21 im Testplan.
DRITTER VOLLTEST (20./21.10.2026, Peters „Jetzt teste alles noch einmal sehr gründlich“, Auftrag über die Sitzung „8EM-Modell Tool“): Einzelheiten im
Testprotokoll (TESTPLAN Abschnitt 9). Behoben: 8EM-Abfragesprache „kein Interface/Schnittstelle“ (8EM-19), Buchhaltung Tab-Folge (BH-10), 85 englische
Reste (Farbnamen der Farbliste als Einzelwörter in den 9 Brett-Werkzeugen, Ikigai-Kurzwörter, „Zukunfts-Ich“, 8EM-Berichtsnamen, Kunden „morgen/monatlich“,
ITSM-Tooltip „Lösung fällig …“, kurze und lange Monatsnamen im Projektmanagement – als Muster in sprache/werkzeuge/*.json zusatz). Stufen D5/D7/C nach den
gleichzeitigen Änderungen der Sitzungen „Stufe 4“ (Installer, Anleitungen) und FreeHTML am 21.10.2026 wiederholt – alle grün.
Während des Tests arbeitete die Sitzung „Werkzeuge: Deutsch/Englisch“ parallel am Suite-Portal (sprache.py/sprache.js, portal.js, Stempel aller Werkzeuge) –
Abstimmung per Nachricht; sprache/ erst nach ihrem Abschluss angefasst. Für Wegwerf-Suiten im Vorschau-Browser bei paralleler Arbeit einen eigenen Port und
Namen in .claude/launch.json wählen (hier „suite-volltest“, 8099) und danach entfernen.
VIERTER VOLLTEST (21.10.2026 abends, Peters „Teste jetzt noch mal alles gründlich und behebe Fehler ohne Rückfrage“): alle Stufen grün; behoben: englisches
Datumsformat (sprache.js datumEn), Satzzerlegung für zusammengesetzte Meldungen, vier fehlende Texte, Prüfstand-Fehlmeldungen; neu: Mac-Skript-Prüfung (MAC_GNU in
statisch_pruefen.py), Q-24/Q-25. Website-Editor im OPFS-Probeprojekt: Dateiliste per Skript, Dateien vom Prüfserver (127.0.0.1:8330) nach OPFS kopieren, Handle
unter IndexedDB „mc4y-editor“/kv/„root“ ablegen → restoreFolder öffnet das Projekt; neue Seite: Dialog-„close“ von Hand auslösen (Vorschau-Browser). Danach OPFS,
IndexedDB und localStorage des Ursprungs leeren. Einzelheiten im Testprotokoll.
FÜNFTER VOLLTEST (23.10.2026 abends, Phase 4 der Pipeline „Anpassen, testen, Fehler selbstständig beheben, deployen“ über „8EM-Modell Tool“; nichts hochgeladen, keine
Suite installiert): alle Stufen grün. Behoben: (1) Grafischer Installer, Server AUS DER FERNE mit „Nur Probelauf“ – der Windows-Helfer rief auf-server-installieren.ps1
OHNE Probelauf auf (das Skript kennt keinen) = echte Installation; jetzt Start-Ablauf → Probe-Fern (Phase „probe“, nichts gestartet) und installer.html zeigt bei
Fernwartung kein Probelauf-Häkchen; Hinweise nennen den Knopf über knopfName() („Einrichten“, „Aktualisieren“ …, vorher immer „Installieren“), im Probelauf kein
UAC-Hinweis; widersprüchlicher Satz „dann wird automatisch aktualisiert“ entfernt (IN-W12, SU-I19). (2) CMDB › Erkennung: angezeigte Befehle – macOS hieß
`installieren.command` statt `installieren-macos.command`, Windows `.\installieren.ps1` scheitert an der Ausführungsrichtlinie → `powershell -ExecutionPolicy Bypass -File …`;
Befehle brachen bei 375 px nicht um (#cmErk details code); OUI-Absatz englisch (texte in sprache/werkzeuge/mc4y-suite.json – Sätze mit eingesetzten Zahlen findet
der Auszug nicht) (SU-C18). (3) Desktop-Taskleiste: Wörterbuch-Block veraltet (Meldungen der Kennwort-Rücksetzung) → `py sprache/taskleiste.py schreiben` – nach
Änderungen an app/konto.php, geraete.php, kern.php, verwaltung.php oder am Katalog immer `py sprache/taskleiste.py pruefen` laufen lassen (TL-S04). (4) Wörterbuch:
Software-„programme“ → „program“ (Glossar; 32 Werte, Veranstaltungs-/Führungskräfte-„programme“ bleiben), Bericht „Transfer to tax programs“ auch im englischen
Buchhaltungs-Blog. (5) FreeHTML: Kopie der Sprach-Laufzeit aktualisiert, neu gebaut (Build 20:43), Test-Download-Kopien ersetzt. (6) Doku: erkennung/LIESMICH+README
(Crawler gibt es auch auf dem Mac, Entfernen mit -Crawler/--crawler), INSTALLATION/INSTALL Abschnitt 9 (Rücksetzcode statt „zweites Administratorkonto“),
Datenschutzerklärung DE/EN Taschenrechner (Häkchen voreingestellt). Testplan Fassung 11: doppelte Kennungen SU-W01/W02 → Kennwort-Fälle heißen SU-PW01…PW04.
„Pinnnadel“ (Zielbild) ist nach der Rechtschreibreform richtig (wie „Schifffahrt“) – nicht ändern.
Danach auf Peters „Mach 1 und 2“ (direkt in der Volltest-Sitzung): Pakete neu gebaut, beide Suiten aktualisiert – PC 21:29 (installieren.ps1 -Aktualisieren
-OhneBrowser -OhneWarten -Sprache de über ein Hilfsskript mit -Verb RunAs, Sicherung C:\ProgramData\MC4Y-Suite-Daten\_update-sicherung\2026-10-23_21-29-21_vor-0.4.8,
Orte auf C:), <server> 21:31 (auf-server-installieren.ps1 -Aktualisieren -OhneWarten -Benutzer <benutzer>@<domaene> im sichtbaren Fenster, Peter tippte das
Kennwort, Orte auf E:) –, D3/D4 je 0/0; Testdomain-Upload 344 Dateien, 0 Fehler, nichts gelöscht (SFTP-Helfer selbst gestartet und beendet, Protokoll
sftp/protokolle/2026-10-23_21-37-33_test_hoch.txt), Pakete und Werkzeuge online byte-gleich.

## Bauen

- Auf Peters Rechner heißt Python `py` (Python 3.14; `python` ist nur der Microsoft-Store-Platzhalter). Die .bat-Dateien
  versuchen zuerst `py`, dann `python` – funktionieren also. Bei eigenen Aufrufen `py` statt `python` verwenden.
- Live: `python generator/build.py` → `website/`. Test: `python generator/build.py --test` → `website-test/`
  (noindex, robots Disallow, X-Robots-Tag, kein HTTPS-Zwang, keine Domain-Umleitung, gelber Testbalken).
- Erwartete Abschlusszeile: „… Seiten erzeugt (x von y Blogbeiträgen veröffentlicht) … keine defekten internen Links.“
- Build erzeugt außerdem sitemap.xml, robots.txt (KI-Crawler ausdrücklich erlaubt), llms.txt,
  site.webmanifest, .htaccess (HTTPS/www, 301 alter URLs, Blog-Weiterleitungen, CSP, Caching), blog/feed.xml.
- `BASE`/Canonical ist immer https://www.my-coaching4you.de/ (auch in der Testversion).
- Fußzeile (seit 04.10.2026): unter „© … · Impressum · Datenschutz“ die Zeile „Letzte Aktualisierung am <Datum> um
  <Uhrzeit> Uhr“ (p.build-time mit <time datetime>). build.main() ruft core.set_build_time(datetime.now()) – jeder Lauf
  stempelt nur seine eigene Ausgabe (website/ bzw. website-test/). Der Fingerabdruck (mc4y-stand) umfasst nur <main>,
  die wechselnde Uhrzeit löst daher keine „außerhalb geändert“-Meldung aus.
- „Stand: …“ JE SEITE (seit 07.10.2026, Peters Wunsch – vorher überall fest UPDATED = 17.09.2026): build.seitenstand(pages)
  läuft nach expand_downloads, vor render. `generator/seitenstand.json` (Build-Zustand, nicht Peters Inhalt, nicht hochladen,
  nicht ins Update-Zip) merkt je Fassung ("live"/"test") {slug: {kennung, datum}}; kennung = core.inhalt_kennung (sichtbarer
  Text + href/src/alt + Titel + Beschreibung, OHNE Stand-Datum und ohne Kontaktband → reine HTML-Umbauten zählen nicht).
  Regeln: Kennung gleich → max(altes Datum, Angabe); geändert → Angabe („aktualisiert“ aus inhalte/seiten-json bzw. eigener
  Stand wie AGB), wenn neuer als altes Datum, sonst heute; erster Lauf einer Fassung → Angabe oder UPDATED; neue Seite →
  Angabe oder heute. Blog (blog/…) wird nicht geführt (eigene Daten). Ergebnis in p["lastmod"] → Sitemap, JSON-LD
  dateModified (auch Schema-Einträge, die noch UPDATED tragen), llms.txt „Stand“ = jüngstes Datum. core.stand_einsetzen()
  ersetzt im Body die Meta-Zeile „Stand: <time>“ (page_hero) und bei Rechtstexten die Einleitung „Stand: …“ – auch in
  gespeicherten Fassungen generator/inhalte/*.html, in denen das alte Datum als Text steht (Datei selbst bleibt unverändert).
  Editor: standSetzen() (gleiche Regex wie core) setzt beim Speichern einer bestehenden Seite das heutige Datum in Body UND
  sofort gepatchte Seite (sonst Fehlalarm „Direkte Änderungen“). today() im Editor jetzt lokales Datum (vorher UTC).
  Datei löschen = Daten werden beim nächsten Bauen aus den gespeicherten Angaben neu angelegt. sftp_helfer.bau_veraltet()
  ignoriert seitenstand.json (sonst meldete ein Testbau „Live vorher bauen“).

## Reihenfolge der Inhaltsquellen (wichtig!)

1. Standardtexte stehen in `generator/pages_main.py`, `pages_services.py`, `pages_info.py`.
2. Existiert `generator/inhalte/<slug>.html/.json`, ersetzt diese Fassung den Python-Text (Body, Titel,
   Beschreibung). Marker `<!--BLOG-TEASER-->` auf der Startseite wird beim Bauen durch die Blog-Vorschau ersetzt.
   FAQ-Schema wird aus `<details><summary>` der Fassung neu gelesen.
3. Eigene Seiten: `generator/seiten/` → `pages_custom.py` (sitemap, kontaktband). Brotkrumen folgen dem Menü
   (Start › übergeordneter Menüpunkt mit Link › Seite).
3a. Menü: `generator/menue.json` → `core.get_menu()/nav_html()`. Einträge {id, text, link, hervorgehoben, punkte:
   [{text, link} | {gruppe}]}; `gruppe` = Spaltenüberschrift (→ `.submenu.wide`). Menüpunkt mit link UND punkte
   → Untermenü mit automatischem „Überblick“. Seit 07.10.2026: Einträge vor der ersten Spaltenüberschrift (z. B. „Überblick“)
   stehen in Spalten-Untermenüs als Kopfzeile ul.sm-head über alle Spalten (vorher eigene, fast leere Spalte → drei Spalten im
   Zweier-Raster, Peter: „sieht schlimm aus“); Spaltenzahl = Anzahl Gruppen (max. 3) per style="--cols:N"
   (.submenu.wide grid repeat(var(--cols))). „hervorgehoben“ (Editor-Häkchen „Knopf“) gilt jetzt auch für Menüpunkte mit
   Untermenü (button.sub-toggle.nav-cta, Schrift bleibt navy, auch aufgeklappt/mobil). Fehlt die Datei, gilt `core.default_menu()` und das alte Feld
   `menue` („ueber“/„wissen“) in seiten/*.json; existiert sie, ist sie allein maßgeblich (Editor löscht `menue`).
   site.js setzt `.nav-narrow` (Mobilmenü), wenn die Leiste auf dem Desktop nicht mehr passt.
3b. Downloads: Marker `<!--DOWNLOADS-->` / `<!--DOWNLOADS:unterordner-->` → `core.expand_downloads()` beim Bauen
   (`<div class="downloads" data-downloads="…">`, darin keine weiteren divs!). `rescue_outside_changes` und der
   Editor machen daraus wieder den Marker (`collapse_downloads` bzw. `markDownloads`). JS `downloadsHtml()` im
   Editor muss zu `core.downloads_html()` passen.
   EXPERIMENTELL/RESSOURCEN (16.10.2026, Peters Auftrag „Verzeichnis download aufräumen: Unterverzeichnisse experimentell und
   ressourcen, darunter beliebige Unterverzeichnisse, deren Namen per Standard übernommen, im Editor besser/länger benennbar,
   ohne dass die Verbindung verloren geht; getestete von experimentell nach ressourcen verschieben, Links in den Blog-
   Einträgen automatisch anpassen“; Start mit eBooks, Software, Formulare in beiden): core.DL_BEREICHE; downloads.json NEU
   {ordner: {pfad: {titel, beschreibung}}, dateien: {pfad: {…}}, umzuege: {alter pfad: neuer pfad}} (alte flache Fassung =
   dateien; _dl_info liefert immer alle drei), dl_ordner_titel (Anzeigename, sonst Ordnername per file_label); Listen mit
   Überschrift h3 (erste Ebene unter dem Listenordner) bzw. h4 (tiefer) und Ordnerbeschreibung (p.dl-desc). Anzeigenamen
   ändern NIE Ordner/Adressen/Links. build.py: je umzuege-Eintrag `RewriteRule "^downloads/<alt>$" "/downloads/<neu>" [R=301,L]`
   (re.escape, nur wenn die alte Datei fehlt und die neue existiert). Editor (Reiter Dateien › downloads): Baum .dltree/.dlord
   mit Anzeigename/Beschreibung je Ordner (renderDlOrdner), je Datei „→ Ressourcen“ (data-mvschnell, gleicher Unterordner
   im anderen Bereich) und „Verschieben …“ (#dlgMove, dlZielOptionen) → dlVerschieben(alt, ziel, links, auchTest): Datei in
   dlRoot() verschieben (Sicherung), im Live-Stand wahlweise auch in website-test (#mvTestBox), Anzeigenamen mitnehmen,
   dlUmzugMerken (Ketten nachziehen, Zurückverschieben löscht die Umleitung), dlVerweiseAendern über dlPfadPaare (roh,
   encodeURI, je Segment kodiert, &amp;; Präfix-Regex mit ../ ./) in inhalte/seiten/blog/menue.json des Arbeitsstands
   (Test: als Testfassung über inhSchreiben). saveDlInfo schreibt das neue Format. DIE ALTE DATEI AUF DEM SERVER bleibt
   liegen, bis Peter sie löscht (SFTP-Reiter: Häkchen „Löschen“ im Bereich downloads) – die 301 greift erst dann.
   UMSTELLUNG am 16.10.2026 (scratchpad downloads_umbau.py, Sicherung editor-backups/2026-10-16_18-55-27_downloads-umbau mit
   PROTOKOLL.txt): alle Werkzeuge → experimentell/software, alle Fragebögen → experimentell/formulare, die zwei eBooks →
   ressourcen/ebooks (website/ und website-test/); Peter verschiebt Getestetes selbst nach ressourcen. 46 Inhaltsdateien
   (inhalte, seiten, blog, test/…) mit neuen Pfaden, test/_teststand.json-basis für 3 Dateien nachgezogen; seiten/ressourcen.html
   hat `<!--DOWNLOADS:ressourcen-->`, seiten/experimentell-zum-ausprobieren.html bekam `<!--DOWNLOADS:experimentell-->`.
   Links auf Werkzeuge, die noch nicht in downloads liegen (z. B. bodenanker, kanban – „nur Link“ im Protokoll), zeigen
   jetzt auf experimentell/software/… und melden beim Veröffentlichen weiter „fehlt“, bis Peter die Datei dorthin kopiert.
   „website-test/downloads/Dokumente - Verknüpfung.lnk“ (Peters Windows-Verknüpfung) blieb unangetastet im Wurzelordner.
   STAMMDATEN-ANSCHLUSS DANACH (16.10.2026 abends, Stempellauf 19:59, Sicherung editor-backups/2026-10-16_19-59-15_editoren): anschluss.js
   link() fand den Stammdaten-Editor nur im selben Ordner (bzw. fest ../downloads/) – nach dem Verschieben einzelner Werkzeuge nach
   ressourcen/ wäre er nicht mehr gefunden worden. Jetzt sdFinden() (nur http/https, nur eigener Server, erst bei der ersten Nutzung von
   link() – beim bloßen Öffnen eines Werkzeugs keine Anfrage): HEAD gleicher Ordner → Rahmenseite <wurzel>/werkzeuge/stammdaten-editor.html
   lesen (ihr iframe src nennt den echten Ort, Name ist dank 3c stabil) → downloads/ressourcen|experimentell[/software]/ → Rückfall gleicher
   Ordner; Ergebnis gemerkt (sdUrl), overlay() setzt iframe-src/↗-Link nach, wenn der Ort erst später feststeht. file:// unverändert
   (daneben). Geprüft im Vorschau-Browser mit dem echten Code (Name ersetzt): anderer Ordner, gleicher Ordner, Rückfall, Rahmenseite.
   Der Stammdaten-Editor liegt zurzeit in KEINEM Download-Ordner – auf der Website führt der Knopf erst nach Peters Kopie zum Ziel.
3c. Werkzeug-Rahmenseiten (seit 07.10.2026, Peter: „bei Seiten, die ich aus downloads auswähle, fehlt die Menüleiste“):
   core.set_werkzeuge(DIST) (in build.main nach prepare_test_dir) sammelt alle *.html/*.htm in DIST/downloads und
   DIST/files → core.WERKZEUGE {"downloads/x.html": "werkzeuge/x.html"} (seit 16.10.2026 Name = NUR der Dateiname per
   slug_id, damit die Rahmenseite beim Verschieben experimentell → ressourcen ihre Adresse behält; gleicher Dateiname in zwei
   Unterordnern: ressourcen/ gewinnt, die übrigen mit Ordner-Vorsilbe; gleicher Name in files/ → „datei-…“). pages_werkzeuge.all_pages(DIST) erzeugt je Datei eine Seite (werkzeug=True, noindex,
   nicht in Sitemap/seitenstand): Brotkrumen, Leiste .tool-head/.tool-bar (h1 = downloads.json-titel > Menütext >
   <title> der Datei; Links „In eigenem Fenster öffnen“ / „Herunterladen“) und iframe.tool-frame. site.js: Rahmenhöhe
   = Fensterhöhe minus Abstand oben (min. 420 px), #/?-Teil der Seitenadresse wird an das Werkzeug weitergereicht,
   Klicks im Werkzeug auf Website-Links (gleiche Herkunft oder my-coaching4you.de, außer andere downloads/files-HTML)
   bekommen target=_top. Links: core._a (Menü) und core.werkzeug_links (Seiteninhalte, in build.main NACH seitenstand,
   damit es nicht als Änderung zählt) setzen href auf die Rahmenseite + data-werkzeug="<ursprünglicher Link>";
   Links mit download-Attribut (Download-Listen!) bleiben. Rückweg: core.werkzeug_zurueck (rescue_outside_changes) und
   im Editor werkzeugLinksZurueck() in loadFrame sowie data-werkzeug in menuFromNav. Der Sofort-Patch des Editors
   schreibt bis zum nächsten Bauen die direkten Links. Getestet: scratchpad werkzeug_test.py (Rundlauf), Browser
   (Menü markiert, Höhe Desktop/Handy, Impressum-Klick im Fragebogen öffnet oben, Mindmap-Editor im Rahmen).
4. Blog: `generator/blog/JJJJ-MM-TT-adresse.md`. Kopf-Schlüssel: titel, datum, aktualisiert, kategorie, autor,
   beschreibung, bild, bild_alt, alte_adresse, ki_hinweis, kommentare (`ja`/`nein`, s. u.),
   status (`entwurf` | `veröffentlicht`).
   `ki_hinweis`: `nein` | `ja` (= ChatGPT, ältere Beiträge) | Name des Assistenten, z. B. `Claude` → Hinweis „mit dem
   KI-Assistenten <Name> erstellt“ (pages_blog ai/ai_name; Editor-Auswahl kiOptions()). Kategorie „Tools“ (seit 03.10.2026,
   CATEGORY_LINKS → ressourcen.html): 15 Entwürfe `2026-10-03-tool-*.md` (je Editor einer, ki_hinweis Claude, status entwurf) –
   Peter prüft und veröffentlicht selbst. Links auf `downloads/<editor>.html` sind erst gültig, wenn Peter die Editoren dorthin
   kopiert hat (sonst meldet die Linkprüfung sie beim Veröffentlichen). 8EM-Beitrag: Editor „in Entwicklung“, kein Download-Link.
   STAND 14.10.2026 (Peters Auftrag „Schreibe oder aktualisiere die Blog-Einträge, neue noch nicht freigegeben“; Sicherung
   editor-backups/2026-10-14_00-38-30_blog-tools): veröffentlicht hat Peter Genogramm, Inneres Team, Kartenarbeit, Mindmap,
   Soziales Atom, Tetralemma. Überarbeitet: Analyse (+ Johari, Werte-/Entwicklungsquadrat, Eisenhower, Energiebilanz), Lebensrad
   (+ Zeitkuchen, Teamuhr nach Tuckman), Lebenslinie (+ Rückwärtsplanung, 3D-Abschnitt), Systembrett (2D-Neuerungen + 3D, Link auf
   seiten/systembrett-2d-und-3d), Träumer–Macher–Kritiker (+ 3D-Abschnitt), 8EM-Beitrag neu als 2026-10-03-tool-8em-modeller.md
   (alte Datei tool-8em-editor.md entfernt, Bild bleibt tool-8em-editor.jpg). Mindmap (veröffentlicht) NUR als Testfassung
   generator/test/blog/2026-10-03-tool-mindmap.md (+ basis in _teststand.json) – Peter übernimmt nach Prüfung per „Nach Live
   übernehmen“. Zwölf neue Entwürfe 2026-10-14-tool-*.md: ikigai, bodenanker, logische-ebenen, u-prozess (je 2D + 3D in einem
   Beitrag), geschaeftsmodell, riemann-thomann, veraenderung, wirkungsgefuege, stakeholder, eam-reifegrad (Reifegradcheck + IT-
   Erhebungsbogen), eam-werkzeuge (Fassung-17-Werkzeuge des 8EM-Modellers), 3d-werkzeuge (Überblick aller acht 3D-Werkzeuge; 3D-Inneres-
   Team und 3D-Soziales-Atom stehen nur dort – die veröffentlichten 2D-Beiträge verlinken bewusst nichts, was noch nicht bereitsteht).
   Entwürfe verlinken sich teils gegenseitig und auf downloads/ – beim Veröffentlichen zusammen freigeben bzw. Werkzeuge kopieren,
   sonst meldet die Linkprüfung es. Beitragsbilder der neuen Beiträge in website/blog/files/tool-<slug>.jpg (Stil wie oben; 3D-Bilder
   per renderImage mit eigener Kamera, Kern-Werkzeuge über buildPNG im iframe; erzeugt mit einem vorübergehenden Empfänger auf
   127.0.0.1:8399, launch.json-Eintrag danach wieder entfernt). LÄNGEN: Seitentitel = „titel | MC4Y Blog“ (nur bis 70 Zeichen,
   sonst titel allein), build.py warnt ab 67 → titel bis 54 oder 59–66 Zeichen; beschreibung höchstens 160.
   FRAGEBÖGEN (14.10.2026, Peters Auftrag „Schreibe auch Beiträge für die Fragebögen“): drei Entwürfe 2026-10-14-tool-werteprofil.md
   (beide Werteprofil-Bögen), -u-profil.md (beide U-Bögen), -werte-u-kompass.md (Kompass inkl. Innovationsklima aus Peters Masterarbeit,
   Link auf e-books.html). Bilder tool-werteprofil/-u-profil/-werte-u-kompass.jpg aus fiktiv ausgefüllten Bögen (Ergebnis-SVGs mit
   eingerechneten Stilen). Regeln dabei: Bögen heißen „Werteprofil“/„U-Profil“, Spiral Dynamics® nur beschreibend + Markenhinweis,
   „Orientierung, kein Etikett“, kein „höher = besser“, KEINE Wissenschaftskritik (Kühl, Vermeren) – auch nicht beim Teil
   Interessenkonflikte –, Pflichthinweis „Erprobungsfassung, nicht wissenschaftlich validiert“; Verbindung beider Modelle = eigene Einordnung.
   BUCHHALTUNG (14.10.2026, Peters Auftrag „Schreibe auch einen Blog-Eintrag für das Buchhaltungstool“): Entwurf 2026-10-14-tool-buchhaltung.md
   (Link downloads/buchhaltung-editor.html – erst gültig, wenn Peter das Werkzeug dorthin kopiert). Bild tool-buchhaltung.jpg aus dem fiktiven Beispiel
   (Rechnungs-PDF, Kennzahlen der Voranmeldung, Monatsdiagramm; mit PyMuPDF show_pdf_page zusammengesetzt, scratchpad blogbild_buchhaltung.py).
   Regeln dabei: keine Steuerberatung, kein zertifiziertes Programm, keine ELSTER-Übermittlung, „in Anlehnung an SKR 03/04“, KoSIT-Prüfung vor dem
   ersten Versand empfehlen; Peters eigene Fehlerfunde aus seinen Listen NICHT im Beitrag nennen (nur allgemeine Stolpersteine).
   Nachgezogen am 14.10.2026 abends (Sicherung editor-backups/*_blog-buchhaltung): Ausfüllhilfen im „Auf einen Blick“, Bundesland/13-stellige
   Steuernummer für die ELSTER-Datei, Abschnitt „Werte für die Steuererklärung übernehmen“ (Bericht „Übertragung an Steuerprogramme“). Der
   Übertragungs-Assistent (Python) ist bewusst NICHT erwähnt – er steht nicht als Download bereit und ist nicht in echter Steuersoftware getestet.
   KANBAN (14.10.2026, Peters Auftrag „Ja, schreibe einen Blog-Eintrag“): Entwurf 2026-10-14-tool-kanban.md (Titel 50 Zeichen, Link
   downloads/kanban-editor.html – erst gültig nach Peters Kopie; verlinkt projektunterstuetzung-und-coaching, teamentwicklung,
   blog/it-projektmanagement-kmu, kontakt). Bild tool-kanban.jpg: Board-Ausschnitt per Edge headless (--screenshot, eigenes Profil im
   Scratchpad, --virtual-time-budget, damit der Start-Hinweis verschwunden ist) + CFD/Burn-up als PNG aus der Vorschau (SVG → Canvas →
   vorübergehender Empfänger 127.0.0.1:8399, launch.json-Eintrag danach entfernt), zusammengesetzt mit PyMuPDF (scratchpad
   blogbild_kanban.py). Hinweis: Screenshot-Koordinaten in CSS-Pixeln rechnen (Anzeige ≠ Originalgröße).
   STAMMDATEN (15.10.2026, Peters Auftrag „Erstelle auch einen Blog-Eintrag für das Personenstammdatenmodul“): Entwurf 2026-10-15-tool-stammdaten.md
   (Titel 53 Zeichen, Beschreibung 149; Link downloads/stammdaten-editor.html – erst gültig nach Peters Kopie; verlinkt 8-ebenen-modell,
   it-gestuetzte-organisationsberatung, kontakt; keine Links auf andere Entwürfe). Bild tool-stammdaten.jpg aus Edge-headless-Aufnahmen von
   stammdaten-editor.html#beispiel&ansicht=… (Organigramm, Personentabelle, Netz, Auswertung; scratchpad blogbild_stammdaten.py; Edge-Aufrufe
   nacheinander mit Start-Process -Wait, sonst blockiert das gemeinsame Profil). Buchhaltungs-Entwurf (Abschnitt „Kontoauszüge abgleichen“, DATEV,
   Satz zum Stammdaten-Anschluss) und Kanban-Entwurf (Satz zum Stammdaten-Anschluss) am 15.10.2026 ergänzt (Sicherungen editor-backups/
   *_blog-buchhaltung-bank und *_blog-stammdaten-anschluss). Peter hatte beide Beiträge in der TESTFASSUNG (generator/test/blog, status
   veröffentlicht) – auf sein „Ja, übertrage“ dieselben Ergänzungen auch dort eingefügt, aktualisiert: 2026-10-15 (Sicherung
   editor-backups/*_testblog-ergaenzung). Live- und Testfassung dieser zwei Beiträge sind damit inhaltlich gleich bis auf Peters Kopfangaben.
   Stammdaten-Entwurf am 15.10.2026 nachmittags (Peters „Ja, ergänze den Blog-Entwurf“) um „Stammdaten führend“ ergänzt (Sicherung
   editor-backups/2026-10-15_15-10-18_blog-stammdaten-verknuepfung): Auf einen Blick, Abschnitt „Auf Knopfdruck in jedem Werkzeug“ (Knopf 👥 Stammdaten,
   Feldhilfe per Rechtsklick), neuer Abschnitt „Vorhandene Daten übernehmen und zusammenführen“ (vier verknüpfbare Werkzeuge, Erstverknüpfung, Abgleich,
   Zusammenführen X in Y, gestellte Rechnungen behalten ihre Angaben, danach führend, Tipp „erst ein Werkzeug“), Satz zur Zuordnung in der Werkzeug-Datei,
   neue beschreibung (159 Zeichen). Die Sätze zum Anschluss in den Kanban-/Buchhaltungs-Entwürfen (live und test) stimmen weiter und blieben unverändert.
   Am selben Abend nachgezogen (Adapter für alle Werkzeuge mit Personen): „Auf einen Blick“ und Abschnitt „Vorhandene Daten übernehmen …“ nennen jetzt alle
   verknüpfbaren Werkzeuge inkl. Coaching-Werkzeuge, mit Absatz „je Figur als Person/Rolle/gar nicht, nur der Name, keine automatische Verbindung zwischen Brettern“.
   PROJEKTMANAGEMENT (15.10.2026, mit dem Werkzeug beauftragt): Entwürfe 2026-10-15-tool-projektmanagement.md (Titel 51 Zeichen; Links downloads/projektmanagement-
   editor.html, blog/tool-buchhaltung.html = noch Entwurf, blog/tool-projektauswertung.html, projektunterstuetzung-und-coaching, blog/it-projektmanagement-kmu,
   kontakt) und 2026-10-15-tool-projektauswertung.md (Titel 53 Zeichen; Links blog/tool-projektmanagement.html, downloads/projektauswertung-editor.html,
   projektunterstuetzung-und-coaching, it-gestuetzte-organisationsberatung, kontakt) – zusammen mit dem Buchhaltungs-Entwurf freigeben bzw. die Werkzeuge nach
   downloads kopieren, sonst meldet die Linkprüfung es. Regeln dabei: „Rational“ nur beschreibend (Marke von IBM; Hilfe des Werkzeugs zusätzlich „ersetzt keine
   Steuer- oder Rechtsberatung“), Hinweis auf
   Mitbestimmung bei Zeit-/Auslastungsauswertungen (Betriebsrat), Quellen am Ende, alle Beispiele fiktiv. Bilder tool-projektmanagement.jpg (Plan/Gantt des
   Wasserfall-Beispiels im Monatszoom mit Kopfzeile + Zeilen ab „Realisierung“, RUP-Disziplinen, Scrum-Velocity) und tool-projektauswertung.jpg (Portfolio +
   Auslastung) aus Edge-headless-Aufnahmen (#beispiel&modell=…&ansicht=…&zoom=…), zusammengesetzt per scratchpad blogbild_projekt.py (PyMuPDF).
   RECHNUNGEN (15.10.2026 abends, Sicherung editor-backups/*_blog-rechnungen): Entwürfe tool-projektmanagement (Auf einen Blick, Kosten-Absatz, neuer
   Abschnitt „Rechnungen zu gezielten Leistungszeitpunkten“, Auswertungen), tool-projektauswertung (Leistungsart, offene Rechnungen mit Buchhaltungsstand)
   und der LIVE-Entwurf tool-buchhaltung (Auf einen Blick, Absätze Versand und Übernahme aus dem Projektmanagement, Link blog/tool-projektmanagement.html)
   ergänzt. Die TESTFASSUNG generator/test/blog/2026-10-14-tool-buchhaltung.md (status veröffentlicht) zunächst nicht geändert, auf Peters „ja“
   dieselben Absätze (Versand, Rechnungen aus dem Projektmanagement, ohne Link) dort ergänzt (Sicherung editor-backups/*_testblog-rechnungen).
   8EM ↔ PROJEKTMANAGEMENT (15.10.2026 abends, Sicherung editor-backups/*_blog-8em-projekt): LIVE-Entwurf tool-8em-modeller (Punkt in „Auf einen Blick“,
   Abschnitt „### Projekte und Projektmanagement“ nach den Architekturmanagement-Werkzeugen, Link blog/tool-projektmanagement.html = noch Entwurf)
   und Entwurf tool-projektmanagement (Abschnitt „## Projekte im Unternehmensmodell“ vor „Daten und Hinweise“, Link 8-ebenen-modell.html, Modeller
   „noch in Entwicklung“). Die TESTFASSUNG generator/test/blog/2026-10-03-tool-8em-modeller.md (status veröffentlicht) am 16.10.2026 auf Peters „Ja,
   übernimm“ nachgezogen (Sicherung editor-backups/*_testblog-8em-projekt, aktualisiert 2026-10-16): Punkt in „Auf einen Blick“ und Abschnitt
   „### Projekte und Projektmanagement“ OHNE Link (Projektmanagement-Beitrag ist in der Testfassung nicht veröffentlicht). In Live-Entwurf UND
   Testfassung lautet der Satz zur Verfügbarkeit jetzt „zunächst unter Experimentell – zum Ausprobieren (experimentell-zum-ausprobieren.html) und nach
   ausreichender Erprobung unter Ressourcen und Downloads“ (Zwei-Stufen-Prinzip der Downloads). Sonst nennt kein Beitrag die Download-Seite in Worten.
   VERTRIEB (16.10.2026, Peters Auftrag „Schreibe einen Blog-Eintrag für das Tool“): Entwurf 2026-10-16-tool-vertrieb.md (Titel 54 Zeichen, Beschreibung 156;
   Links downloads/vertrieb-editor.html – erst gültig nach Peters Kopie –, 8-ebenen-modell, prozess-management-beratung, kontakt; KEINE Links auf andere
   Entwürfe, Buchhaltung/Projektmanagement/Stammdaten nur beim Namen genannt). Regeln dabei: LinkedIn/Sales Navigator und Microsoft 365/Copilot nur
   beschreibend mit Markenhinweis in den Quellen; „liest LinkedIn nicht aus“; Treffer aus der LinkedIn-Suche einzeln per Schnellerfassung mit Profil-Adresse,
   viele Kontakte über Vernetzen + eigenen Datenexport; Firmensuche in offenen Daten geht auf der Website und lokal (CSP-Ausnahme seit 16.10.2026); DSGVO/UWG
   als Hinweise, keine Rechtsberatung. Bild tool-vertrieb.jpg aus Edge-headless-Aufnahmen von vertrieb-editor.html#beispiel&ansicht=pipeline|gremium
   (&sel=Fehnstern)|aw (über http://localhost:8322), zusammengesetzt mit PyMuPDF (scratchpad blogbild_vertrieb.py: PNG-Seiten rechnen mit 96 dpi –
   Ausschnitt-Faktor aus der echten Pixelbreite fitz.Pixmap, nicht aus get_pixmap()).
   KUNDEN-NAVIGATOR (16.10.2026, ohne Rückfrage nach Peters Vorgabe „Datenschutz und Blog gehören zum Auftrag“; Sicherung editor-backups/
   *_blog-kunden-navigator): Entwurf 2026-10-16-tool-kunden.md (Titel 50, Beschreibung 145; Links downloads/kunden-editor.html – erst gültig nach Peters
   Kopie –, 8-ebenen-modell, prozess-management-beratung, kontakt; keine Links auf Entwürfe). Bild tool-kunden.jpg aus Edge-headless-Aufnahmen von
   kunden-editor.html#beispiel&ansicht=start|anliegen&modus=board|kunden&sel=Nordlicht|aw (scratchpad blogbild_kunden.py). Ergänzt: Vertriebs-Entwurf
   (Auf einen Blick „Weiterreichen“, Abschnitt „Vom Abschluss zur Betreuung, Rechnung und zum Projekt“ mit Übergabe an den Kunden-Navigator und
   Zusatzgeschäft zurück), Buchhaltung LIVE-Entwurf UND Testfassung generator/test/blog (aktualisiert 2026-10-16; Punkt „Zusammenarbeit mit Vertrieb und
   Kundenbetreuung“, Absatz nach den Projektrechnungen: Kunden aus dem Vertriebs-Navigator, Kundenstand und Leistungen mit dem Kunden-Navigator), Stammdaten-
   Entwurf (verknüpfbare Werkzeuge + Projektmanagement, Vertriebs- und Kunden-Navigator mit Firmenzugehörigkeit).
   KUNDEN-NAVIGATOR: DIENSTLEISTER, DOKUMENTE, E-MAIL (16.10.2026 abends, ohne Rückfrage; Sicherungen editor-backups/*_blog-kunden-dokumente-mail und
   *_datenschutz-dokumente-mail): tool-kunden-Entwurf (Beschreibung 151; Auf einen Blick + drei Abschnitte „Auch Ihre eigenen Dienstleister“, „Dokumente …“,
   „E-Mails aus Outlook, Thunderbird & Co.“, vierter Weg Eingangsrechnungen, Quellen RFC 5322/MIME/[MS-OXMSG] + Markenhinweis Outlook = Microsoft,
   Thunderbird = Mozilla Foundation); Buchhaltung LIVE-Entwurf UND Testfassung (Auf einen Blick, Absatz Eingangsrechnungen als Beleg). Bild tool-kunden.jpg
   unverändert (zeigt weiterhin zutreffende Ansichten). Datenschutzerklärung Abschnitt 7: Satz „ruft kein Postfach ab …“, Zeile „Dokumente und E-Mails im
   Kunden-Navigator“ (IndexedDB 'mc4y-kunden' Store dateien bzw. Dokumentenordner), Übergaben + Eingangsrechnungen (Datei wird nach Übernahme entfernt),
   Kundenstand + Eingangsrechnungen je Rechnungssteller (ohne private Belege).
   HTML-PY-EDITOR (16.10.2026, Peters Auftrag „ergänze den Blog-Eintrag so, dass leicht nachvollziehbar ist, wie Python und die Module
   auf den Client-Rechnern ggf. nachzuinstallieren sind, als Installationsvoraussetzungen“ – es gab noch keinen Beitrag zu FreeHTML,
   daher neu): Entwurf 2026-10-16-tool-html-py-editor.md (Titel 48, Beschreibung 146; FreeHTML + HTML-PY, Downloads experimentell/
   ressourcen, Abschnitt „Installationsvoraussetzungen“ mit Windows 10/11, macOS, Linux und Installation von Hand; Links
   downloads/experimentell/software/freehtml-editor.html und …/html-py-editor.zip – erst gültig, wenn Peter beides dorthin kopiert;
   Beitragsbild tool-html-py-editor.jpg seit 16.10.2026 abends: fiktives Probeprojekt „Muster Beratung“ (Farbschema Waldgrün & Messing) im
   Vorschau-Browser; Zustände als statisches DOM-Abbild (Feldwerte als Attribute, ohne Skripte; Host/Benutzer im Abbild durch ssh.ihr-hoster.example/
   web123 ersetzt, Schlüsselbund-Anzeige wie mit keyring) über einen vorübergehenden Empfänger 127.0.0.1:8399 gespeichert, mit Edge headless
   fotografiert (Handy über einen 390-px-iframe-Rahmen – Edge headless schneidet schmale Fenster sonst rechts ab), Programmfenster gezeichnet;
   zusammengesetzt mit PyMuPDF (scratchpad blogbild_htmlpy.py). Blog-Markdown kennt `Inline-Code`, aber keine Codeblöcke/Tabellen – Befehle daher als Listenpunkte.
   UML-EDITOR UND TASCHENRECHNER (17.10.2026, ohne Rückfrage; Sicherung editor-backups/*_blog-uml-rechner): Entwürfe 2026-10-17-tool-uml-editor.md (Titel 53,
   Beschreibung 147; Links downloads/experimentell/software/uml-editor.html – erst gültig nach Peters Kopie –, 8-ebenen-modell, it-gestuetzte-organisationsberatung,
   kontakt; Quellen OMG UML 2.5.1 formal/17-12-05, Markenhinweis UML®/OMG, PowerPoint/Visio) und 2026-10-17-tool-taschenrechner.md (Titel 52, Beschreibung 144;
   Link …/taschenrechner-editor.html, kontakt). Bilder tool-uml-editor.jpg (Klassendiagramm im Editor + Zustands-, Sequenz-, Aktivitätsdiagramm) und
   tool-taschenrechner.jpg aus Edge-headless-Aufnahmen der #beispiel-Ansichten (scratchpad blogbild_uml_rechner.py). 8EM-Beitrag live (Entwurf) UND Testfassung
   (veröffentlicht, aktualisiert 2026-10-17): Satz „Den UML-Editor gibt es außerdem als eigenständiges Werkzeug …“ ohne Link. Suite-Entwurf: Kacheln/Taskleiste.
   BLOG-PRÜFUNG ALLER BEITRÄGE (23.10.2026, Auftrag der Hauptsitzung „8EM-Modell Tool“, Phase vor dem Testdomain-Deploy; Sicherung
   editor-backups/2026-10-23_18-42-30_blog-pruefung): alle Beiträge DE + EN, Live-Entwürfe und Testfassungen (192 Dateien) auf tote/verfrühte Links,
   Richtigkeit gegen den Werkzeugstand, DE/EN-Gleichheit, Längen und Bilder geprüft (vier Prüfgruppen als Unteragenten, Fokusbeiträge Installation/
   Suite/Crawler/Taskleiste/CMDB selbst). Korrigiert u. a.: Crawler/Agenten (Windows-Starter Agent-/Crawler-installieren.bat, Home Assistant mit
   mc4y-erkennung.json, `-Sprache`/`--lang`), Suite installieren (Kachel „Dieses Gerät an die CMDB melden“, Autostart je System, Mac-Installer nur für
   Fachleute, Kennwort vergessen, Paketname 0.4.8), Suite (Mac-Einzelplatz erprobt, grafischer Installer, Agenten/Crawler), CMDB/ITSM (Herkünfte 8EM/
   Agent/Crawler, Häkchen-Wortlaute), 8EM (ITIL = PeopleCert, fehlender Listenpunkt), Taschenrechner (Verlauf standardmäßig behalten), UML (PNG ist
   Bild), Stammdaten (Feldhilfe), Kunden/Vertrieb (Tippfehler), Werkzeuge-Englisch (CSV-Köpfe englisch, Stammdaten-Beispiel schaltet nicht um),
   Veränderung (Reihenfolge nach Lage), Ressourcenbaum, Systembrett, Mindmap, Riemann-Thomann, U-Prozess, ein EN-Zitat bei Peters KI-Beitrag.
   Testfassungen 8EM und Buchhaltung haben seitdem die Links auf tool-cmdb/tool-projektmanagement (dort veröffentlicht). Live-Rumpf jeweils
   gleich der Testfassung (außer den bewussten Unterschieden „noch nicht öffentlich“ ↔ „Erprobungsfassung“ bei 8EM/EAM-Werkzeuge und der Mindmap);
   _teststand.json basis für 23 Live-Dateien nachgezogen. Peters eigene deutsche Texte NICHT geändert, Funde nur gemeldet.
   Beitragsbilder der Tool-Beiträge: website/blog/files/tool-<slug>.jpg (1200 × 675, JPEG, weiß mit Navy-Gold-Streifen
   unten), erzeugt aus dem eingebauten Beispiel des jeweiligen Editors (buildPrims → buildPNG; Systembrett Ist + Lösung,
   Entscheidung Tetralemma + Waage, TMK Anordnung „Reihe“, Mindmap eigenes Beispiel „Teamworkshop planen“, 8EM = Ebenenleiste
   + Diagramm von 8-ebenen-modell.html). Kopf: `bild: blog/files/…` (relativ ab Website-Wurzel – core.relativize macht in
   blog/ daraus ../blog/files/…) und `bild_alt`. Seit 03.10.2026 setzt jede Beitragsseite ihr Bild als og:image und als
   absolute BlogPosting-image-Adresse (pages_blog img_abs → core og_image/og_image_alt).
   Nur veröffentlichte werden gebaut. `alte_adresse` = alter X5-Query-String (z. B. `das-innere-team`)
   → 301 auf neue Seite (302 auf /blog/, solange Entwurf). Bei Dubletten gewinnt der veröffentlichte Beitrag.
   Blogseiten liegen in `blog/`; relative Links werden beim Schreiben mit `../` versehen (core.relativize).
   Erlaubtes Inline-HTML im Markdown: `<span class="c-navy|c-bronze|c-grau|c-ink">`, `<mark>`, sowie
   `<span style="color:…">` (Hex oder rgb()), `<span style="font-size:…px">`, `<span style="font-family:…">`
   aus dem Editor (feste, eng geprüfte Muster – siehe `pages_blog._inline()`/`mdInline()`).

## Schutz vor Überschreiben (Fingerabdruck)

- Jede erzeugte Seite enthält `<meta name="mc4y-stand" content="…">` = Hash aus Body, Titel, Beschreibung
  der tatsächlich geschriebenen Datei (build.stamp_html).
- Vor dem Schreiben prüft build.rescue_outside_changes() `website/` und `website-test/`: Weicht der Inhalt
  vom Fingerabdruck ab, wurde die Seite außerhalb geändert → normale Seiten werden nach `generator/inhalte/`
  übernommen (nur wenn neuer als eine vorhandene Fassung; neueste gewinnt). Blog-/eigene Seiten: Sicherung in
  `editor-backups/<zeit>_build/` und Warnung „ACHTUNG“.
- `website-test/` wird abgeglichen, nie gelöscht. Im Blogordner werden nur *.html und feed.xml ersetzt
  (Unterordner wie `blog/files/` bleiben).
- Testbau seit 07.10.2026: core.DOWNLOADS_DIR = website-test/downloads (Download-Listen der Testversion aus dem Test-
  Ordner). prepare_test_dir() kopiert aus website/ nur, was seit dem letzten Testbau neu oder geändert ist (Signatur
  [Größe, mtime] in website-test/.mc4y-abgleich.json; Punktdatei → SFTP-Helfer ignoriert sie) und nur, wenn die Live-Datei
  neuer ist als die Testdatei. In Test ersetzte oder gelöschte Dateien bleiben so; neuere Testfassungen werden gemeldet.
  Getestet mit Wegwerf-Dateien (scratchpad abgleich_test.py).

## Editor (mc4y-editor.html)

- Läuft lokal im Browser, Ordnerfreigabe per showDirectoryPicker, Handle in IndexedDB.
- Speichern bestehender Seite → `generator/inhalte/` + sofortiges Patchen von `website/<slug>.html`.
  Eigene Seiten → `generator/seiten/`. Blog → Markdown in `generator/blog/`. Vor jedem Überschreiben
  Sicherung in `editor-backups/`.
- Werkzeuge: Fett/Kursiv/H2/H3/Listen/Zitat, Link-Dialog (Seite der Website, Datei aus files, extern,
  E-Mail, Telefon; Download-Option nur auf Seiten), Farbpalette (Markenfarben-Klassen s. o., Gold nur als
  `<mark>`, daneben `<input type="color">` für beliebige Farbe als Inline-Style), Größe (Zahlenfeld px,
  beliebiger Wert als Inline-Style `font-size`), Schrift (Auswahl aus fest hinterlegten, überall
  installierten Schriften – bewusst keine Google Fonts/Nachladen von außen, das widerspräche der CSP und
  dem früher behobenen Google-Fonts-Problem der alten Seite), Bild aus assets/img, Tabelle einfügen ODER
  bearbeiten (Cursor in einer vorhandenen Tabelle → derselbe Knopf öffnet den Dialog vorbefüllt und baut die
  Tabelle beim Bestätigen neu auf: Zeilen/Spalten hinzufügen oder entfernen, Spaltenbreite in ch, Rahmenstil
  – vorhandene Zellinhalte bleiben so weit erhalten, wie die neuen Maße sie noch abdecken; nur auf Seiten,
  nicht im Blog, da `pages_blog.markdown()` keine Tabellen unterstützt), Quelltext/Markdown-Ansicht,
  Einfügen nur als reiner Text.
  Farbe/Größe/Schrift wickeln markierten Text in ein `<span style="…">` mit genau EINER Eigenschaft
  (color/font-size/font-family); mehrere Auszeichnungen verschachteln sich (siehe `applyStyleSpan()`).
- JS-Funktionen md2html/html2md müssen zu `pages_blog.markdown()` passen – bei Änderungen beide anpassen.
- Dialoge (seit 16.10.2026): globaler keydown-Handler nach `document.addEventListener("keydown", keys)` – Enter in einem
  Eingabefeld eines `dialog form[method=dialog]` löst den Knopf `value="ok"` aus (vorher löste der Browser den ersten Knopf
  „Abbrechen“ aus, z. B. Seite anlegen: Name tippen + Enter = Abbruch). Dialoge mit eigener Enter-Behandlung (preventDefault,
  z. B. #pwVal, Server-Formular) bleiben unberührt. Gleicher Handler im FreeHTML-/HTML-PY-Editor.
- Downloads verschieben (experimentell → ressourcen) siehe „Reihenfolge der Inhaltsquellen“ 3b.
- Reiter „Menü“: bearbeitet `generator/menue.json` (Entwurf in S.menuDraft, Speichern mit Prüfung).
  Ohne menue.json wird das Menü aus der gebauten index.html gelesen (menuFromNav).
- Quelltext-Ansicht: `formatHtml()` (DOM-basiert, Block-Elemente je Zeile, Inline-Inhalt bleibt einzeilig,
  idempotent) + Textarea transparent über `<pre>` mit `highlight()`; Tag-Paare je Tiefe gleich gefärbt,
  Paar unter dem Cursor gelb, Strg+M springt zum Gegenstück. Pre und Textarea müssen identische Schrift-/
  Umbruch-Metriken haben (kein Fett/Kursiv in der Hervorhebung).
- Neuer Beitrag mit Titel eines vorbereiteten Alt-Eintrags → Rückfrage; freie Adresse wird automatisch gewählt.
- SEITE KOPIEREN / UMBENENNEN (seit 08.10.2026, Peters Wunsch): Knöpfe #btnCopy („⧉ Kopieren …“, Standard- und eigene
  Seiten) und #btnRename („✎ Umbenennen …“, nur eigene Seiten) in der Aktionsleiste → Dialog #dlgCopy (copyDialog(mode),
  cpH1/cpSlug/cpAuto, Menü cpMenu nur beim Kopieren – Vorschlag = Menüplatz des Originals, cpLinks/cpRedirect nur beim
  Umbenennen; vergebene Adresse per setCustomValidity in cpPruefen, slugVergeben/RESERVIERT/freieAdresse). Kopie nimmt den
  aktuellen Editorinhalt (auch ungespeichert). Standardseite → eigene Seite über ausStandardseite(): .page-hero heraus
  (h1 → ueberschrift = neuer Name, .lead → einleitung als Text), letztes section.contact mit Standardüberschrift heraus →
  kontaktband true; meta layout "voll" (pages_custom setzt den Inhalt NICHT in die schmale Prosa-Spalte; Editor-Rahmen
  <main id="ed"> statt prose-div – vollLayout()); ohne page-hero aber mit h1 (Startseite) zusätzlich kopf false (kein
  zweiter Seitenkopf). <!--BLOG-TEASER--> in eigenen Seiten: pages_custom ersetzt es, Editor zeigt den Platzhalter
  (openPage markBlog auch bei Marker). titel wird bei Kopien geleert. Umbenennen (renamePage): neue seiten-Dateien schreiben,
  alte per inhLoeschen (Test = Löschmarke), gebaute WEB()/alt.html löschen, Menü immer (Link + Text, wenn er der alten
  Überschrift entsprach), verweiseAendern(alt, neu) in inhalte/seiten/blog des Arbeitsstands (href="…alt.html", Markdown
  ](alt.html), mit ../ ./ #/?), alte Adresse in meta.alte_adressen → build.py: RewriteRule 301 alt.html → /neu.html (nicht,
  wenn die alte Adresse wieder eine Seite ist) und entfernt DIST/alt.html, wenn sie den mc4y-stand-Stempel trägt.
  Geprüft: OPFS-Probeprojekt (Kopie Standardseite/Startseite/eigene Seite, Umbenennen live und im Test-Stand mit Löschmarken,
  vergebene Adresse), Generator in Wegwerf-Kopie (scratchpad kopie_test.py: ein h1, ein Kontaktband, Abschnitte, 301,
  alte Datei entfernt, Blog-Vorschau, zweiter Lauf), echte Builds je zweimal grün.
- ARBEITSSTAND Test | Live (seit 07.10.2026, Peters Kritik: „website-test sollte aus website-test gebaut und dort die
  einzelnen Sachen ausgewählt werden – ich will erst testen“): Schalter #wsSwitch in der Kopfzeile, S.ws 'test'|'live'
  (localStorage 'mc4y-arbeitsstand', Standard Test, sofern website-test/index.html existiert), body.ws-test. WEB() =
  website-test bzw. website; bauBat(); WEB_RE. Aus WEB() kommen: S.images, S.downloads/dlDirs (dlRoot()), S.pubFiles
  (fileRoot()), Link-Dialog (fillFileOptions), Bild-Dialog, Download-Listen (downloadsHtml), Menüziele (linkOptionsHtml),
  Seitenliste/Titel und Brotkrumen (Rückfall website), S.css/S.js, Bildauflösung im Bearbeitungsrahmen, Vorschau,
  Sofort-Patch nach dem Speichern (nur WEB()), Reiter „Dateien“ (TARGETS() mit „@“ = WEB(), fileTarget wird beim Umschalten
  umgeschrieben). Feste Hinweise in Dialogen über span.wsweb/.wsbat. Reiter „Dateien“ im Test-Stand: Abschnitt „Nach Live
  übernehmen“ (promoListe/renderPromo): PROMO_ORDNER files, downloads, assets/img, assets/dokumente, blog/files; „neu“ (nur
  Test), „geändert“ (Größe anders oder Test neuer und Inhalt anders – gleicheBlobs), „gelöscht“ (fehlt in Test, steht aber in
  website-test/.mc4y-abgleich.json = war schon einmal abgeglichen); Übernahme mit writeFile/removeFile (Sicherung in
  editor-backups). Löschen im Test-Stand löscht nur Test; im Live-Stand Rückfrage, ob auch in Test. downloads.json-Eintrag
  nur entfernen, wenn die Datei in beiden fehlt. Geprüft mit OPFS-Probeprojekt (Listen, Umschalten, Vergleich, Übernahme, Speichern nur Test).
  SEIT 07.10.2026 NACHMITTAGS (Peter: „trenne auch die Seitentexte und Links“) sind auch die INHALTE getrennt – Überlagerung
  generator/test/<rel> (rel = inhalte/…, seiten/…, blog/…, menue.json, downloads.json) + generator/test/_teststand.json
  {geloescht: [rel], basis: {rel: {groesse, geaendert(ms)} | null}}. Test liest zuerst generator/test/, sonst generator/
  (Löschmarke = gilt als nicht vorhanden). Editor: inhPfad/inhLesen/inhSchreiben/inhLoeschen/inhListe/inhOrt, teststand()/
  saveTeststand(), inhBasis (Stand der Live-Datei beim ersten Anlegen der Testfassung), testUeberlagerung() → S.testRel →
  Abzeichen „Test“ in der Liste, docRels(d), „Testfassung verwerfen“ (verwerfeTestfassung). Alle früheren generator/…-Zugriffe
  laufen darüber (refreshAll, openPage/openPost, save, resetPage = Löschmarke im Test, deleteDoc, neue Seite/Beitrag,
  loadMenu/writeMenu (S.menuPfad), saveDlInfo). Kopfknopf #btnPromo „⇧ Nach Live übernehmen“ (nur Test) → showPromo():
  promoInhalte() je Seite/Beitrag gruppiert (neu/geändert/gelöscht/identisch, „auch in Live geändert“ über basis,
  fehlendeLiveZiele(): Verweise in Test-HTML/Markdown/Menü, deren Ziel es in website/ bzw. generator/seiten noch nicht gibt)
  + promoListe() (Dateien). Übernahme: generator/test/rel → generator/rel (Sicherung), Testfassung löschen; Löschmarke →
  Live-Datei löschen. Generator: core.inhalt_pfad/inhalt_liste/inhalt_schreibpfad/teststand/teststand_basis; core.TEST_MODE
  wird schon beim Import aus sys.argv („--test“) gesetzt (Menü/Downloads-Ordner hängen daran). pages_custom.load,
  pages_blog.load_posts, core.get_menu, core._dl_info, build.apply_overrides nutzen das. rescue_outside_changes je Ordner:
  website-test/ → generator/test/inhalte (mit teststand_basis), website/ → generator/inhalte. Getestet mit Wegwerf-
  Testfassungen (scratchpad trennung_test.py) und OPFS-Probe im Editor.
- AUTOMATISCH BAUEN VOR DEM HOCHLADEN (seit 07.10.2026, Peters Wunsch): sftp_helfer.plan_erstellen (Richtung „hoch“)
  verbindet zuerst (Passwort-/Fingerabdruck-Rückfragen des Editors wiederholen die Anfrage – so wird nur einmal gebaut),
  dann website_bauen(cfg, ordner): subprocess build.py bzw. build.py --test (nur wenn Quelle = website bzw. website-test
  passend zu cfg.art), Abbruch ohne Upload bei „FEHLER beim Bauen“, defekte Links → Warnung in plan.hinweise, Antwort
  plan.bau {gebaut, zusammenfassung} → grüne .okbox in renderPlan. Daten-Schalter bauen (Standard True). Der laufende
  Helfer muss nach Updates neu gestartet werden (SFTP-Helfer.bat).
- Reiter „Server“ (seit 06.10.2026, Peters Wunsch): Hoch-/Herunterladen per SFTP, ganze Website oder Auswahl aus
  Verzeichnissen UND einzelnen Dateien (Peters ausdrücklicher Wunsch). Auswahl als DATEIBROWSER wie Explorer/WinSCP
  (Peter: „Auswahlbox, die sich innerhalb der Verzeichnisse öffnen lässt“ – ein aufklappbarer Baum war ihm zu versteckt,
  Klick auf den Ordnernamen hakte dort den Ordner an statt ihn zu öffnen): Pfadleiste #fbPfad (data-geh), Liste #fbListe
  des geöffneten Ordners SV.cwd[treeKey()] mit „Alle Dateien in diesem Ordner“, „⬆ eine Ebene höher“, Ordnern (Name =
  öffnen, Kästchen = ganzer Ordner) und Dateien (ganze Zeile = Kästchen, Größe); Auswahl-Liste #fbAuswahl über alle
  Ordner (× entfernen, Klick = Ordner öffnen, „Auswahl aufheben“). Schlüssel in SV.auswahl: "d:<pfad>" Ordner rekursiv,
  "n:<pfad>" nur Dateien des Ordners ("n:" = Hauptverzeichnis), "f:<pfad>" einzelne Datei; auswahlSetzen() entfernt
  enthaltene Einträge, abgedeckt() sperrt sie in der Anzeige; bereiche() → {pfad, rekursiv} bzw. {pfad, datei:true}.
  Knoten SV.baum[key][pfad] = {dirs, files:[{name, groesse}]} (lokal_liste/liste, bei Bedarf nachgeladen). Filterfeld
  #svFilter wirkt auf die Dateien des geöffneten Ordners (angehakte bleiben sichtbar).
  Helfer: Port EXKLUSIV (Klasse Dienst: allow_reuse_address False + SO_EXCLUSIVEADDRUSE – unter Windows hatte
  SO_REUSEADDR eine zweite Instanz auf demselben Port zugelassen und Anfragen verteilt); Doppelstart wird erkannt
  (laufende_instanz() fragt die Instanz aus der Token-Datei ab und beendet sich mit Hinweis). auftrag_ausfuehren:
  jeder Fehler auch beim Start beendet den Auftrag (sonst blieb „läuft“ für immer und blockierte weitere Aufträge).
  TESTS: nie die echte .sftp-helfer.json oder sftp/ benutzen – Testinstanz mit MC4Y_SFTP_TOKENDATEI, MC4Y_SFTP_ORDNER
  und MC4Y_SFTP_DATEN in den Scratchpad umlenken (sonst kann ein Test Peters sftp/stand-*.json überschreiben oder
  seine laufende Instanz stören).
  Browser kann kein SFTP → lokaler Dienst `generator/sftp_helfer.py` (Start `SFTP-Helfer.bat`, Fenster offen lassen;
  braucht paramiko, am 06.10.2026 mit Peters OK per `py -m pip install --user paramiko` installiert, 5.0.0). Läuft auf
  127.0.0.1:8765–8775, schreibt Port + Zufallsschlüssel in `.sftp-helfer.json` (Projektwurzel, beim Beenden gelöscht);
  Editor liest die Datei über den Ordner-Handle und schickt POST text/plain {aktion, token, …} (Aktionen info, zugang,
  zugang_speichern/_loeschen, test, liste, lokal_liste, plan, start, status, abbrechen). Prüft Host-Header (DNS-Rebinding)
  und Origin (null/localhost). Einstellungen OHNE Passwörter in `sftp/einstellungen.json` {ablage, test, live: {name, art,
  quelle, url, host, port, user, basis, schutz, nurServer}}; Passwörter nur auf Wunsch per Windows-DPAPI in
  `%APPDATA%\MC4Y\sftp-zugaenge.json` (Schlüssel user@host:port), Server-Fingerabdrücke in `%APPDATA%\MC4Y\sftp-known_hosts`
  (erste Verbindung: Bestätigung im Editor, danach Abbruch bei Änderung). Testumgebung MC4Y_SFTP_DATEN lenkt diese Ablage um.
  Schutz: Quelle muss zur Umgebung passen (Testfassung = robots „Disallow: /“ bzw. X-Robots-Tag noindex → nur „test“,
  sonst nur „live“); gesperrte Quellen unterlagen-privat, editor-backups, generator, rechtstexte, AlteSkripten, sftp,
  server-kopien; `nurServer` (blog/kommentare, team-sitzungen) nie hochladen/überschreiben/löschen, `schutz` (images,
  blog/files, .well-known, cgi-bin) nie löschen; Löschen nur auf Wunsch und nur in gewählten Bereichen. „Nur geänderte“:
  hochladen, wenn Datei auf dem Server fehlt, Größe abweicht oder SHA-256 nicht zum Stand passt (`sftp/stand-test|live.json`,
  gilt je Ziel user@host:port|basis). Upload als *.mc4y-tmp + posix_rename, chmod 644, mtime übernommen. Vorschau (plan,
  30 Min. gültig) zeigt jede Datei; Produktiv-Upload und Löschen mit Rückfrage. Downloads immer in neuen Ordner
  `server-kopien/<test|live>_<zeit>/`. Protokolle `sftp/protokolle/`. Hinweis in der Vorschau, wenn generator/ neuer als
  index.html der Quelle (vorher bauen). Geprüft mit lokalem paramiko-SFTP-Testserver (Scratchpad sftp_testserver.py,
  helfer_test.py: 34 Prüfungen) und im Editor (OPFS-Ersatzordner); gegen STRATO selbst noch NICHT getestet – beim ersten
  Einsatz Serververzeichnis mit „Verbindung testen“ prüfen. Host und Serververzeichnis sind NICHT vorbelegt (meine
  frühere Vorbelegung ssh.strato.de war falsch – STRATO-Pakete haben je einen eigenen SFTP-Host, Testpaket von Peter:
  <sftp-host>, Benutzer <sftp-benutzer>, Port 22; Server bietet publickey + password, Hostschlüssel
  <host-fingerabdruck>, am 06.10.2026 ohne Anmeldung abgefragt). Livepaket:
  Benutzer <sftp-benutzer>, Host noch von Peter einzutragen. Falsches Serververzeichnis → Fehlermeldung listet die Ordner im
  Startverzeichnis.

## Blog-Kommentare

- Je Beitrag über Kopf-Schlüssel `kommentare: ja` einschaltbar (Editor-Feld „Kommentare“, Default „aus“).
  `pages_blog.comments_section()` fügt dann am Ende des Artikels Liste + Formular ein (nur bei `kommentare: ja`).
- Vollständig serverseitig via `website/blog-kommentare.php` (JSON-Datei je Beitrag in `blog/kommentare/<slug>.json`,
  live auf dem Webserver – der Generator kennt und speichert nichts davon). Client: neuer Abschnitt am Ende von
  `website/assets/js/site.js` (Guard: `document.getElementById('kommentare-liste')`), reines `fetch()`, kein Cookie
  (Admin-Token in `sessionStorage`, Header/Feld `token`, 12 Std. gültig, `_tokens.json` auf dem Server).
- Erstmalige Passwort-Einrichtung läuft direkt im Browser über den Login-Dialog (`action=setup`, nur solange
  `_auth.json` fehlt) – kein CLI/SSH auf dem Hoster nötig.
- Spamschutz wie `kontakt-senden.php` (Honeypot + Mindestzeit), zusätzlich IP-Ratenbegrenzung; Kommentare sind
  reiner Text (`strip_tags`), keine Formatierung erlaubt.
- Löschen kaskadiert eine Ebene (Antworten auf einen gelöschten Kommentar verschwinden mit). Nur eine Antwortebene
  vorgesehen (nur Peter antwortet, keine Endlosverschachtelung).
- `blog/kommentare/` bekommt beim ersten Schreiben automatisch eine eigene `.htaccess` (`Require all denied`) –
  die JSON-Dateien (auch der Passwort-Hash) sind damit nie direkt per URL abrufbar.

## Gestaltung

- Markenfarben aus dem Logo-Banner: Navy #173F63, Gold #FFC000, Logo-Grün #82FF81, Bronze #C69A43;
  abgeleitet: --navy-deep #0F2D48, --navy-ink #0A2236, --gold-soft #FFE08A, --gold-wash #FFFBEA,
  --mist #EEF3F7, --ink #1B2A38, --ink-soft #4A5B6B, --line #D3DEE8, --bronze-text #8A6420.
- Nur Systemschriften (keine Google Fonts). Überschriften Serif-Stack, Text system-ui.
- Keine Cookies, kein Tracking, keine eingebetteten Karten/Videos, keine externen Skripte (CSP in .htaccess).
  CSP-AUSNAHMEN (seit 16.10.2026, Peters Wunsch): build.py csp_basis = allgemeine Regel aller *.html; csp_verbindungen = Liste (Dateiname,
  [Server]) → je Eintrag ein zusätzliches <FilesMatch "^name$"> NACH der allgemeinen Regel (Apache „Header set“ der späteren Regel gewinnt) mit
  csp_basis + connect-src 'self' + diesen Servern. Bisher nur vertrieb-editor.html: overpass-api.de, overpass.private.coffee, www.wikidata.org,
  query.wikidata.org. Nur Verbindungen (fetch), keine Skripte; die übrigen Seiten bleiben unverändert (.htaccess vorher/nachher verglichen, im
  Browser mit nachgebautem Header geprüft: erlaubte Server 200, andere und andere Seiten per connect-src gesperrt). Neue Ausnahmen nur auf Wunsch
  und mit Hinweis in der Datenschutzerklärung.
- Signatur: einmaliger Lichtkegel aus dem Leuchtturm im Startseiten-Hero (respektiert reduced motion).
- Responsiv getestet 320–1400 px; keine horizontale Scrollleiste; Grid-Spalten mit minmax(0,1fr).
- Seit 03.10.2026 (Feedback „Startseite zu lang, Entscheider-Motiv fehlt, Handy ohne Struktur“):
  Startseite (generator/inhalte/index.html, Sicherung editor-backups/*_startseite) ist ein kurzer Teaser: Hero mit
  Entscheider-Botschaft (8EM → Menschen nach Bedürfnissen/Fähigkeiten einbinden → motivierende, menschliche Abläufe →
  Produktivität, Kosteneinsparung; Wortlaut nach Peters Vorgabe), „Was Ihr Unternehmen davon hat“ (.benefits),
  „8EM auf einen Blick“ (.levels-mini, nicht .levels – das ist die Registerkarten-Logik in site.js), „Wie ich Sie
  unterstütze“ (.teasers, Links auf Übersichtsseiten), Blog-Vorschau, Kontaktband.
  Menüleiste (07.10.2026, Peter: „plötzlich Darstellung wie auf dem Handy“ nach neuem Menüpunkt „Experimentell“):
  .site-head .head-row max-width 1360px (breiter als --wrap 1120px); site.js fit() setzt erst .nav-tight, dann .nav-tighter
  (kleinere Abstände/Schrift, Untertitel der Marke aus), dann .nav-tightest (nur Logo ohne Schriftzug), erst dann .nav-narrow
  (Hamburger). Mit 7 Menüpunkten: ab 1280 px normal, 1180 tight, 1100 tighter, 1070 tightest, ≤ 1060 Handy-Menü (Media-Query).
  VERSIONSKENNUNG (07.10.2026): site.css/site.js werden 30 Tage gecacht (.htaccess) – Peter sah nach dem Hochladen weiter das
  alte Hamburger-Layout. Seitdem verlinkt render() assets/css/site.css?v=<sha1[:10]> bzw. site.js?v=… (core.ASSET_V,
  set_asset_versions(DIST) in build.main nach prepare_test_dir, asset_q()). Editor-Vorschau-Regex akzeptiert ?v=…. Details (Silos, Ebenen-Registerkarten,
  Theorie U, Vergleichstabelle) stehen nur noch auf 8-ebenen-modell.html; FAQ auf faq.html. Hero-Überschrift wieder h1.
  pages_main.home() ist nur noch Rückfallfassung (alt, lang).
  Handy/Tablet (≤ 1060 px oder .nav-narrow): Hamburger-Knopf (.burger, Label .mt-label „Menü“) öffnet #hauptnav als
  bildschirmfüllende Ebene (position:fixed ab --head-h, html.nav-open sperrt Scrollen), Untermenüs als Akkordeon, Bereich
  der aktuellen Seite automatisch offen, Esc/Linkklick schließt. site.js setzt --head-h. Lange Seiten (≥ 4 h2, ≥ 3
  Bildschirmhöhen, nicht Kontaktseite) bekommen per JS eine klebende Sprungleiste .toc-bar „Auf dieser Seite“ (nur im
  Browser, nicht in den HTML-Dateien → Fingerabdruck unberührt) und einen „Nach oben“-Knopf .to-top. ≤ 600 px: kleinere
  Abschnittsabstände, 16-px-Rand, Blog-Vorschau ohne Bilder, Fußzeile zweispaltig/Rechtliches als Zeile.

## SEO/GEO-Bausteine

„Auf einen Blick“-Box (.glance) je Seite, FAQ mit FAQPage-Schema, JSON-LD-@graph (ProfessionalService,
Person, WebSite, WebPage, BreadcrumbList, Service, BlogPosting, DefinedTermSet im Glossar), speakable,
llms.txt, robots.txt mit GPTBot/ClaudeBot/PerplexityBot u. a., Sitemap mit lastmod.

SEO UND GEO SIND DAUERAUFTRAG (Peter, 27.10.2026: „achte auch auf ein gutes SEO- und GEO-Ranking, damit die Webseite und
Blog-Einträge möglichst weit bei Google oder KI-Suchmaschinen auftauchen und dort genutzt werden“). Bei jeder neuen Seite und
jedem neuen Blog-Eintrag darauf achten: aussagekräftiger Titel (≤ 54 oder 59–66 Zeichen), Meta-Beschreibung 120–160 Zeichen,
Beitragsbild mit bild_alt, „Auf einen Blick“ und ggf. FAQ, passende JSON-LD-Typen, sinnvolle interne Verlinkung und sprechende
Überschriften; KI-Crawler bleiben in robots.txt erlaubt, llms.txt aktuell. tests/statisch_pruefen.py (Stufe A, Blog) meldet
Titel-/Beschreibungslängen und fehlende bild_alt – bei neuen Beiträgen ausräumen (die Hinweise zu den alten importierten
Beiträgen von 2024/2025 sind Peters Inhalt und werden nur auf Wunsch angefasst).

## Technische Regeln

- Peter nutzt Windows; Python-Version unbekannt → Code muss ab Python 3.8 laufen:
  KEINE Backslashes in f-String-Ausdrücken, keine gleichen Anführungszeichen in einzeiligen f-Strings.
  Prüfen z. B. mit einem ast-Durchlauf über alle JoinedStr/FormattedValue.
- Niemals Inhalte von Peter überschreiben: generator/blog, generator/inhalte, generator/seiten,
  generator/menue.json, generator/downloads.json, generator/test/ (Testfassung der Inhalte), website/files, website/downloads, website/assets/img,
  unterlagen-privat, editor-backups, sftp/einstellungen.json, server-kopien. Serverseitig zusätzlich (existiert nicht lokal): web/blog/kommentare/.
- `unterlagen-privat/` enthält auch urheberrechtlich geschützte Unterlagen, die Peter nur für die Arbeit in
  seinen eigenen Sitzungen bereitstellt. Nur lesen, wenn die Aufgabe es erfordert oder Peter darum bittet.
  Inhalte nur als Hintergrund nutzen und in eigenen Worten wiedergeben – nie wörtlich oder in längeren
  Passagen in Website-Texte, Blogbeiträge, Downloads, Editoren oder sonstige Ausgaben übernehmen. Nichts
  daraus nach `website/`, `website-test/` oder in ein Update-Zip kopieren, nie als Artifact/Doc veröffentlichen,
  nie an externe Dienste senden. Quellen bei inhaltlicher Anlehnung benennen.
- Auswertung der Fachbücher für das 8EM-Tool: `unterlagen-privat/_8em-notizen.md` (eigene Worte, Seitenangaben,
  Abgleich mit dem Tool, Prioritäten A–G, Leseplan). Vor Arbeiten am 8em-editor.html lesen und bei neuen
  Erkenntnissen fortschreiben. Text der PDFs lässt sich mit `py` + PyMuPDF (`import fitz`) auslesen
  (Python heißt auf Peters Rechner `py`, nicht `python`).
- Updates bisher als `mc4y-update.zip` (nur Programmdateien: Editor, .bat/.command, generator/*.py,
  site.css, site.js, blog-kommentare.php, team-sitzung.php, ANLEITUNG.md, editoren-version.py).
- Nach Änderungen: beide Builds laufen lassen, zweimal hintereinander (keine Fehlalarme), Linkprüfung grün.
- Jedes neue eigenständige Werkzeug (Peters Vorgabe, 02.10.2026) bekommt automatisch Lizenz GPL-3.0-or-later,
  Haftungsausschluss, sichtbare Build-Version und Logo in der Kopfzeile: als `*-editor.html` mit `<header>` und
  Hilfe-Fenster (helpDlg/closeHelp) bauen und `py editoren-version.py` ausführen; bei anderen Dateinamen das Skript erweitern.
- DATENSCHUTZ UND BLOG GEHÖREN ZUM AUFTRAG (Peters Vorgabe, 16.10.2026: „Muss ich Dir jetzt jedes Mal erklären …?“): Bei jedem neuen Werkzeug
  und jeder neuen Funktion OHNE Rückfrage (1) die Datenschutzerklärung nachziehen (generator/inhalte/datenschutz.html: Speichertabelle in
  Abschnitt 7, Übertragungen an fremde Server, Werkzeuglisten; rechtlich Unsicheres als <mark>; .json „aktualisiert“) und (2) einen Blog-Entwurf
  schreiben bzw. betroffene Entwürfe ergänzen (status entwurf, Bild tool-<name>.jpg); bei veröffentlichten Beiträgen die Testfassung in
  generator/test/blog anpassen. Vorher immer Sicherung in editor-backups/, danach beide Builds; im Bericht nennen, was geändert wurde.
- BROWSER-SPEICHER NUR BEI BEDARF (seit 18.10.2026, Peters Auftrag „für alle Tools umsetzen, auch 8EM-Modeller und FreeHTML-Editor“; Sicherung
  editor-backups/2026-10-18_13-46-43_indexeddb-bei-bedarf): Das bloße Öffnen eines Werkzeugs legt NICHTS im Browser an – so steht es seitdem in der
  Datenschutzerklärung (Abschnitt 7, Satz vor der Speichertabelle). Regeln für alle bestehenden und neuen Werkzeuge: (1) IndexedDB nur lesen bzw.
  löschen, wenn es die Datenbank schon gibt – kleine Hilfsfunktion `dbDa(…)` (bzw. `idbDa` in der Buchhaltung) mit `indexedDB.databases()`, ohne diese
  Funktion (sehr alte Browser) wie bisher öffnen; Schreiben legt die Datenbank an. Umgesetzt in werkzeug-kern/kern_c.js (stateGet/stateDel), Bodenanker,
  Ikigai, Inneres Team, TMK, Zielbild, Kartenarbeit (+ libLoad/libDel), Mindmap (setGet, setPut(null), ownAll), buchhaltung-quellen/b1_grund.js (kvGet),
  projekt-quellen/p1_grund.js (kvGet, dbDa) + pa_abrechnung.js (uebAlle, uebLoeschen), stammdaten-quellen/s1_grund.js (kvGet), 8em-modeller.html (kvGet,
  prjAlle, prjDbWeg), freehtml-editor/quellen/editor.html (kvGet). Schon vorher richtig: Stammdaten-Anschluss, Kanban, ITSM, Kunden, Vertrieb, Übergaben in
  der Buchhaltung. In der Suite meldet anschluss.js mc4y-uebergabe/mc4y-stammdaten in databases() als vorhanden – die Prüfung greift dort nicht störend.
  (2) Ansichtseinstellungen (localStorage) erst speichern, wenn die Person etwas verstellt: applyLayout(refit, ohneSpeichern) mit Startaufruf
  applyLayout(false, true) in werkzeug-kern-3d/k3_c4.js, 3d-systembrett-editor.html, systembrett-editor.html; Mindmap applyUi(true) beim Start und beim
  Wechsel der Fensterbreite. (3) Der Rauchtest (pruefstand.js) meldet seitdem bei JEDEM Werkzeug Speicher nach dem bloßen Öffnen als Fehler und öffnet
  Werkzeuge mit `#beispiel` zusätzlich ohne Hash (Ausnahme nur `oeffnenSpeicherErlaubt` in werkzeuge.json, bisher mc4y-editor.html). Neues Werkzeug mit
  Browser-Speicher = diese Regeln einhalten, sonst schlägt Q-11 fehl.

## MC4Y-Produktivitätssuite

- MC4Y-PRODUKTIVITÄTSSUITE (16.10.2026, Peters Auftrag „Meisterstück … zuerst ein Konzept, das Du später evtl. mit Ergänzungen meinerseits
  umsetzen darfst“, ergänzt um Wiki, Dokumentenablage mit Versionen/Archiv, Notizen, Kalender, Aufgaben): Konzept als Claude-Docs-Dokument
  „Konzept MC4Y-Produktivitätssuite“ (https://claude.ai/code/artifact/9089d7c6-f958-475d-8eda-e15c92a2a128). Kern: Toolbox mit frei
  konfigurierbaren Kategorien (Start: Business Support Tools, Produktivitäts-Tools, Personal und Coaching, Sonstige), Server NUR PHP 8 (SQLite über
  PDO für Benutzer/Rechte/Sperren/Protokoll/Module, Inhalte als Dateien), Datenordner mc4y-daten außerhalb des Webverzeichnisses nur für
  Administratoren + Betriebssystemkonto mc4y-systemuser (unter dem PHP läuft), Berechtigungsgruppen in den Stammdaten (Lesen / Lesen und Schreiben /
  Vollzugriff je Werkzeug, optional Bereiche), Sperre je Dokument (Lebenszeichen 30 s, Verfall 30 Min., Leerlauf 60 Min. – Peters Entscheidung –, Übernahme mit Vollzugriff,
  Fassungsprüfung beim Speichern), Suite-Anschluss wie der Stammdaten-Anschluss (Server-Ordner statt Dateidialog), Python nur auf Clients,
  Installationsskripte je Plattform. Umsetzung in vier Stufen mit Prüfpunkten (Stufe 1 Kern + drei Stellvertreter Systembrett/Buchhaltung/
  Stammdaten auf dem Einzelplatz Windows). Peter hat alle offenen Entscheidungen abgehakt (Docs-rev 995: u. a. Lizenz GPL-3.0; die Suite
  betreibt er nur selbst – STRATO-Test, eigenes Netz –, einen Betrieb für andere bietet er nicht an; Dokumentenablage mit Klassifizierung,
  Schlagworten, mehreren Kategorien, Archiv-ZIP, abschaltbarer Versionierung; Aufgaben-Abgleich mit Kanban/PM) und am 16.10.2026 abends
  „Baue Stufe 1“ beauftragt (siehe STUFE 1 unten). Vor jeder weiteren Stufe das Dokument lesen (Kommentare/Häkchen). Word-Kopie (Momentaufnahme, Docs-rev 995 mit Peters Bearbeitungen, wird NICHT automatisch
  nachgezogen): konzepte/Konzept MC4Y-Produktivitätssuite (Stand 2026-10-16).docx – Ordner konzepte/ (Projektwurzel) nicht hochladen.
  Maßgeblich bleibt das Docs-Dokument.
  ENTWICKLUNGSUMGEBUNG (16.10.2026 abends, Peters „Ja, installiere PHP und den IIS auf den Rechner“): IIS 10 war schon installiert (seit
  05/2026, Standard-Website auf Port 80 – UNVERÄNDERT gelassen, führt kein PHP aus). Neu: Windows-Komponente IIS-CGI (FastCGI); PHP 8.5.11
  NTS x64 (windows.php.net, SHA-256 geprüft) in C:\PHP, Rechte ohne Vererbung (Administratoren/SYSTEM Vollzugriff, Benutzer und IIS_IUSRS
  lesen/ausführen), php.ini aus php.ini-development mit extension_dir C:\PHP\ext, curl/fileinfo/mbstring/openssl/pdo_sqlite/sqlite3/sodium/zip,
  Europe/Berlin, cgi.fix_pathinfo=1, fastcgi.impersonate=1, Upload 64M, memory 256M (Visual C++ 14.51 war vorhanden). PHP NICHT im PATH –
  Aufruf `C:\PHP\php.exe`. IIS: FastCGI-Anwendung C:\PHP\php-cgi.exe (serverweit, PHPRC=C:\PHP, max. 4 Instanzen), App-Pool „MC4Y-Test“ (ohne
  .NET, ApplicationPoolIdentity), Website „MC4Y-Test“ NUR http://127.0.0.1:8080 → C:\inetpub\mc4y-test (index.php = Prüfseite: Version, SAPI,
  Erweiterungen, SQLite mit Umlauten, Argon2id); PHP-Handler nur in dieser Website (location, @start). Über die Netzadresse antwortet IIS mit 400.
  Einrichtungsskripte lagen nur im Sitzungs-Scratchpad (php_iis_einrichten.ps1, iis_php_site.ps1); Lehre: PowerShell-Hilfsfunktion nie „AC“
  nennen (Alias von Add-Content). Änderungen an IIS/C:\PHP brauchen Administratorrechte → Skript per Start-Process -Verb RunAs, Peter bestätigt
  die Windows-Rückfrage. mc4y-systemuser wurde am 17.10.2026 mit Peters Freigabe („Installiere und Prüfe …“) vom Installer angelegt (siehe
  INSTALLATION unten).
  STUFE 1 (16.10.2026 abends, Peters „Baue Stufe 1“): Ordner mc4y-suite/ in der Projektwurzel (nie eigenmächtig hochladen; LIESMICH.md dort).
  app/ (nicht abrufbar): kern.php (konfig.php neben app/, ersatzweise php -d mc4y_suite_daten=… oder Umgebung MC4Y_SUITE_DATEN; SQLite
  <daten>/system/suite.sqlite mit WAL; EINSTELLUNGEN_STANDARD; SuiteFehler; ist_lokal = Absender UND aufgerufener Host lokal und keine
  Proxy-Kopfzeilen), konto.php (Argon2id, Cookie mc4y_suite HttpOnly/SameSite=Strict, Sitzungs-id als sha256, CSRF-Schlüssel je Sitzung –
  anmelden() liefert ihn als $b['_csrf'], NICHT über „neueste Sitzung“ suchen –, Kontosperre nach 5 Fehlversuchen, 20 Versuche je IP/15 Min.;
  Fehlanmeldungen speichern eingegebene Namen unbekannter Konten NICHT; Einrichtungscode system/einrichtungscode.txt nur ohne lokalen Zugriff
  nötig, wird nach der Einrichtung mit Wiederholungen gelöscht), rechte.php (Gruppen Stufe 1–3 je Werkzeug und Bereich, Gruppen können einer
  Stammdaten-Rolle folgen; Administratoren haben KEINE automatischen Inhaltsrechte), sperren.php (je Dokument und Browser-Tab;
  uebernehmen_erlaubt: eigene Sperre immer, fremde mit Vollzugriff oder als Admin – auch in sperrstatus/puls geliefert; Lebenszeichen holt
  freie, unveränderte Sperren still zurück), ablage.php (Datei-Dokumente; Ordner-Dokumente mit .mc4y-dokument.json über dok_*; Fassungen in
  versionen/; Papierkorb mit eintrag.json inkl. Name; name_pruefen/pfad_pruefen – Leerraum wird zu einem Leerzeichen, Steuerzeichen/..
  /Punkt am Anfang/reservierte Windows-Namen abgelehnt), bestand.php (gemeinsame Bestände mit rev + flock; veraltete rev → {konflikt, daten,
  rev}, das Werkzeug führt zusammen), verwaltung.php, start.php (AKTIONEN, Zugang o/l/a/v), router.php (nur für php -S).
  public/: index.php (Portal), api.php, datenschutz.php (Organisation und Fristen aus den Einstellungen), assets/portal.js (Einrichten,
  Anmelden, Toolbox, Konto, Verwaltung; ?eingebettet=1; räumt beim Abmelden – mit Rückfrage – und bei Anmeldung einer ANDEREN Person die
  localStorage-Zwischenstände der Werkzeuge weg: Schlüssel mit entwurf|autosave|zwischenstand, Konzept „Übergang“), assets/anschluss.js
  (window.MC4YSuite; Server-Dialog statt Dateidialog, Doppelklick öffnet – Auswahl ohne Neuzeichnen, sonst geht der Doppelklick verloren;
  Pille/Tafel mit Zustand, Lebenszeichen, Leerlauf-Rückfrage, Freigabe beim Schließen per sendBeacon; Hook MC4Y_SUITE_HOOK {geaendert,
  oeffnen(h), aktuell()} und MC4YSuite.dokumentGewechselt(): Sperre wird frei, wenn das Werkzeug ein anderes Dokument lädt – 15 s Schonfrist
  nach dem Öffnen), werkzeuge/ = ERZEUGT von `py mc4y-suite/bauen.py` (alle Werkzeuge mit eingesetztem Anschluss-Script data-werkzeug/
  -ablage/-endung/-suite; Tabelle WERKZEUGE dort: suite=True bisher nur systembrett, buchhaltung, stammdaten; schreibt auch app/katalog.json).
  Nach jeder Werkzeug-Änderung bauen.py laufen lassen.
  Werkzeug-Seite: systembrett-editor.html (Block „MC4Y-Produktivitätssuite“ vor „Start“, Beispiel/Neu rufen dokumentGewechselt), buchhaltung-
  quellen (ServerAblage in b1, suiteAktiv/ablageMitDateien, Start-Dialog-Zweig + #startIntro in b7, ACT srv* in b11), stammdaten-quellen (s2
  suiteSchreiben/suiteNachladen, s6 suiteStart, für Admins Ansichten „Benutzer“/„Berechtigungen“ als iframe ins Portal). Außerhalb der Suite
  ist dieser Code inaktiv.
  TESTS: `C:\PHP\php.exe mc4y-suite\tests\api_test.php` (134 Prüfungen; eigener Server, Wegwerf-Datenordner in %TEMP%; --behalten;
  `--ziel=http://127.0.0.1:8090` prüft eine laufende, noch nicht eingerichtete Installation von außen: 129 Prüfungen, 5 mit Blick in den
  Datenordner entfallen (nurLokal/fs()), Sperrverfall echt über sperre_verfall_min = 1 + 62 s Warten, danach wieder 30), Vorschau
  „mc4y-suite“ in .claude/launch.json (Datenordner C:\Users\<benutzer>\AppData\Local\MC4Y-Suite-Entwicklung – nie den Datenordner einer echten
  Installation). Browsertest 16.10.2026 mit zwei Benutzern in zwei Ursprüngen (localhost = Admin, 127.0.0.1 = Coach mit Schreiben):
  Systembrett (nur lesen mit Namen, Speichern verweigert, Freigabe → „Jetzt ist es frei“, Übernahme → „verloren“, Kopie über Speichern unter,
  Fassungen, Papierkorb), Buchhaltung (neue Server-Ablage, Autospeichern, zweite Person nur lesen, Freigabe, Binärdateien), Stammdaten
  (gleichzeitige Änderungen und ein erzwungener Konflikt zusammengeführt, Verwaltung eingebettet) – ohne Datenverlust (= Prüfpunkt 1).
  INSTALLATION Windows 10/11: install/windows/installieren.bat/.ps1 (nur ASCII; -Probelauf ändert nichts; -Aktualisieren; -PhpZip mit
  -PhpSha256): Konto mc4y-systemuser (Kennwort erzeugt und einmal angezeigt; nicht in „Benutzer“; per secedit Anmeldung am Bildschirm/RDP/
  übers Netz verweigert, Ausführen als Aufgabe erlaubt), Programm C:\Program Files\MC4Y-Suite, Daten C:\ProgramData\MC4Y-Suite-Daten (icacls
  ohne Vererbung: Administratoren, SYSTEM, Konto), konfig.php nur für Administratoren/SYSTEM/Konto lesbar, Aufgabe „MC4Y-Suite“ beim
  Systemstart: php -S 127.0.0.1:8090 (8090, damit nichts mit dem Entwicklungsserver 8095 kollidiert), .url-Verknüpfung auf Desktop und im
  Startmenü (alle Benutzer), Browser über explorer.exe (nicht als Administrator). deinstallieren.ps1 lässt den Datenordner stehen (außer
  -DatenLoeschen + Eingabe „LOESCHEN“); zuruecksetzen.bat/.ps1 (17.10.2026) leert nur den Inhalt des Datenordners nach Eingabe „LOESCHEN“
  und startet die Aufgabe neu (Ordner/ACL/Konto bleiben). INSTALLIERT am 17.10.2026 08:13 auf Peters Rechner (Protokoll C:\Program Files\
  MC4Y-Suite\installation_2026-10-17_08-13-17.log): alle Schritte ohne Fehler, PHP 8.5.11, secedit-Rechte gesetzt. Von außen geprüft: lauscht
  nur auf 127.0.0.1:8090 (Netzadresse nicht erreichbar, „localhost“ geht), php.exe läuft unter fremdem Konto (für <benutzer> nicht einsehbar),
  Datenordner und konfig.php für <benutzer> gesperrt, Konto nicht in „Benutzer“, CSP/nosniff/SAMEORIGIN, Verknüpfungen Desktop (öffentlich) und
  Startmenü; api_test --ziel: 129 bestanden, 0 fehlgeschlagen. Danach ist die Installation mit WEGWERF-Konten eingerichtet (Organisation
  „Testbetrieb“, Kennwort unbekannt) – das Zurücksetzen per Administrator-Skript hat Peter in der Windows-Rückfrage abgelehnt; er setzt selbst
  mit zuruecksetzen.bat zurück (oder auf seine Anweisung erneut versuchen). Die 5 Datenordner-Prüfungen (Einrichtungscode, fehler.log …) sind
  für die Installation noch offen – bei der nächsten erhöhten Ausführung mitprüfen (Scratchpad-Skript suite_zuruecksetzen.ps1 tat beides).
  Blog-Entwurf generator/blog/2026-10-16-tool-mc4y-suite.md, Bild tool-mc4y-suite.jpg (Edge-Aufnahmen statischer DOM-Abbilder; die Abbilder
  schrieb die Seite über die Suite selbst in eine Test-Ablage, weil die CSP Verbindungen zu fremden Adressen verbietet). Website-
  Datenschutzerklärung unverändert: Die Suite steht nicht auf der Website, die geänderten Werkzeuge verarbeiten außerhalb der Suite nichts
  Neues; Hinweise für Suite-Benutzer stehen in public/datenschutz.php.
  STUFE 2 (17.10.2026, Peters „Starte Stufe 2, wie von Dir vorgeschlagen“): alle 48 Werkzeuge angeschlossen, OHNE die Werkzeug-Dateien zu ändern
  (keine neuen Build-Stempel, Website-Downloads unberührt). Der Anschluss (public/assets/anschluss.js) bildet in der Suite die Browser-
  Schnittstellen nach: showOpen/SaveFilePicker (Speichern geht an den Server, wenn der vorgeschlagene Name auf .json bzw. die Endung endet;
  Ziel-Ablage nach Endung eines verwandten Werkzeugs; Server endung_pruefen erlaubt seitdem jede .json-Datei), <input type=file> mit JSON-accept
  (Prototyp-click + Klick-Abfang, DataTransfer), Mehrfachauswahl (Strg/Umschalt), IndexedDB-put/result für gemerkte Datei-/Ordnerverweise
  (Merkzettel __mc4ySuiteDatei; SuiteDatei.wieder → requestPermission/erstes Speichern holt die Sperre per api 'sperre', nur bei unveränderter
  Fassung, sonst Hinweis + „Speichern unter“), Band „Nur lesen“ unten rechts über der Pille (bandZeigen, je Zustand ausblendbar), S.oeffnen(pfad),
  S.mitVorgabe(handle, fn) = nächster Dialog/nächste Dateiauswahl liefert das vorgegebene Dokument. bauen.py: HAKEN je Familie (aktuell, Öffnen-
  Befehl) → Inline-Skript /*MC4Y-SUITE-HAKEN*/ vor </body>, Namen werden im Werkzeug geprüft (H_2D fileHandle/openDoc, H_3D fileHandle/openFile,
  H_BS datei.handle/dateiOeffnen, Fragebögen #fileLoad, Mindmap, 8EM root/act('startOpen'), FreeHTML S.root/openProject); EIGENER_HAKEN
  (systembrett, buchhaltung, stammdaten); VERWANDT (3D↔2D, PM↔Kanban, Projekt-Auswertung→PM, START_ABLAGE); ORDNER_EMU (8EM, FreeHTML,
  data-ordner="1"): showDirectoryPicker → Dialog „Ordner wählen“ (Dokument öffnen, „＋ Neues Dokument“, „Diesen Ordner wählen“) → Klassen
  SuiteAblageOrdner (Ablage-Ordner; Unterordner anlegen = dok_neu) / SuiteDokOrdner / SuiteDokDatei mit getDirectoryHandle/getFileHandle/entries/
  removeEntry/createWritable/queryPermission/requestPermission/resolve; Ordnerbaum 4 s zwischengespeichert (dok_liste baum=1, baumSetzen) –
  8EM-Modell öffnen 0,8 s statt 15 s. Server neu: dok_liste baum/flach, dok_info art, dok_ordner, dok_loeschen rekursiv (baum_loeschen).
  ÜBERGABEN: IndexedDB 'mc4y-uebergabe' wird in der Suite nachgebildet (UebDatenbank/UebTransaktion/UebSpeicher: get/put/add/delete/getAll/
  getAllKeys/count über Präfix-Bereich; Blobs/Dateien/Daten als __mc4yBlob/__mc4yDatum) → app/uebergabe.php (DB-Fassung 2: Tabellen uebergaben,
  uebergabe_ereignisse; UEB_TEILNEHMER = welche Werkzeuge welche Art r:/s:/k:/kb:/vc:/vcs:/b:/pa:/ps:/kst:/kst-wunsch lesen (≥1) bzw. schreiben
  (≥2)); BroadcastChannel: window.BroadcastChannel = SuiteKanal (merkt Kanäle der Seite), postMessage → ueb_melden; ueb_ereignisse alle 8 s →
  dispatchEvent an die Kanäle der Seite (nur fremde Sitzungen). STAMMDATEN-ANSCHLUSS: IndexedDB 'mc4y-stammdaten' Schlüssel 'daten' = Bestand
  auf dem Server (SdSpeicher; übrige Schlüssel echt im Browser über echtKv), Schreiben mit Fassung, Konflikt → misch3 (Drei-Wege je id/Schlüssel,
  Listen einfacher Werte als Menge) und erneut; bestand_stand (Fassung + Recht, ohne Fehler) alle 30 s → Nachricht 'geaendert' an Kanal
  'mc4y-stammdaten'; bestand_lesen still=1 (kein „Zuletzt“). TEAM-SITZUNGEN: fetch auf …/team-sitzung.php → api.php?a=team (app/team.php, Port
  der Website-Fassung mit ts_*-Funktionen, Daten in daten/team-sitzungen, Recht ≥1 auf 'analyse', anlegen/schreiben ≥2). BROWSER-DATEN: Portal
  löscht beim Abmelden (Dialog mit Häkchen, abwählbar) bzw. immer bei Benutzerwechsel alle localStorage-Schlüssel außer mc4y-suite-*, *-layout/
  -palette/-palette-reihenfolge/-ansicht/-ui und mc4y-stammdaten-anschluss, sessionStorage und ALLE IndexedDB-Datenbanken der Herkunft.
  GEPRÜFT (Entwicklungs-Suite, Konten admin/coach/leser, Kennwort nur im Sitzungs-Scratchpad): api_test 135/135; Prüfskript im iframe für alle
  36 Datei-Werkzeuge (speichern über den Dialog, neu laden, S.oeffnen, erneut speichern ohne Dialog, neue Fassung) + 7 Fragebögen (Beispiel
  speichern, über #fileLoad wieder öffnen) – alle bestanden; Leser: Band + keine neue Fassung; Wiederherstellen eines gemerkten Kanban-Verweises
  nach Neuladen (speichert) und mit inzwischen geänderter Server-Fassung (verweigert, „Speichern unter“); 3D → „Als 2D speichern“ in der 2D-Ablage;
  Projekt-Auswertung liest aus der PM-Ablage ohne Sperre; Übergabe mit Datei + Nachricht zwischen zwei Benutzern, nicht beteiligtes Werkzeug
  abgewiesen; Stammdaten: Anlage aus Kanban, zwei parallele Anlagen zusammengeführt, Änderung eines anderen nach 30 s im Werkzeug; Abmelde-Dialog
  (33 Speicherbereiche gelöscht, Ansichtseinstellungen bleiben); 8EM: Modell anlegen (37 Ordner), wieder öffnen, speichern mit Sicherung, zweite
  Person nur lesen; FreeHTML: neue Website im neuen Dokument; Team-Sitzung zwischen zwei Benutzern (PIN, Entschärfung, beenden). Gefundener und
  behobener Fehler: Bestätigen im Dialog, bevor die Liste geladen war (ersteLadung).
  GRENZEN/OFFEN: Dokumentenordner des Kunden-Navigators und Projektordner der Mindmap bleiben lokal (in der Suite Ablage „intern“ nutzen),
  eigene Symbole der Mindmap nur im Browser; Firmensuche des Vertriebs-Navigators in der Suite gesperrt; Sicherung als ZIP, IIS/Linux/macOS/
  Webhosting (Stufe 4). Die INSTALLIERTE Suite (C:\Program Files) wurde am 17.10.2026 auf 0.2.0 (Stufe 2) aktualisiert (Datenordner leer –
  Peter legt seinen ersten Administrator selbst an); zum Aktualisieren immer installieren.ps1 -Aktualisieren (Administrator, nur mit Peters OK).
  STUFE 3 (17.10.2026, Peters „Ja, aktualisiere die Suite und starte mit Stufe 3“; SUITE_VERSION '0.3.0 (Stufe 3)', DB_SCHEMA 7): ARBEITSMODULE
  als Teil des Portals – Oberfläche public/assets/module/<id>.js (registriert über MC4YPortal.modul(id, {seite, weiter, verlassen, ungesichert}),
  Adresse #/m/<id>/…; #/ = Arbeitsplatz, #/toolbox = Toolbox), Server app/<modul>.php mit Aktionen in start.php AKTIONEN (roh = Rohdaten-Upload,
  Parameter dann über $_GET). bauen.py: Tabelle MODULE (dokumente, wiki, notizen, aufgaben, kalender) → Katalog, nur wenn die Oberfläche existiert;
  index.php meta mc4y-module (Dateizeitstempel). Rechte wie Werkzeuge (Gruppen, Stufe 1–3, Bereich = Bibliothek[/Ordner] bzw. Wiki-Bereich bzw.
  gemeinsamer Kalender; Modulzugang über werkzeug_rechte = Maximum über alle Bereiche); aktion_kontext liefert 'module'. Portal-Hilfen in portal.js:
  sicherer Markdown-Renderer (erst escapen; [[…]]-Rückruf, Aufgaben-Kästchen, mdHaken), Zeilen-Diff (LCS), frage/eingabe, drucken (#druckbereich),
  roh (XHR mit Fortschritt), bake (sendBeacon), TAB; Glocke (ml_liste alle 60 s, kal_erinnerungen alle 5 Min. → Notification nur bei offener Suite).
  SCHEMA: 3 Dokumentenablage (bib, bib_ordner, bib_dok, bib_version, bib_kategorie, bib_dok_kat, bib_ansicht, bib_fts; Einrichtungsgruppe → '*'),
  4 Wiki (wiki_bereich, wiki_seite mit eindeutigem Titel je Bereich NOCASE, wiki_version, wiki_link, wiki_fts), 5 Notizen (notiz, notiz_freigabe,
  notiz_mein, notiz_fts), 6 Aufgaben (aufgabe, aufgabe_kommentar), 7 Kalender + Meldungen (kalender, termin, termin_teilnahme, meldung). FTS5 mit
  unicode61 remove_diacritics 2, sonst LIKE.
  • DOKUMENTENABLAGE (app/dokumente.php da_*, Datei-Ausgabe public/datei.php nur GET – inline nur sichere Typen, sonst CSP sandbox; Dateien
    daten/dokumente/<bib>/<dok>/<h>.<n>-<sha8>, Archiv daten/dokumente/archiv/<bib>.zip mit Prüfsummen): Bibliotheken mit Ordnern, eigenen Spalten
    (Typ-Prüfung), Kategorien, gespeicherten Ansichten, Hochladen per Ziehen (409 'vorhanden' → neue Fassung), Vorschau, Fassungen x.y (Neben-/
    Hauptfassung, Kommentar, Autor, Wiederherstellen, Textvergleich, max_versionen je Bibliothek, Versionierung je Dokument abschaltbar), Aus-/
    Einchecken, Klassifizierung (Standard intern), Schlagworte, Volltext auch aus docx/odt/ods/odp/xlsx/pptx (ZipArchive), Papierkorb, Archivieren
    (schreibgeschützt, endgültig löschen erst nach aufbewahren_bis), da_fristen. Bibliothek umbenennen zieht Rechte-Bereiche mit. Deep Link #/m/dokumente/d/<id>.
  • WIKI (app/wiki.php wiki_*): Bereiche mit Seitenbaum (Ziehen = umhängen, Zyklusprüfung), Markdown, [[Seite]], [[Bereich:Seite]], [[dok:Nr]],
    Rückverweise, rote Verweise legen an, Umbenennen passt Verweise an (außer in von anderen gesperrten Seiten), Sperre wie Werkzeuge (Pfad
    '<Bereich>/<id>'), Konflikt bei veralteter rev, Entwurf localStorage mc4y-wiki-entwurf-<id> (beim Abmelden gelöscht), Versionen + Diff,
    Papierkorb (Kinder rücken hoch), Suche, Drucken/PDF, Export ZIP (datei.php?wiki=<bereich>). Kein ':' im Bereichsnamen.
  • NOTIZEN (app/notizen.php nz_*): Markdown mit abhakbaren Aufgaben, Farben NZ_FARBEN, Schlagworte, Anheften je Person, Verweise (dok, wiki,
    werkzeug, aufgabe, url nur http/https), Teilen mit Personen/Gruppen (lesen/bearbeiten) bzw. „alle“ (nur mit Vollzugriff), Meldung an neu
    Berechtigte, Archiv/Papierkorb; Teilupdates behalten nicht gesendete Felder. SCHNELLERFASSUNG in jedem Werkzeug: anschluss.js-Tafel
    „🗒 Schnell festhalten“ → Notiz oder Aufgabe (erste Zeile = Titel), wahlweise mit Verweis auf das offene Dokument, Strg+Enter.
  • AUFGABEN (app/aufgaben.php af_*): Status offen|arbeit|wartet|erledigt, Priorität 0–3, Zuständige, Fälligkeit, Unteraufgaben, Kommentare (+Meldung),
    Anhänge aus der Dokumentenablage, Ansichten heute/woche/ueberfaellig/alle/erledigt/papierkorb, Filter ich/vergeben/alle. WERKZEUG-AUFGABEN:
    af_werkzeug_aufgaben liest daten/ablage/kanban/**.kanban.json und projektmanagement/**.projekt.json (Person über doc.stammdaten.p[lid].sd =
    benutzer.person, sonst gleicher Name); aktion_af_werkzeug_status schreibt zurück (json_decode als Objekte, version_sichern + atomar_schreiben,
    423 bei Sperre; Kanban-Regeln sperrt/erfuellt, WIP/DoR/DoD warnen|sperren|aus; PM backlog status/verlauf/erledigtAm bzw. plan status/fortschritt/
    istStart/istEnde). Deep Link #/m/aufgaben/a/<id>; Ansicht in localStorage mc4y-aufgaben-ui.
  • KALENDER (app/kalender.php kal_*, ICS public/kalender.php: ?abo=<48 hex> ohne Anmeldung – private Termine als „belegt“ – bzw. ?id= mit Sitzung):
    persönlicher Kalender „Mein Kalender“ (kal_eigener) + gemeinsame Kalender (Bereich = Name), Monat/Woche/Tag/Liste, Serien taeglich/woechentlich
    (Tage)/monatlich/jaehrlich mit bis/anzahl/Ausnahmen, einzelnes Vorkommen ändern/löschen (Ausnahme + Einzeltermin), Teilnehmende mit Zusage und
    Meldungen (Einladung, Absage, Antwort), private Termine, Erinnerungen, Fristen-Ebenen (Aufgaben, Kanban/Projekte, PM-Meilensteine), .ics-Export/
    -Import (TZID/UTC/DATE, RRULE FREQ/INTERVAL/COUNT/UNTIL/BYDAY, EXDATE; vorhandene UID übersprungen), Abo-Link an/aus. localStorage mc4y-kalender-ui.
  • MEIN ARBEITSPLATZ (app/arbeitsplatz.php ap_start, suche_alles): Termine heute/morgen, fällige eigene Aufgaben + Werkzeug-Aufgaben, angeheftete
    Notizen, zuletzt geänderte Dokumente/Wiki-Seiten/Werkzeug-Dokumente, Favoriten, Meldungen; Suche über alles (Dokumente, Wiki, Notizen, Aufgaben,
    Termine ohne private, Werkzeugnamen, Dateinamen der Werkzeug-Ablagen). MELDUNGEN (meldung_neu, höchstens 200 je Person, 90 Tage).
  public/datenschutz.php um die Module, Browser-Speicher und den Abo-Link ergänzt. LIESMICH.md Abschnitt „Stufe 3: Arbeitsmodule“.
  GEPRÜFT: api_test 349/349 (Abschnitte „Stufe 3: Dokumentenablage/Wiki/Notizen/Aufgaben/Kalender, Meldungen, Arbeitsplatz“, u. a. Rechte je Bereich,
  Konflikte, Sperren, Archiv-Prüfsummen, Kanban-Regeln beim Zurückschreiben, ICS-Rundlauf); im Vorschau-Browser mit Maus und Tastatur alle Module
  (Hochladen, Fassungen, Auschecken, Wiki-Verweise/Umbenennen/Baum ziehen, Notiz-Kästchen, Aufgaben + Kanban-Rückschreiben, Serien, Einladung →
  Glocke, Schnellerfassung im Werkzeug), 375 px ohne Querscrollen (13 Ansichten). Behobene Fehler u. a.: Listenwechsel ul→ol und '---' nach '|'-Zeile im
  Markdown, Teilupdate von Notizen, Kalenderzugang nur mit Bereichsrechten (kal_modul). NICHT geprüft: echte Benachrichtigungen des Betriebssystems,
  Abo-Link in Outlook/Thunderbird/Handy, Kalender-Import echter Exporte großer Programme. Installiert ist seit 17.10.2026 14:02 Fassung 0.3.1 (siehe unten).
  OFFEN ab Stufe 4: WebDAV/Laufwerk, CalDAV, Anschluss der Dokumentenablage an die Werkzeuge (Anhänge), E-Mail-Benachrichtigungen, Sicherung als ZIP,
  Betrieb auf IIS/Linux/macOS/Webhosting.
  KACHELN ODER TASKLEISTE (17.10.2026, Peters Wunsch „Art Taskleiste … einstellen, ob Taskleiste oder Kachel-Ansicht; in der Kachel-Ansicht ein kleiner
  Text, der erläutert, wofür die Kachel steht, mit Mouse-Over der vollständige Text“; SUITE_VERSION '0.3.1 (Stufe 3)', DB_SCHEMA 8): benutzer.ansicht (JSON,
  ANSICHT_WERTE in konto.php: toolbox kacheln|taskleiste, leiste unten|oben|links, beschriftung symbole|namen; ansicht_lesen füllt Standardwerte = jeweils
  erster Wert), Aktionen 'ansicht' (nur genannte Teile) und 'favoriten_ordnen' (liste = gewünschte Reihenfolge; Unbekanntes/Doppeltes fällt weg, Rest
  bleibt hinten). Angeheftet = Favoriten (eine Liste für Stern und Taskleiste). KATALOG (bauen.py): je Werkzeug INFO {datei: (Symbol, ausführliche
  Beschreibung)} und je Modul MODUL_TEXT → katalog.json Felder 'symbol' und 'text' (Hinweis beim Bauen, wenn INFO fehlt; api_test prüft, dass jedes
  Werkzeug/Modul beides hat – neue Werkzeuge also IMMER in WERKZEUGE und INFO eintragen); Kurztexte 'kurz' teils aussagekräftiger. Ablageart 'ohne'
  (Werkzeug ohne Dokumente, z. B. Taschenrechner): suite = True, oeffnen = False, anschluss.js zeigt „legt keine Dokumente an“. PORTAL (portal.js):
  ansicht()/ansichtSetzen, ziele(id)/knoepfeHtml/ablageMarke; Kacheln mit .info[data-tip] (Text gekürzt per line-clamp 3, vollständig im Tooltip .wtip –
  450 ms Verzögerung beim Darüberfahren, sofort bei Tastaturfokus, Antippen auf Touch; Rechtsklick = Werkzeug-Menü); Toolbox-Kopf mit Umschalter .seg
  „▦ Kacheln | ▭ Taskleiste“ und Link zu Konto › Darstellung (#/konto?darstellung=1, Radiogruppen, speichern sofort). TASKLEISTE (#taskleiste, nur
  angemeldet, nicht eingebettet, nicht bei Kennwortwechsel; body.tl-an/tl-unten|tl-oben|tl-links/tl-namen; unten fixed, oben sticky unter der Kopfzeile,
  links fixed 60 bzw. 204 px – unter 600 px immer unten): Start-Knopf → .tl-menue (Suche mit Rang Name-beginnt > Name-enthält > Kurztext > Beschreibung,
  Enter öffnet den ersten Treffer, ↑↓; Angeheftet + Kategorien als <details> – auf dem Handy zugeklappt; 📌 je Eintrag; rechts Zuletzt bearbeitet, Links,
  Abmelden), angeheftete Werkzeuge als Links (Module per Hash, Werkzeuge in neuem Tab; .an = gerade geöffnetes Modul), Umordnen per Zeiger-Ziehen (nicht
  HTML5-DnD; klickSperreBis verhindert das Öffnen nach dem Ziehen), Rechtsklick-Menü .tl-kmenue (Neu/Öffnen/Öffnen …, anheften/lösen, nach links/rechts,
  Beschreibung als Dialog), Uhr/Datum → Kalender. In der Taskleisten-Darstellung zeigt #/toolbox eine Liste (.wliste/.wzeile mit 📌). tb wird bei An-/
  Abmeldung verworfen. Suche über alles (arbeitsplatz.php) durchsucht auch 'text'. GEPRÜFT: api_test 361/361 (neu: Abschnitt „Darstellung … und angeheftete
  Werkzeuge“ inkl. Katalog-Vollständigkeit, Taschenrechner/UML im Katalog); im Vorschau-Browser mit der Maus: Mouse-over-Text an einer Kachel, Umschalten
  auf Taskleiste, Anheften in der Liste und im Start-Menü, Suche „rech“, Ziehen des UML-Editors an die erste Stelle (Reihenfolge nach Neuladen erhalten,
  kein Tab geöffnet), Rechtsklick › nach links, Kalender aus der Leiste (aktiv markiert), Konto › Darstellung links + Namen und oben, 375 px (Leiste unten,
  Start-Menü ohne Überlappung, kein Querscrollen), eingebettete Verwaltung ohne Leiste; UML-Editor in der Suite (Speichern in die Ablage, Öffnen per
  #suite-oeffnen), Taschenrechner in der Suite (Tafel-Text). Testdaten danach entfernt (Favoriten, Darstellung zurück, UML-Testdatei im Papierkorb der
  Entwicklungs-Suite). Datenschutzhinweise der Suite (public/datenschutz.php) nennen Darstellung und angeheftete Werkzeuge. Die installierte Suite hat
  diesen Stand noch nicht (installieren.ps1 -Aktualisieren mit Peters OK).
  INSTALLIERT 0.3.1 (17.10.2026 14:02, Peters „Installiere den neuesten Stand auf meinen Rechner“): Die Installation war schon von Peter eingerichtet
  (Organisation „MC4Y P. Hoenig“, 2 Benutzer) – daher NUR installieren.ps1 -Aktualisieren, kein Leeren, KEIN api_test --ziel gegen seine Installation.
  Vorher Aufgabe angehalten und system/suite.sqlite gesichert nach C:\ProgramData\MC4Y-Suite-Daten\_update-sicherung\2026-10-17_14-01-51_vor-0.3.1
  (im geschützten Datenordner; Peter kann sie nach erfolgreicher Nutzung löschen). Datenbank beim ersten Aufruf von Fassung 2 auf 8 umgestellt
  (Spalte benutzer.ansicht vorhanden, Benutzer unverändert 2), kein fehler.log, Portal 200, 50 Werkzeuge inkl. UML/Taschenrechner, alle Module,
  php.exe als mc4y-systemuser. Skript dafür nur im Sitzungs-Scratchpad (suite_update_031.ps1). Künftige Aktualisierungen genauso: Daten sichern,
  -Aktualisieren, nur lesend prüfen.
  TASKLEISTE NEU (17.10.2026 nachmittags, SUITE_VERSION '0.3.2 (Stufe 3)', Peters Korrektur „Bei der Statusleiste haben wir uns missverstanden: erst nur die
  Kategorien, ein Klick zeigt die zugehörigen Module; Anordnung vertikal und horizontal; frei verschiebbar“ – „Statusleiste“ = Taskleiste). ERSETZT den
  Start-Menü-/Anheften-Teil oben (tl-unten/oben/links, .tl-app, .tl-menue gibt es nicht mehr). konto.php: ANSICHT_WERTE toolbox kacheln|taskleiste,
  ausrichtung waagerecht|senkrecht, beschriftung namen|symbole (Standard jetzt namen) + ANSICHT_LAGE x/y (Zahl 0–1 = Anteil an Fensterbreite/-höhe ohne
  Bildlaufleiste, auf 4 Stellen gerundet, null = Standardplatz; Text/Bool/außerhalb → Fehler 'ansicht'); ansicht_lesen übernimmt ein altes 'leiste'
  (links → senkrecht, sonst waagerecht) und verwirft ungültige Lagen. portal.js Block „Taskleiste“: #taskleiste position fixed, frei schwebend (Klassen
  .senkrecht/.namen), Inhalt: Griff .tl-griff (data-tl="griff"; Zeiger-Ziehen mit Einrasten am Rand ≤ 14 px, erst ab 3 px Bewegung; Klick fokussiert ihn,
  dann Pfeiltasten 10 px / Umschalt 50 px, Pos1 bzw. Doppelklick = lageZuruecksetzen), Knöpfe .tl-kat (data-tl-kat = 'suche' | 'fav' (nur wenn Favoriten) |
  'k<Index>' je Kategorie; .an = geöffnetes Modul liegt in der Kategorie, .offen = aufgeklappt), .tl-dreh (⇅/⇆ = drehen()), Uhr → Kalender. Klick auf eine
  Kategorie → flyoutAuf(art, fokus) = .tl-flyout (role dialog, data-art) mit Liste eintragHtml (Symbol, Name, Kurztext, data-tip = Beschreibung neben der
  Liste, ☆/★ data-tl-pin, Rechtsklick kMenueAuf – in den Favoriten mit ▲/▼); 'suche' = Suchfeld (Rang wie bisher, Enter öffnet Treffer 1), ohne Begriff
  Zuletzt bearbeitet + Links + Abmelden. flyoutLage: waagerecht unter- bzw. oberhalb der Leiste (je nach Bildschirmhälfte) an der Kategorie ausgerichtet,
  senkrecht rechts bzw. links daneben; Esc/Klick daneben schließt, Fokus zurück auf die Kategorie. Tastatur: Pfeile längs der Leiste wechseln die Kategorie
  (offene Liste folgt), quer dazu klappt auf/springt in die Liste. Favoriten per Zeiger-Ziehen in ihrer Liste umordnen (zug, favoritenOrdnen). leisteLage:
  Standardplatz waagerecht unten links, senkrecht links mittig (dann body.tl-platz-links mit padding --tl-b, damit nichts verdeckt wird); immer ganz im
  Fenster (fensterB/fensterH = clientWidth/-Height – NICHT „VW“, den Namen hat die Verwaltung). lageSpeichern entprellt 500 ms (Aktion 'ansicht' {x, y}).
  Handy (innerWidth ≤ 600): Leiste unten über die ganze Breite, ohne Griff/Drehen/Namen, Liste darüber. Toolbox-Liste mit ☆/★ statt 📌, Überschrift
  „★ Favoriten“. Konto › Darstellung: Ausrichtung, Beschriftung, Hinweis zum Verschieben, Knopf „↺ Taskleiste an den Standardplatz“ (data-tl-reset, nur
  bei verschobener Leiste aktiv; im deaktivierten Feldsatz bei Kacheln). GEPRÜFT: api_test 367/367 (Abschnitt „Darstellung (Kacheln/Taskleiste) und
  Favoriten“: Standard, Ausrichtung, Lage speichern/runden/zurücksetzen, ungültige Werte, Umstieg von 'leiste' per Datenbank); im Vorschau-Browser mit Maus
  und Tastatur (500 px Pane, 1000 × 640 emuliert, 375 px): Kategorie aufklappen, Favorit ☆, Favoriten ziehen und per Rechtsklick ▼, Leiste am Griff
  verschoben (Lage gespeichert, nach Neuladen und Größenwechsel erhalten), oben → Liste darunter, ⇅ senkrecht mit Liste rechts daneben, Tooltip neben der
  Liste, Esc, Pfeiltasten in der Leiste, Modul aus der Liste geöffnet (Kategorie markiert), Doppelklick auf Griff, Pfeiltasten am Griff, Konto-Knöpfe, Suche
  „rech“, Handy ohne Querscrollen, eingebettet ohne Leiste. Gefundene und behobene Fehler: Bewegungserkennung über movementX (fehlt bei manchen Ereignissen →
  Leiste sprang zurück), Liste unter der Bildlaufleiste (innerWidth statt clientWidth). Testeinstellungen der Entwicklungs-Suite zurückgesetzt.
  Datenschutzhinweise der Suite, LIESMICH.md und Blog-Entwurf tool-mc4y-suite (Sicherung editor-backups/*_blog-suite-taskleiste) nachgezogen.
  INSTALLIERT 0.3.2 (17.10.2026 14:58, Peters „Spiele die Fassung ein“): wie bei 0.3.1 – Aufgabe angehalten, Datenbank gesichert nach
  C:\ProgramData\MC4Y-Suite-Daten\_update-sicherung\2026-10-17_14-58-38_vor-0.3.2, installieren.ps1 -Aktualisieren (Skript suite_update_032.ps1 im
  Sitzungs-Scratchpad), nur lesend geprüft: Status 0.3.2, Organisation „MC4Y P. Hoenig“, Datenbank Fassung 8 mit 2 Benutzern, kein fehler.log, Portal 200,
  portal.js mit neuer Taskleiste, 50 Werkzeuge + alle Module, php.exe als mc4y-systemuser. Gespeicherte alte Einstellung „leiste“ wird beim Lesen umgesetzt.
  DESKTOP-TASKLEISTE + APP (17.10.2026 nachmittags, SUITE_VERSION '0.3.3 (Stufe 3)', DB_SCHEMA 9; Peters „Kannst Du die Taskleiste so umbauen, dass diese
  auch ohne Browser läuft, ggf. auch den Client zu installieren?“ – per AskUserQuestion: beides (eigenes Programm + installierbare App), Windows/macOS/Linux,
  Öffnen im eigenen App-Fenster, Leiste frei schwebend). SERVER: Tabellen geraete (id AUTOINCREMENT – ohne wurden ids gelöschter Geräte wiederverwendet –,
  benutzer_id CASCADE, hash = SHA-256 des 256-Bit-Geräteschlüssels, name, system, erstellt, zuletzt, ip) und einmal (hash, benutzer_id, ziel, ablauf),
  sitzungen.geraet; Einstellung geraet_tage (Standard 90, Verwaltung 1–3650). konto.php sitzung_anlegen($b, $geraet) (anmelden() nutzt sie).
  app/geraete.php: geraet_anmelden (o; Kennwort, pw_wechsel → 403 'pw_wechsel'), geraet_sitzung (o; Ratenbegrenzung über anmeldeversuche, ungültig/
  verfallen 401 'geraet', inaktiv 403, gesperrt 429; gleiche Person → vorhandene Sitzung), geraete, geraet_entfernen (eigene bzw. Admin; beendet dessen
  Sitzungen), geraet_abmelden (Gerät der aktuellen Sitzung), einmal_schluessel (a; Ziel nur '#/…' oder vorhandenes werkzeuge/<klein>.html[#…], keine
  Steuer-/Anführungszeichen; liefert adresse 'einmal.php?t=…', 60 s). public/einmal.php (nur GET, no-referrer): einlösen in BEGIN IMMEDIATE, Sitzung
  derselben Person weiterverwenden, Sitzung einer anderen Person beenden → 303 index.php#/oeffnen?ziel=… bzw. 410-Seite. Portal: Route /oeffnen
  (oeffnenAusTaskleiste: Benutzerwechsel-Aufräumen wie bei der Anmeldung, dann location.replace), Konto-Abschnitt „Desktop-Taskleiste und App“ (#desktop,
  desktopHtml/geraeteZeichnen/desktopBinden: Download client/mc4y-taskleiste.zip, Verbundene Geräte mit Entfernen, „📲 Als App installieren“ über
  beforeinstallprompt, sonst Hinweis aufs Browsermenü), Link im Such-Flyout der Taskleiste (#/konto?desktop=1). PWA: public/manifest.webmanifest
  (standalone, id/start_url index.php, scope ./), Symbole assets/app-192/512/maskable-512.png (aus website/assets/img/logo-mc4y.jpg 400 px, Skript
  app_symbole.py im Sitzungs-Scratchpad), router.php kennt .webmanifest; KEIN Service Worker. CLIENT mc4y-suite/client/ (seit 18.10.2026 mc4y-suite/taskleiste/, siehe „TASKLEISTE 1.1.2/1.1.3 … 1.2.0“; nie eigenmächtig verteilen;
  bauen.py client_paket() → public/client/mc4y-taskleiste.zip): mc4y_taskleiste.py (Python ≥ 3.8 + tkinter, NUR Standardbibliothek, keine f-Strings):
  rahmenloses topmost-Fenster, Bedienung wie die Portal-Taskleiste (Kategorien → Flyout mit Werkzeugen, Kurztext, Beschreibung als Tooltip daneben, ☆,
  Rechtsklick Neu/Öffnen …/Favorit/Beschreibung; 🔍 Suche mit Rang wie im Portal, Zuletzt bearbeitet, Einstellungen; ★ Favoriten per Ziehen ordnen;
  Uhr → Kalender; ⇅/⇆; Griff ⠿ ziehen/Doppelklick/Rechtsklick; Lage als Anteil des virtuellen Bildschirms, Arbeitsbereich über ctypes), Netz in einem
  Hilfsthread + Warteschlange (root.after), Toolbox alle 5 Min. neu; Win11 runde Ecken (DwmSetWindowAttribute 33), DPI-bewusst, Emoji-Ersatz für Tk 8.x,
  macOS-Suche als normales Fenster (rahmenlose bekommen dort keine Tastatur). Öffnen: <Browser> --app=<adresse>einmal.php?t=… (Edge, Chrome, Chromium,
  Brave; Einstellung auto/Pfad/„Standardbrowser“ = webbrowser.open). Tresor: Windows DPAPI, macOS security, Linux secret-tool (dienst/konto), sonst Datei
  0600. Einstellungen %APPDATA%\MC4Y\taskleiste.json, ~/Library/Application Support/MC4Y/, ~/.config/mc4y/ (Umlenkung MC4Y_TASKLEISTE_ORDNER für Tests).
  Einzelinstanz über Port 47100 + crc32(Benutzer|Ordner) % 800 (zweiter Start → „zeigen“ = Suche öffnen). Kommandozeile: --selbsttest <Adresse> <Login>
  <Kennwort> (legt Gerät an und entfernt es), --beenden, --autostart an|aus (HKCU\…\Run „MC4Y-Taskleiste“ mit pythonw, LaunchAgent
  de.my-coaching4you.mc4y-taskleiste.plist, XDG mc4y-taskleiste.desktop). Installationsskripte ohne Administratorrechte: installieren-windows.bat/.ps1
  (-Desktop, -OhneAutostart, -OhneStart, -Ziel; prüft JEDEN Python-Kandidaten – Peters Python-Installationsmanager liegt unter WindowsApps –, dazu
  %LOCALAPPDATA%\Programs\Python\Python3* und Python\pythoncore-3*; bietet winget an; Startmenü-Verknüpfung → pythonw.exe), deinstallieren-windows
  (-EinstellungenLoeschen, -Ziel), installieren-/deinstallieren-macos.command, installieren-/deinstallieren-linux.sh (LF), LIESMICH.txt. Symbole
  mc4y-taskleiste.png/.ico. GEPRÜFT: api_test 403/403 (Abschnitt „Desktop-Taskleiste: Geräte und Einmal-Schlüssel“, Wegwerfkonto geraetetest),
  --selbsttest 7/7 gegen die Entwicklungs-Suite (8095), zwei Oberflächen-Prüfskripte im Sitzungs-Scratchpad (tl_gui_test.py 23/23 mit Bildern: waagerecht,
  Kategorie-Flyout, Tooltip, senkrecht, Suche „rech“, Favoriten, Einstellungen, nur Symbole; tl_gui_test2.py 14/14: Anmeldefenster mit falschem Kennwort,
  Einstellungen ohne Kennwort, zweiter Start, Gerät abmelden, Autostart ein/aus und wiederhergestellt), Ablauf im Vorschau-Browser (Einmal-Link → Portal →
  Taschenrechner mit Suite-Pille; Benutzerwechsel admin → coach räumt Entwürfe weg, Ansicht bleibt; Konto „Verbundene Geräte“ mit Entfernen per Maus),
  Windows-Installer/-Deinstaller mit -Ziel in einen Testordner (Verknüpfung → pythonw.exe), danach entfernt. macOS/Linux NICHT auf echten Geräten
  geprüft (nur LF-Zeilenenden). In der Entwicklungs-Datenbank wurden geraete/einmal einmal gelöscht und user_version auf 8 gesetzt (AUTOINCREMENT
  nachträglich). Datenschutzhinweise der Suite (Verbundene Geräte, Einmal-Schlüssel, App, Programm), LIESMICH.md (Abschnitt „Desktop-Taskleiste und App“)
  und Blog-Entwurf tool-mc4y-suite (Sicherung editor-backups/*_blog-suite-desktop) nachgezogen.
  INSTALLIERT 0.3.3 + TASKLEISTEN-PROGRAMM (17.10.2026 15:52, Peters „Installiere alles“): installieren.ps1 -Aktualisieren -OhneBrowser (sichert die
  Datenbank selbst nach …\_update-sicherung\2026-10-17_15-52-29_vor-0.3.3), Datenbank 8 → 9 (Tabellen geraete/einmal, 2 Benutzer unverändert, 0 Geräte),
  kein fehler.log, Manifest/Symbole/Client-Zip ausgeliefert, php.exe als mc4y-systemuser; tests/suite_installation_pruefen.py 0 Fehler. DABEI GEFUNDEN:
  installieren.ps1 wartet am Ende mit Read-Host auf Enter – im versteckten, erhöhten Fenster hing der Aufruf (Prozess ließ sich nur mit einer zweiten
  Administrator-Freigabe beenden). Behoben: neuer Schalter -OhneWarten (überspringt beide Read-Host); die korrigierte Datei wurde in die Installation
  kopiert. Künftige Aktualisierungen aus Skripten IMMER mit -Aktualisieren -OhneBrowser -OhneWarten. Programm „MC4Y-Taskleiste“ für Peter installiert
  (installieren-windows.ps1 ohne Schalter): %LOCALAPPDATA%\MC4Y\Taskleiste, Python pythoncore-3.14-64 (pythonw), Startmenü-Verknüpfung, Autostart
  HKCU\…\Run „MC4Y-Taskleiste“, läuft; Anmeldung (Adresse vorbelegt http://127.0.0.1:8090/, Anmeldename, Kennwort) macht Peter selbst.
  TASKLEISTE: EIGENE KATEGORIEN, REIHENFOLGE, PROGRAMME DES RECHNERS (17.10.2026 abends, Peters „Baue in die Taskleiste noch die Möglichkeit ein, eigene,
  lokale Programme wie Word, aber auch den mc4y-Editor usw. einzubinden, eigene Kategorien anzulegen und die Kategorien beliebig anzuordnen. Ist die Suite auf
  einem Server installiert, sollen die Anwendungen der Suite per default vom Server stammen.“; parallel zur Stufe-4-Sitzung, gehört zu Fassung 0.4.0, kein neues
  DB-Schema). SERVER (app/konto.php): benutzer.ansicht.leiste = {reihenfolge [Schlüssel fav|k<id>|e<id>], aus [fav|k<id>], eigene [{id e[0-9a-z]{4,16}, name ≤ 40,
  symbol ≤ 8 ohne <>"'&, eintraege ≤ 60: {art werkzeug, id} | {art link, id l…, name ≤ 60, url nur http(s) mit Host, ohne Leer-/Anführungszeichen, symbol}}]
  (≤ 30)}; leiste_lesen($l, $streng) bereinigt (streng → SuiteFehler 'leiste' bei ungültigem Weblink, leerem Namen, > 30/60), unbekannte bzw. doppelte Schlüssel
  fallen weg, eigene Kategorien sind nicht ausblendbar; Aktion 'leiste' (a), ansicht_lesen liefert 'leiste' mit, aktion_toolbox zusätzlich 'leiste'. LOKALE
  PROGRAMME WERDEN NIE AUF DEM SERVER GESPEICHERT (sonst könnte ein Server bestimmen, was ein Arbeitsplatz startet) – leiste_lesen verwirft andere Arten.
  public/taskleiste.php (GET, https_pflicht): Kopie von public/client/mc4y-taskleiste.zip + voreinstellung.json {adresse = Schema+Host+Pfad dieser Suite,
  organisation, erstellt} im Ordner des Programms (MC4Y-Taskleiste/ – zuerst lag sie fälschlich in der Wurzel, die Installer hätten sie nicht kopiert; vom
  api_test gefunden); Download-Link im Konto zeigt jetzt dorthin. PORTAL (portal.js, Block nach toolboxLaden): leisteAufbau, kategorienGeordnet(mitAus)
  ({key, art fav|sys|eigen, name, symbol, ids, eintraege, aus}), leisteSpeichern (sofort zeichnen, bei Fehler zurück), reihenfolgeAus (ausgeblendete behalten
  ihren Platz), kategorieDialog/werkzeugeDialog/linkDialog, eintragInKategorie/eintragEntfernen/eigeneOrdnen; Leisten-Knöpfe data-tl-kat = Schlüssel (vorher
  'k'+Index!), data-tl-typ; Ziehen der Kategorien (kzug, Zeiger-Ereignisse, nicht auf dem Handy/Touch, klickSperreBis auch für .tl-kat), Rechtsklick katMenueAuf
  (auch auf 🔍), Werkzeug-Rechtsklick „➕ In …“/„✕ Aus … entfernen“/▲▼, linkMenueAuf, Einträge eigener Kategorien per Ziehen (li data-ek 'w:<id>' bzw.
  Weblink-id), Suche findet Weblinks, Toolbox-Seite in Benutzer-Reihenfolge mit eigenen Kategorien (linkZeile), Konto-Kasten #leisteAnpassen
  (leisteAnpassenZeichnen/-Binden: ▲▼, zeigen, Werkzeuge …, ＋ Weblink, ✎, ＋ Eigene Kategorie, ↺ Übliche Reihenfolge), Route #/konto?leiste=1; suite.css
  .tl-kat.vor/.nach/.zieht, .tl-f-aktionen, .symwahl, .wdliste, table.la. PROGRAMM mc4y_taskleiste.py 1.1.0: Konfig + programme [{id p…, name, symbol, pfad,
  argumente, kat, app}], leiste_lokal (Kopie bzw. ohne Suite der eigene Aufbau), ohne_suite; leiste_normal, neue_id, voreinstellung() (utf-8-sig),
  programm_starten (Windows: .exe/.bat/.cmd/.com per Popen mit list2cmdline, sonst os.startfile; macOS open [-a … --args]; Linux ausführbar/gtk-launch/xdg-open;
  HTML mit app → --app=file:///… im Chromium-Browser), programm_vorschlaege (Office 15/16, LibreOffice, Editor, Explorer, Rechner bzw. macOS-/Linux-
  Gegenstücke). App: kategorien() wie im Portal plus Programme (kat ohne vorhandene Kategorie → Pseudo-Kategorie „lokal“ 💻 „Programme“, nicht ziehbar),
  leiste_speichern (lokal sofort, verbunden → Aktion 'leiste'), knopf_druck/zug/los (Klick erst beim Loslassen, Einfügemarke self.marke per place),
  kat_menue, kat_verschieben, kat_ausblenden, kategorie_loeschen (Programme darin → „Programme“), zeile_bauen (self.zeilen = (Schlüssel, Rahmen, ordnen)),
  zeile_link/zeile_programm, eigene_ordnen, programm_menue (bearbeiten, ▲▼, andere Kategorie, Speicherort, entfernen), Dialoge programm_dialog (Vorschläge,
  Datei …/Ordner …, Argumente, Symbol, Kategorie inkl. „＋ neue Kategorie „Meine Programme““, HTML im App-Fenster), kategorie_dialog(key, danach),
  link_dialog, werkzeuge_dialog (Listbox Mehrfachwahl); Suche auch über Programme/Weblinks; Anmeldefenster mit Voreinstellung und „Ohne Suite nutzen“
  (ohne_suite_starten, lokale_toolbox; Einstellungen › „Mit der Suite verbinden …“; beim ersten Verbinden werden eigene Kategorien von „ohne Suite“
  hochgeladen, wenn die Suite noch keine hat – hochladen_lokal). Installer (Windows/macOS/Linux) kopieren voreinstellung.json mit. GEPRÜFT: api_test 503/503
  (neu: Aufbau speichern/bereinigen, Weblinks javascript:/ftp/ohne Host/Anführungszeichen/leer abgelehnt, ≤ 30, 401, Rückgängig, Download mit Voreinstellung im
  Programmordner); Programm-Oberflächentest im Sitzungs-Scratchpad tl_gui_test3.py 18/18 (eigene Kategorie, Programm/Weblink/Werkzeug in der Liste, Ziehen in
  der Liste und in der Leiste, nach hinten, ausblenden/zeigen, Suche, Dialoge, Vorschläge, ohne Suite; startet nichts, Aufnahmen angesehen) und angepasster
  Regressionstest tl_gui_test1b.py 23/23; Voreinstellung mit BOM gelesen (Fehler gefunden und behoben); Portal im Vorschau-Browser mit Maus und Tastatur
  (Kategorie anlegen über Rechtsklick auf 🔍, Werkzeuge per Häkchen, Weblink mit abgelehnter javascript:-Adresse, Kategorie in der Leiste an den Anfang
  gezogen, Weblink in der Liste nach oben gezogen – kein versehentliches Öffnen –, Konto-Tabelle ▲ und „zeigen“, Werkzeug-Rechtsklick „In Büro“, 375 px
  ohne Querscrollen). Testdaten der Entwicklungs-Suite zurückgesetzt. NICHT geprüft: macOS/Linux, echtes Starten von Word (Starten im Test abgefangen),
  Download über einen echten Server (nur über den Test-Server). Datenschutzhinweise der Suite, LIESMICH.md (Abschnitt „Eigene Kategorien …“), client/
  LIESMICH.txt und Blog-Entwurf tool-mc4y-suite (Sicherung editor-backups/2026-10-17_17-30-23_blog-suite-taskleiste-kategorien) nachgezogen.
  INSTALLIERT 0.4.0 + TASKLEISTE 1.1 AUF PETERS PC (17.10.2026 17:43, Peters „Ja, jetzt“): vorher api_test 503/503 gegen das Projekt; installieren.ps1
  -Aktualisieren -OhneBrowser -OhneWarten per Hilfsskript mit Start-Transcript (Sitzungs-Scratchpad update040.ps1, Start-Process -Verb RunAs -Wait);
  Datenbank gesichert nach C:\ProgramData\MC4Y-Suite-Daten\_update-sicherung\2026-10-17_17-43-44_vor-0.4.0, Aufgabe MC4Y-Suite-Sicherung 12:30 angelegt,
  Status „0.4.0 (Stufe 4), eingerichtet“; tests/suite_installation_pruefen.py 0 Fehler (130 Programmdateien = Projekt). Danach client/installieren-
  windows.ps1 ohne Schalter (beendet die laufende Fassung, Autostart bleibt) → mc4y_taskleiste.py 1.1.0 läuft. Ohne Administratorrechte zeigt der
  -Probelauf „Aufgabe MC4Y-Suite fehlt“ – das ist nur fehlende Sichtbarkeit, kein Fehler.
  STUFE 4 „BETRIEB“ (17.10.2026, Peters „Baue Stufe 4 und installier die Suite anschließend auf <server>“ – Windows Server 2019, Domänencontroller,
  <server-ip>, Domäne <domaene>; Peters Entscheidungen per AskUserQuestion: Zugang über ein Anmeldefenster (Get-Credential, Peter tippt, ich sehe
  kein Kennwort; WinRM/Kerberos), Zertifikat selbst signiert, PHP 8.5.11 + VC++-Laufzeit mitbringen, Active-Directory-Anmeldung schon in Stufe 4; Frage
  „Wo legst Du die Dateien und Konfigurationen ab?“ → Datenordner-Dialog in allen Installern, Verwaltung › Speicherorte, Skript zum Verschieben,
  Browser-Dialog „Ablage einrichten“). SUITE_VERSION '0.4.0 (Stufe 4)', DB_SCHEMA 10 (benutzer.ad_name, ad_sid mit eindeutigem Index, Tabelle
  ad_gruppen). WICHTIG beim Entwickeln: Migrationsblock IMMER vor dem Hochsetzen von DB_SCHEMA einfügen, wenn der Entwicklungsserver läuft (sonst
  setzt er user_version ohne die Spalten). KERN: kern.php konfig() Standard {daten '', zeitzone, https 'erzwingen'|'proxy'|'aus', hsts false,
  windows_anmeldung false, ad []}, programm($rel), https_pflicht($seite) (cli/aus/https/lokal frei; Schnittstelle 403 'https', Seiten 301 nach Host-
  Prüfung; in api_ausfuehren, index/datei/einmal/kalender/datenschutz.php), hsts(), ist_https im Proxy-Modus über X-Forwarded-Proto, EINSTELLUNGEN
  sicherung_anzahl 14, sicherung_ordner, ad_* (ad_tls '' = Vorschlag aus konfig, sonst ldaps). app/ablage_einrichten.php (Startseite ohne Datenordner:
  Prüfungen, Code programm/ablage-einrichtungscode.txt, ablage_pruefen – absolut, nicht in public/app, leer oder Suite-Datenordner –, schreibt
  konfig.php + .htaccess im Datenordner). app/sicherung.php (ZIP mit MC4Y-SICHERUNG.json + PRUEFSUMMEN.sha256, VACUUM INTO-Momentaufnahme, zwei Durchgänge,
  Hash aus dem ZIP-Inhalt, .part + Umbenennen, Sperrdatei, Rotation, Anlässe auto/hand/update/vor-wiederherstellung; sicherung_pruefen inkl.
  integrity_check; sicherung_wiederherstellen schiebt den alten Inhalt nach _vor-wiederherstellung_<Zeit>, prüft beim Entpacken; Aktionen v_sicherung*;
  datei.php?sicherung= nur Admin, protokolliert). app/betrieb.php (v_speicherorte mit Größen/freiem Platz und Befehlen, v_system in Gruppen Betrieb/
  Sicherung/Sicherheit + Fehlerprotokoll; Zahlen mit deutschem Komma). app/cli.php (status, sicherung [--auto], sicherungen, pruefen, wiederherstellen
  --ja, einstellung sicherung_ordner|sicherung_anzahl, system). app/ad.php (LDAP-Anmeldung mit dem Konto selbst, KEIN Dienstkonto; ad_konto_zerlegen
  name/UPN/NETBIOS\name; TLS ldaps|starttls|keine – keine nur zu 127.0.0.1/::1/localhost; Zertifikatsprüfung DEMAND; Fehler 49 data 533/775/532/773/701
  und errno 8 (Signatur) mit Klartext; verschachtelte Gruppen über 1.2.840.113556.1.4.1941; ad_anmeldung: SID vor Name, automatisch anlegen (ad_anlegen),
  Gruppenabgleich ad_gruppen, Admin-Gruppe; Testverzeichnis über Umgebung MC4Y_SUITE_AD_TEST = JSON-Datei, nur für api_test). konto.php: AD-Konten ohne
  eigenes Kennwort/Kennwortwechsel, Fehlversuche sperren auch AD-Konten; verwaltung.php: Benutzer mit Feld „Domänenkonto“ (Verknüpfen setzt ad_sid zurück,
  Lösen verlangt Kennwort). public/windows-anmeldung.php (IIS Windows-Authentifizierung, nur verknüpfte bzw. automatisch angelegte Konten, ad_windows()).
  Portal: Reiter Anmeldung (Domäne), Sicherung, Speicherorte, Systemprüfung; Login-Knopf „🪟 Mit dem angemeldeten Windows-Konto anmelden“.
  public/handbuch.html (statisches Handbuch für Administratoren, 16 Abschnitte inkl. #sicherheit Prüfliste und #fehler; Linux/macOS/Webhosting-Abschnitte
  5–7 an die Skripte des Hintergrund-Agenten anpassen, sobald sie da sind), web.config maxAllowedContentLength 115343360 (Upload 100 MB + Rand).
  WINDOWS-SKRIPTE (install/windows, nur ASCII, Laufwerke über IO.DriveInfo – Get-Volume liefert ohne Administratorrechte nichts): installieren-server.ps1/.bat
  (IIS-Rollendienste inkl. Web-Scripting-Tools für das WebAdministration-Modul, neue Default Web Site angehalten; PHP aus -PhpZip mit Prüfsumme, VC++ nur
  bei Startfehler; php.ini der Suite <Programm>\php\php.ini über PHPRC mit fileinfo/ldap/mbstring/openssl/pdo_sqlite/sodium/zip, fastcgi.impersonate 0,
  Upload 100M; FastCGI-Anwendung php-cgi.exe '-d mc4y_suite=1'; Anwendungspool MC4Y-Suite ApplicationPoolIdentity, Rechte über die BERECHNETE SID
  S-1-5-82-SHA1(klein(UTF-16 Poolname)) – lokal gegen DefaultAppPool geprüft –; Handler mit -AtIndex 0 (sonst StaticFile zuerst); Zertifikat selbst
  signiert 5 Jahre (FQDN, Kurzname, IPv4), FriendlyName MC4Y-Suite, Speicher WebHosting (DC nutzt es nicht für LDAPS), öffentlicher Teil in
  <Programm>\zertifikat und public\zertifikat\mc4y-suite.cer, auf dem Server in LocalMachine\Root; SNI, wenn 443 belegt; HTTP-Bindungen für FQDN/Kurzname
  (Suite leitet um); Windows-Auth nur für windows-anmeldung.php; anonym = Pool-Identität; Firewall MC4Y-Suite-HTTPS Domäne/Privat; Aufgabe
  MC4Y-Suite-Sicherung 02:30 SYSTEM; konfig.php mit ad-Vorschlag – auf einem DC server 127.0.0.1/tls keine; Einrichtungscode nur im Fenster/ErgebnisDatei,
  nicht im Protokoll; KURZANLEITUNG.txt; -Aktualisieren/-Probelauf/-Still/-OhneWarten/-ErgebnisDatei/-Zertifikat/-HostName/-Sicherungen),
  auf-server-installieren.ps1 (Arbeitsplatz: Paket per robocopy + ZipFile, Get-Credential, New-PSSession Negotiate, Server prüfen, Ablage im Fenster
  wählen, Copy-Item -ToSession, installieren-server.ps1 -Still, Protokoll/Kurzanleitung/Zertifikat nach Dokumente\MC4Y-Suite-Installationen\<Server>_<Zeit>,
  Mitschrift fenster.txt, auf Wunsch Zertifikat in CurrentUser\Root – Windows fragt), suite-betrieb.ps1 (Get-SuiteInstallation IIS/Einzelplatz,
  Invoke-SuiteCli UTF-8, Stop-/Start-Suite, Test-SuiteAntwort), wiederherstellen.ps1, datenordner-verschieben.ps1 (robocopy /COPY:DAT mit ACL des alten
  Ordners, Vergleich, konfig.php.vor-verschieben_<Zeit>, php.ini-tmp), deinstallieren-server.ps1/.bat; Einzelplatz installieren.ps1 jetzt mit Ablage-Abfrage
  (Test-Ablage), -SicherungOrdner, täglicher Sicherung 12:30 (auch bei -Aktualisieren nachgerüstet), zip/ldap im PHP-Paket, Warnung ohne zip;
  deinstallieren.ps1 entfernt die Sicherungsaufgabe. datenschutz.php: Abschnitt „Für den Betrieb kommen hinzu“ (AD, Sicherungen, Webserver-Protokolle,
  Fehlerprotokoll, HTTPS). LIESMICH.md Abschnitt „Stufe 4: Betrieb“. Blog-Entwurf tool-mc4y-suite ergänzt (Sicherung editor-backups/*_blog-suite-stufe4).
  GEPRÜFT: api_test 483/483 (Abschnitte „Stufe 4: …“ inkl. CLI status/einstellung), Parser + ASCII aller .ps1, Probeläufe Einzelplatz (neu und
  -Aktualisieren) und Server-Installer auf dem Arbeitsplatz, Systemprüfung/Sicherung/Speicherorte/Anmeldung im Vorschau-Browser. NICHT geprüft: echtes
  Active Directory (nur auf dem Server), Linux/macOS, Webhosting.
  LINUX/MACOS/WEBHOSTING (Hintergrund-Agent, nur per Prüfskript auf Struktur/LF/Namen geprüft, NICHT auf echten Systemen): install/linux/{installieren.sh
  (apt/dnf, Apache Standard oder nginx, PHP-FPM-Pool mc4y-suite unter mc4y-systemuser mit php_value – nicht php_admin_value, weil sicherung.php
  set_time_limit(0) ruft –, /opt/mc4y-suite, Daten-Vorschlag /var/lib/mc4y-suite-daten 700, /etc/mc4y-suite/{installation.conf,tls,alt}, SELinux,
  systemd-Timer 02:30 sonst cron, --einzelplatz/--aktualisieren/--konfiguration-erneuern/--probelauf/--still/--ja/--firewall/--https proxy),
  deinstallieren.sh, wiederherstellen.sh, datenordner-verschieben.sh, gemeinsam.sh, LIESMICH.txt}, install/macos/{installieren.command (bash 3.2,
  Homebrew-PHP, dscl-Konto versteckt, launchd Server + Sicherung, /Library/Application Support/MC4Y-Suite[-Daten], --server nur Grundgerüst),
  deinstallieren.command, wiederherstellen.command, gemeinsam.sh, LIESMICH.txt} – KEIN datenordner-verschieben für macOS; .htaccess in der Paketwurzel
  (sperrt nur konfig*.php, Einrichtungscodes, Skripte, versteckte Dateien – bewusst keine Pauschalsperre, weil Apache übergeordnete .htaccess auch bei
  Domain auf public/ anwendet), app/.htaccess, install/.htaccess, public/.user.ini (Upload 100M/110M, 600 s, 512M). Von mir nachgezogen: public/.htaccess
  sperrt versteckte Dateien, web.config hiddenSegments .htaccess/.user.ini, betrieb_befehle macOS-Pfade (wiederherstellen.command, verschieben „von Hand“)
  und CLI-Befehle unter Linux/macOS mit runuser/sudo -u <Konto>, Handbuch 5–7/8/11/12/16 und LIESMICH.md an die Skripte angepasst. Erstprüfung laut
  Agent: Debian 12 + Apache, Ubuntu 24.04 + nginx, Rocky 9 mit SELinux, macOS 14 Einzelplatz, jeweils zuerst --probelauf.
  <server> (17.10.2026 abends, nur lesend erkundet): DC mit IIS (WSUS 8530/8531, Default Web Site *:80), 443 frei, Laufwerke C/D(WSUS)/E. WinRM
  nur über IPv4 erreichbar – der Name löst zuerst auf IPv6 auf (GUA <ipv6> gar nicht, ULA <ipv6> ohne Port 5985), New-PSSession
  scheitert; per IP bräuchte es TrustedHosts (NICHT ändern). SMB \\<server>\C$ geht mit Peters Kerberos-Ticket <benutzer>@<domaene> (Peter ist
  lokal als „<benutzer>“ angemeldet), DCOM/schtasks aus der Ferne nicht. LÖSUNG ohne Serveränderung: New-PSSessionOption -OpenTimeout 240000 – WinHTTP
  probiert die globale IPv6-Adresse ~60 s und nimmt dann IPv4 (Test-WSMan brach nach 8 s mit ERROR_WINHTTP_TIMEOUT ab); auf-server-installieren.ps1
  nutzt das (und listet bei Fehlern die Adressen mit Port 5985). Peter wählte „B: WinRM freigeben“ + „Alles auf E:“ – Freigabe am Server war danach
  unnötig. ERSTER LAUF 18:18 abgebrochen: In „-ArgumentList …, [bool]$Aktualisieren, …“ wird der Cast im ARGUMENTMODUS NICHT ausgeführt – es kommt
  der Text „[bool]False“ an (= wahr), das Server-Skript lief als -Aktualisieren und brach ab (nichts eingerichtet). Behoben ($aktJa vorher berechnen;
  Regel: Casts in Argumentlisten immer in Klammern oder vorher in eine Variable). Das Skript räumt jetzt alte C:\Windows\Temp\mc4y-suite-paket_* auf
  und den eigenen Ordner auch bei Fehlern (finally).
  INSTALLIERT auf <server> (17.10.2026 18:55–18:57, gestartet von der Ursprungssitzung, Peter tippte die Anmeldedaten): Suite 0.4.0, Windows Server
  2022 Standard (nicht 2019) als Domänencontroller, Daten E:\MC4Y-Suite-Daten, Sicherungen E:\MC4Y-Suite-Sicherungen (gleiches Laufwerk – Systemprüfung
  gelb, Peters Wahl), PHP 8.5.11 nach C:\PHP, Rollendienste Web-CGI + Web-Scripting-Tools ergänzt (WSUS und Default Web Site unverändert), Pool-SID
  <pool-sid>, Zertifikat <zertifikat-fingerabdruck> (bis 17.10.2031) auf Peters PC in CurrentUser\Root, http-Umleitung
  für FQDN und Kurzname, Windows-Auth, Firewall 443, Sicherung 02:30. Protokolle C:\Users\<benutzer>\Documents\MC4Y-Suite-Installationen\<server>_
  2026-10-17_18-52-32. Nur lesend geprüft: Status 0.4.0 https, eingerichtet (Peter hat den ersten Administrator angelegt), http → 301, API über http
  403, konfig.php/app/.user.ini ohne Inhalt, Handbuch 200. FEHLER gefunden: /zertifikat/mc4y-suite.cer 404.3 – dieser IIS hat keinen mimeMap für
  .cer → public/web.config <staticContent> remove + mimeMap .cer (application/x-x509-ca-cert); wirkt auf <server> erst nach einem
  auf-server-installieren.ps1 … -Aktualisieren (neue Anmeldung). Offen für Peter: Verwaltung › Anmeldung (Domäne) einschalten – Vorschläge aus
  konfig.php (Server 127.0.0.1, Verschlüsselung keine), AD-Gruppen zuordnen, „Verbindung prüfen“; Arbeitsplätze: Zertifikat per GPO verteilen,
  Adresse in „Lokales Intranet“ für die Windows-Anmeldung. NIE api_test --ziel gegen diese Installation.
  TASKLEISTE 1.1.1: SERVER STANDARD + LOKAL, ⚙ ANPASSEN (17.10.2026 abends, Peters Kritik „alte Taskleiste, nicht wie gefordert konfigurierbar“ und
  „Hast Du eine Default-Einstellung nicht verstanden: falls Server vorhanden mit Server verbinden, aber auch die Möglichkeit, die Suite lokal zu nutzen.
  Die rechte Maustaste hab ich entdeckt.“ – installiert war 1.1.0, verbunden nur mit 127.0.0.1:8090). client/mc4y_taskleiste.py: MEHRERE VERBINDUNGEN
  Konfig verbindungen [{adresse, login, geraet_id, geraet_name, tresor, schluessel_datei}] (aktive = Felder oben, Konfig.aktive_sichern bei jedem
  Speichern; Umstieg alter Einstellungen = erster Eintrag mit Schlüsseldatei „taskleiste.schluessel“, neue taskleiste-<crc32(Adresse)>.schluessel;
  Tresor.datei je Verbindung), wahl 'auto' | Adresse, vor_abgelehnt. kandidaten(): bei auto erst Server (nicht adresse_lokal), dann dieser Rechner;
  verbinden_reihe(kand, still, vorher) probiert der Reihe nach (ungültiger Schlüssel nur für diese Verbindung gelöscht), bevorzugt_pruefen() alle 5 Min.
  im auffrischen_takt (zurück zum Server), verbindung_waehlen, verbindung_menue (Knopf 🌐 Server / 💻 Lokal in der Leiste, Rechtsklick auf den Griff),
  verbindung_trennen (ersetzt abmelden; meldet das Gerät dort ab, nächste Verbindung übernimmt), anmeldung_zeigen(hinweis, adresse, neu, vor) – neu =
  weitere Suite hinzufügen, ohne zu trennen; voreinstellung_pruefen(): voreinstellung.json mit nicht verbundenem Server → einmalige Einladung (Abbrechen
  → vor_abgelehnt). Einstellungen › Verbindung: Radio „Automatisch …“ / „nur …“ je Verbindung, „trennen …“, „＋ Weitere Suite verbinden“. auffrischen
  verwirft Listen einer inzwischen gewechselten Verbindung. SCHNELLER VERBINDUNGSAUFBAU schnell_verbinden + _Schnell…Handler: alle Adressen eines Namens
  gleichzeitig, Gewinner je Host/Port gemerkt (<server>: Python probierte die nicht erreichbaren IPv6-Adressen nacheinander – 21,5 s je Abfrage,
  jetzt 0,1 s). adresse_normal ohne Vorsilbe: https für Server, http nur für 127.x/localhost/::1 (vorher http → Server lehnt die API ab). SICHTBAR
  ANPASSEN: Knopf „⚙ Anpassen“ in der Leiste + Suche-Fuß + Einstellungen → Fenster „Taskleiste anpassen“ (anpassen_zeigen/_fuellen/_zu, kat_reihe auch
  für ausgeblendete, reihenfolge_zuruecksetzen; je Kategorie ▲▼, ausblenden/zeigen, ＋ Programm/Datei, ＋ Weblink, Werkzeuge …, ✎, 🗑; neu_zeichnen
  aktualisiert es). LIESMICH.txt (Abschnitt „Server und dieser Rechner“), Suite-datenschutz.php (Programm-Absatz: mehrere Verbindungen, je Schlüssel),
  Blog-Entwurf tool-mc4y-suite (Sicherung editor-backups/*_blog-suite-taskleiste-server). GEPRÜFT: Oberflächentest im Sitzungs-Scratchpad
  tl_verbindung_test.py 28/28 (Wegwerf-Einstellungen, nachgebaute Suiten: Umstieg, Server zuerst, Ausfall → lokal, zurück, feste Wahl, Anpassen-Fenster,
  trennen, weitere Suite über das Anmeldefenster, Voreinstellung + Ablehnen; Bilder angesehen); schneller Aufbau gegen <server> und 127.0.0.1 (nur
  Status). INSTALLIERT auf Peters PC 19:46 (installieren-windows.ps1 -OhneStart, danach pythonw), dazu voreinstellung.json (https://<server>/,
  „MC4Y P. Hoenig“) im Programmordner → Einladung zur Server-Anmeldung erschien; Kennwort tippt Peter. Client-Zip neu (bauen.py); die Zips in den
  INSTALLIERTEN Suiten (Peters PC, <server>) haben noch 1.1.0 bis zum nächsten -Aktualisieren. Hinweis: eigene Kategorien gelten je Suite/Konto –
  beim Umschalten erscheinen die der anderen Suite; Programme dieses Rechners in deren eigenen Kategorien stehen dann unter „Programme“.
  NACHBESSERUNG 20:0x (Peters „wie weiß ich, ob und mit welchem Server ich verbunden bin, und warum immer ‚Anmeldename oder Kennwort falsch‘“):
  Knopf zeigt den Namen („Dieser PC“ bzw. Kurzname des Servers, „⚠“ bei Verbindungsfehler), Suche-Fuß nennt Verbindung + Adresse; „Weitere Suite“
  übernimmt den Anmeldenamen NICHT mehr von der bisherigen Suite (war mit „<benutzer>“ vorbelegt), Hinweis im Fenster „Ihr Konto in DIESER Suite“,
  Fehlertext bei 'anmeldung' erklärt Konto der Suite vs. Domänenkennwort (gilt erst nach Verwaltung › Anmeldung), bei 'login' „ohne Domäne“. Die
  Server-Suite prüft die Taskleiste mit derselben anmelden()-Funktion wie den Browser (AD inklusive, sobald eingeschaltet). Lesen der Benutzerdatenbank
  von <server> über E$ hat die Berechtigungsprüfung abgelehnt (Produktivdaten) – nicht umgehen. Test 29/29, neu installiert.
  TASKLEISTE 1.1.2/1.1.3 – BILDSCHIRME, AUTOSTART, BITDEFENDER (18.10.2026, Peters „Die Taskleiste ist im Startmenü sichtbar, wird aber nicht
  ausgeführt bzw. am Desktop angezeigt“): Ursachen (1) Lage war Anteil der GESAMTFLÄCHE aller Bildschirme – bei Peters drei Bildschirmen (1920 × 1200
  Haupt, 3072 × 1728 bei (1920, −972), 3440 × 1440 bei (5760, −234)) lag die Leiste oben auf Bildschirm 2; (2) der Run-Eintrag (HKCU) wurde beim
  Anmelden nicht ausgeführt (Shell-Core-Ereignis 9707 listet ihn nicht). 1.1.2: Lage je Bildschirm (Konfig monitor = Rechteck, x/y = Anteile des
  freien Platzes im Arbeitsbereich; monitore_windows über EnumDisplayMonitors, lage_rechnen/lage_merken/ganz_sichtbar, Standardplatz = Haupt-
  bildschirm unten links; alte Lage ohne monitor → Standardplatz; sichtbar_halten im Uhrtakt; zweiter Start holt die Leiste auf den Bildschirm der
  Maus – zur_maus). Der Autostart von 1.1.2 (Python startete verborgen PowerShell → WScript.Shell → Verknüpfung im Autostart-Ordner, Run-Wert per
  winreg gelöscht) wurde von BITDEFENDER als Bedrohung eingestuft: Quarantäne von client/mc4y_taskleiste.py, installieren-/deinstallieren-windows.ps1,
  deinstallieren-windows.bat, tests/taskleiste_lage_test.py (rief selbst PowerShell auf) und der installierten Kopie samt Startmenü-/Autostart-
  Verknüpfung. Peter hat alles wiederhergestellt; danach waren genau diese Pfade für Schreiben/Löschen/Neuanlegen gesperrt (Zugriff verweigert trotz
  Vollzugriff in der ACL – Filter von Bitdefender), Lesen ging. 1.1.3 (Peters Hinweis dazu: „keinen Button, die Taskleiste zu schließen“): Python
  startet KEINE anderen Programme für den Autostart und fasst die Registrierung nicht an (kein winreg, kein PowerShell, kein 0x08000000 –
  TL-B14); installieren-windows.ps1 legt Startmenü UND Autostart-Verknüpfung (GetFolderPath('Startup')) mit WScript.Shell an und entfernt den alten
  Run-Wert, ruft `--autostart an` nicht mehr auf; neuer Schalter -Verknuepfungsordner (Prüfungen: alle Verknüpfungen in einen Wegwerf-Ordner, keine
  Registrierung) auch im Deinstaller. Knopf ✕ am Ende der Leiste (beenden_fragen: askyesno, nennt den Weg zurück), Griff-Tooltip nennt ihn.
  TL-O25/O26 im Oberflächentest, Umleitung MC4Y_TL_CLIENT (Oberfläche, Selbsttest) bzw. Pfadargument/--programm. REGEL: in Programmen und
  Prüfständen keine Schadsoftware-Muster (verborgene Shell aus Python, Autostart/Run selbst schreiben – auch nicht per Dateikopie –, Nachladen);
  Sicherheitseinstellungen (Quarantäne, Ausnahmen) nur Peter.
  1.2.0 (18.10.2026 nachmittags, Peters „lässt sich nach wie vor nicht starten … so umbauen, dass ein Virenscanner diese nicht als potenzielle
  Bedrohung wahrnimmt“): Weil Bitdefender die wiederhergestellten PFADE sperrt (auch Neuanlegen unter gleichem Namen), liegen die Quellen jetzt in
  mc4y-suite/taskleiste/ (client/ = gesperrte Reste; bauen.py alter_client_ordner() entfernt sie, sobald es geht, client_paket() packt nur
  taskleiste/; statisch_pruefen meldet client/ als Hinweis), installiert wird nach %LOCALAPPDATA%\Programs\MC4Y-Taskleiste, Verknüpfung „MC4Y
  Taskleiste.lnk“ (VERKNUEPFUNG; alte „MC4Y-Taskleiste.lnk“ = VERKNUEPFUNG_ALT); Prüfstand tests/taskleiste_bildschirm_test.py (TL-B01 … B19; der
  alte taskleiste_lage_test.py ist gesperrt). Paketinhalt, Download, Skriptnamen und Einstellungen (%APPDATA%\MC4Y\taskleiste.json) unverändert.
  Windows-Autostart im Programm NUR noch Anzeige: autostart_an() (beide Namen im Autostart-Ordner), Einstellungen zeigen „Start beim Anmelden:
  eingerichtet/nicht eingerichtet“ + Knopf „Windows-Einstellungen › Autostart …“ (autostart_einstellungen = os.startfile('ms-settings:startupapps'));
  autostart(an) wirft unter Windows OSError(AUTOSTART_WINDOWS), `--autostart` gibt den Hinweis aus (Rückgabe 1); macOS/Linux unverändert (Häkchen,
  LaunchAgent/.desktop). installieren-windows.ps1: beendet bei echter Installation die laufende Leiste über das NEUE Programm (`--beenden`),
  übernimmt voreinstellung.json aus dem alten Ordner, legt „MC4Y Taskleiste“ in Startmenü (+ Desktop) und Autostart an, räumt alte Verknüpfungen/
  Run-Wert/alten Ordner und nennt gesperrte Reste („nach einem Neustart erneut ausführen“); mit -Verknuepfungsordner nichts davon (kein Beenden,
  keine Registrierung, keine alten Reste). EINE INSTANZ: neues Protokollwort „fassung“ (lauschen antwortet direkt mit VERSION; Einzel.fassung() →
  Fassung | '' = vor 1.2 | None = keine); main(): Port belegt und alte Fassung → „beenden“ schicken und übernehmen; `--fassung`. „zeigen“ in den
  ersten 15 s nach dem Start nur lift() (alte Autostart-Verknüpfung startet beim Anmelden ggf. die alte Fassung mit). ERKENNTNIS: Die Leiste lief
  beim Klick aufs Startmenü durchaus – sie lag oben links auf Bildschirm 2 (gespeicherte Lage); „py“ aus dem Python-Installationsmanager (WindowsApps)
  läuft als Paket-App und sieht %LOCALAPPDATA%\MC4Y\… NICHT („No such file“) – Prüfungen dort mit der echten python.exe
  (C:\Users\<benutzer>\AppData\Local\Python\pythoncore-3.14-64\python.exe). INSTALLIERT bei Peter 13:16 aus dem fertigen Zip (Temp-Ordner): 1.2.0 läuft,
  Bitdefender ohne Meldung, neue Dateien nicht gesperrt; Reste der alten Fassung bleiben bis zu einem Neustart (TESTPLAN Abschnitt 8).
  1.3.0 – PROGRAMME UND APPS DES RECHNERS (20.10.2026, Peters „Baue die Taskleiste so um, dass sie alle lokal auf dem Rechner installierte
  Software und Apps als Vorschläge findet“): installierte_programme(neu) → [{name, pfad, args, symbol, quelle}] aus _apps_windows() (Shell-Ordner
  AppsFolder über ctypes-COM: SHGetKnownFolderItem → BindToHandler(BHID_EnumItems) → IEnumShellItems, GetDisplayName SIGDN_NORMALDISPLAY und
  SIGDN_PARENTRELATIVEPARSING 0x80018001 – NICHT 0x80058000 (= FILESYSPATH, E_INVALIDARG); Hilfsthread mit CoInitializeEx STA; enthält Store-Apps),
  _startmenue_windows() (Verknüpfungen beider Startmenüs), _apps_macos() (.app-Bündel, Info.plist ohne LSUIElement/LSBackgroundOnly), _apps_linux()
  (.desktop nach XDG, gio launch); _AUSSCHLUSS (Deinstallieren, Hilfe, Liesmich …), symbol_raten (_SYMBOL_REGELN). Start einer Store-App über
  os.startfile('shell:AppsFolder\\<id>') (SHELL_APPS, ist_shell_app, programm_da). Dialog programme_auswahl(eltern, einzeln, mehrere) mit Suche,
  Quelle, Mehrfachauswahl; im programm_dialog Knopf „Aus den Programmen und Apps dieses Rechners wählen …“ (mehrere = gleich mehrere Einträge).
  TL-B14 bleibt gültig (kein PowerShell/winreg/WScript in Python). Geprüft: Bildschirmtest 18/18, Oberflächentest 52/52 (TL-K20 … K26), 296 Funde
  auf Peters PC. Sicherung editor-backups/*_taskleiste-programmsuche; Doku taskleiste/LIESMICH.txt, Blog-Entwurf tool-mc4y-taskleiste (Markenhinweis
  Microsoft Store), Suite-datenschutz.php (liest die Liste nur lokal).
  INSTALLATIONSPAKETE ZUM HERUNTERLADEN (17.10.2026 abends, Peters „Erstelle die notwendigen Installationspakete und lege sie im Ordner
  website-test\downloads\experimentell\software“): `py mc4y-suite/bauen.py`, dann `py mc4y-suite/paket_bauen.py [Zielordner]` (Standard eben dieser
  Ordner) → mc4y-suite.zip (Wurzel MC4Y-Suite-<Version>/ mit app, public inkl. Werkzeuge/Handbuch/Taskleisten-Zip, install, .htaccess,
  konfig.beispiel.php, LIESMICH.md, INSTALLATION.txt – Platzhalter {VERSION} wird beim Packen ersetzt –, LIZENZ.txt; NICHT: tests/, bauen.py,
  paket_bauen.py, client/ (steckt in mc4y-taskleiste.zip), konfig.php, Einrichtungscodes, *.log; PHP ist NICHT enthalten) und mc4y-taskleiste.zip
  (= public/client/mc4y-taskleiste.zip ohne voreinstellung.json). Alle ZIP-Einträge mit create_system 3, damit unzip/macOS die Ausführungsrechte
  (.sh/.command 0755) übernehmen – dafür auch bauen.py client_paket() geändert. LIZENZ.txt = Lizenzhinweis wie in den Werkzeugen mit Link auf den
  GPL-Text (der volle Lizenztext liegt NICHT bei). Geprüft: Pakete entpackt, installieren.ps1 und installieren-server.ps1 -Probelauf aus dem
  entpackten Paket, entpacktes public mit leerem Wegwerf-Datenordner über php -S → Seite „Suite einrichten“ ohne Fehler (TESTPLAN SU-P01).
  Downloadliste zeigt die Dateinamen („Mc4y suite“) – Anzeigenamen setzt Peter im Editor (downloads.json nicht angefasst). Nach jeder Suite-Änderung,
  die heruntergeladen werden soll, Pakete neu bauen. Die installierten Suiten (PC, <server>) haben das geänderte Taskleisten-Zip erst nach
  -Aktualisieren. MACOS-ANLEITUNG (20.10.2026, Peters Fund „Datenschutz-Hinweise und nicht genügend Berechtigungen beim Start des Skripts“):
  Doppelklick-Anleitungen waren falsch – seit macOS 15 öffnet „Rechtsklick › Öffnen“ gesperrte Skripte nicht mehr („Dennoch öffnen“ unter
  Datenschutz & Sicherheit), und Nextcloud/Windows/manche Entpacker verlieren das Ausführungsrecht („nicht die nötigen Zugriffsrechte“).
  Überall gleich empfohlen: im Terminal `ditto -x -k ~/Downloads/<zip> ~/…-paket`, `xattr -dr com.apple.quarantine …`, `cd …`, Skripte IMMER mit
  `bash …` starten; „Operation not permitted“ = TCC-Schutz von Schreibtisch/Dokumente/Downloads/Cloud-Ordnern. Geändert: install/macos/LIESMICH.txt,
  INSTALLATION.txt (Abschnitte 1, 2, 7), handbuch.html (#macos, #grafisch), installer.html, installer-starten.command (Kopf), taskleiste/LIESMICH.txt,
  Blog-Entwurf tool-mc4y-suite-installieren; Pakete neu gebaut (Sicherung editor-backups/*_macos-anleitung). Auf Peters Wunsch OHNE Testlauf.
  Neue macOS-Texte nie wieder mit „Rechtsklick › Öffnen“ formulieren. Blog: neue Entwürfe 2026-10-17-tool-mc4y-suite-installieren.md (Schritt-für-Schritt-Anleitung aller Betriebsarten, Fehlersuche,
  Markenhinweise) und 2026-10-17-tool-mc4y-taskleiste.md (Bilder tool-mc4y-suite-installieren.jpg aus Konsolen-Ausgabe des Probelaufs mit neutralen
  Pfaden + Handbuch-Tabelle + Einrichtungsseite; tool-mc4y-taskleiste.jpg aus Aufnahmen des Programms mit erfundenen Daten über einem eigenen
  Hintergrundfenster, scratchpad blogbild_taskleiste.py/blogbilder_setzen.py, PyMuPDF), Entwurf tool-mc4y-suite aktualisiert (50 Werkzeuge, > 500 Tests,
  Download unter Experimentell, Links auf beide neuen Entwürfe – alle drei zusammen freigeben). Testfassungen 8em-modeller und eam-werkzeuge: „noch
  nicht öffentlich verfügbar“ → Erprobungsfassung unter Experimentell (die Datei liegt in website-test). Datenschutzerklärung Abschnitt 7: Satz zu Suite und
  Taskleiste als selbst betriebene Programmpakete (Sicherung editor-backups/*_blog-suite-installation).
  MAC OHNE HOMEBREW + ANLEITUNGEN FÜR LAIEN (21.10.2026, Peters „Installer-Paket für meinen älteren MacBook Pro 2019 funktioniert nicht, da Homebrew
  nur für die neueren Macs … zusätzlich auf älteren macOS auf Intel … Installationsanleitung grottenschlecht – Schritt für Schritt für Anfänger, das gilt
  für alle Anleitungen“; Auftrag über die Sitzung „8EM-Modell Tool“). GEWÄHLTER MAC-WEG: eigenes PHP der Suite = fertig übersetzte, eigenständige PHP-Datei
  von static-php-cli (https://static-php.dev, Satz „common“: pdo_sqlite mit FTS5, mbstring, openssl, fileinfo, zip, curl, dom …; OHNE sodium, ldap, opcache),
  PHP 8.4.23, Mach-O minos 12.0 → läuft ab macOS 12 (Monterey) auf Intel UND Apple-Chip, ohne Homebrew/Xcode. Verworfen: MacPorts (braucht Xcode-Werkzeuge),
  Homebrew (Intel nur noch eingeschränkt), PHP im Paket (je Architektur ~15 MB mehr, Lizenzhinweise). macOS 10.15/11: Suite nicht möglich → „Nur im
  Browser“ (mc4y-werkzeuge.zip) bzw. macOS aktualisieren (MBP 2019 kann macOS 15). install/macos/installieren.command: MAC_PHP_VERSION/MAC_PHP_URL
  (dl.static-php.dev/static-php-cli/common)/MAC_PHP_SHA_X86/_ARM fest (bei neuer Fassung Dateien laden, shasum -a 256, eintragen), mac_arch über
  hw.optional.arm64 (auch unter Rosetta), php_besorgen (Standard 'eigen'; vorhandenes eigenes PHP nutzen; macOS < 12 → vorhandenes PHP ≥ 8.2 als
  Ausweg, sonst mac_zu_alt; --php-quelle homebrew = alter Weg; --server erzwingt Homebrew), php_eigen_holen (curl https, Prüfsumme, tar nach
  /Library/Application Support/MC4Y-Suite-PHP/<Fassung>/bin/php root:wheel 755 + HERKUNFT.txt, notfalls codesign --sign -, offline über
  <Paket>/voraussetzungen/php-<Fassung>-cli-macos-<arch>.tar.gz), Download erst im Installationsteil (Probelauf-Zeile „PHP … für … herunterladen
  (dl.static-php.dev, ca. 15 MB) … nach … entpacken“), php_fuer_update (bisheriges PHP bleibt; eigenes PHP auf die Paketfassung; fehlt es → eigenes),
  php_alt_aufraeumen, kennwort_hinweis (Datenordner einer früheren Installation + PHP ohne Argon2id → Hinweis). Feld PHP_QUELLE in installation.conf.
  gemeinsam.sh MC4Y_PFLICHT ohne sodium (empfohlen), MC4Y_PHP_DIR; deinstallieren.command entfernt das eigene PHP. Skripte holen sich sudo selbst –
  Anleitungen sagen `bash …` (nicht `sudo bash`), damit vor „Password:“ die deutsche Erklärung kommt (Kennwort ohne Zeichen). Suite: betrieb.php
  Systemprüfung ohne sodium-Pflicht, neue Zeile „Kennwortverfahren“ (Argon2id/bcrypt), ablage_einrichten.php ohne sodium. installer_dienst.py:
  mac_info() → info.mac {fassung, major, chip, zuAlt}, brew_pfad(), familie() 'brew' nur mit Homebrew, Planen erkennt php/eigen und macos/fehlt, Python
  der Taskleiste auf dem Mac = sys.executable (sonst „manuell“, python.org), liefert /public/assets/sprache.js (CSP script-src 'self'); ebenso
  installer-dienst.ps1 (zeigt im Fenster die volle Adresse mit Schlüssel). installer-starten.command und taskleiste/installieren-macos.command rufen
  /usr/bin/python3 nur nach `xcode-select -p` (sonst bietet macOS ungefragt die Xcode-Werkzeuge an) und erklären python.org bzw. den Terminal-Weg.
  INSTALLER: Texte für Laien (Schritte „Was einrichten?“ … , Ziele „Nur im Browser (am einfachsten)“, „Die Suite auf diesem Windows-Rechner“, „Die Suite
  auf diesem Mac“ …), Mac zu alt → ausgegraut, Demo #demo=macalt; Sprachwahl DE | EN über die Suite-Laufzeit (sprache.py suite_teile liest installer.html,
  SUITE_AUSZUG 4; Übersetzungen sprache/texte/0084–0085.json); Schrittnummer und -name sowie PHP-Zusatz (vorName) in getrennten Elementen, sonst
  übersetzt die Laufzeit sie nicht. PAKETE: paket_bauen.py baut zusätzlich mc4y-werkzeuge.zip („Nur im Browser“: Werkzeuge ohne Suite-Anschluss, index.html
  DE/EN aus Katalog + sprache/texte, Links mit ?lang= nach Wahl, Schlüssel mc4y-sprache-ui, LIESMICH.txt/README.txt aus install/browser, LIZENZ.txt);
  neue Pflichtdateien INSTALL.txt, README.md, install/{windows,linux,macos,browser}/{LIESMICH,README}.txt. ANLEITUNGEN (DE + EN, Muster für künftige:
  „Welcher Weg passt zu mir?“ → „Was Sie brauchen“ mit Fassung nachsehen → je Schritt eine Handlung mit genauem Wortlaut und „Sie sehen jetzt …“ →
  „So prüfen Sie, ob es geklappt hat“ → „Häufige Probleme“ → Aktualisieren/Entfernen → „Für Fachleute“): INSTALLATION.txt/INSTALL.txt, install/windows
  (neu), install/macos, install/linux, install/browser, taskleiste/LIESMICH.txt/README.txt, LIESMICH.md (neuer Abschnitt „Installieren – so fangen Sie
  an“)/README.md, handbuch.html/-en.html (neuer Abschnitt „Schnellstart für Einsteiger“ #einstieg, Abschnitte 2, 3, 6, 7, 16). Mac-Anleitung: Finder-
  Doppelklick auf die ZIP (Safari packt oft selbst aus), Terminal per cmd + Leertaste, `cd ` + Ordner ins Terminal ziehen, `bash install/macos/
  installieren.command`. Apple-Symbol nicht als Zeichen schreiben (Privatbereich, geht unter Windows verloren) – „Apple-Symbol (der kleine Apfel)“.
  Blog: tool-mc4y-suite-installieren (Live-Entwurf + Testfassung DE neu für Laien, EN-Testfassung neu übersetzt) und tool-mc4y-taskleiste (Abschnitt
  „Installation – Schritt für Schritt“ in Live, Test DE und EN); _teststand.json basis der beiden Live-Dateien nachgezogen (Sicherung
  editor-backups/*_blog-installation-laien). Blog-Markdown nummeriert Listen nach jeder Unterbrechung neu → Zwischenüberschriften als **fett** im ersten
  Listenpunkt. Datenschutzerklärung DE/EN Abschnitt 7: Satz zum PHP-Download von dl.static-php.dev (Sicherung editor-backups/*_datenschutz-mac-php).
  NICHT GEPRÜFT: auf einem echten Mac (kein Mac und kein bash auf diesem Rechner) – Prüfliste für Peter in tests/TESTPLAN.md Abschnitt 7 (SU-I14).
  BLOGBILD (21.10.2026, Peters „4 – ja“; Sicherung editor-backups/2026-10-21_13-45-46_blogbild-suite-installieren): tool-mc4y-suite-installieren.jpg neu aus
  der Vorführung `installer.html?lang=de#demo=macos&bild=1` bzw. `bild=5` (Edge headless, eigenes Profil, --allow-file-access-from-files – sonst liest die
  Vorführung unter file:// den Katalog nicht –, 1200 × 1400, Ausschnitt x 70–1130 ohne leere Seitenränder, zusammengesetzt mit PyMuPDF wie die übrigen
  Tool-Bilder). ENGLISCHE Testfassung hat ein EIGENES Bild tool-mc4y-suite-installieren-en.jpg (`?lang=en`), weil der Installer bei englischem Browser
  englisch erscheint und die englische Anleitung die englischen Knopftexte nennt. bild_alt in Live-Entwurf und beiden Testfassungen neu; _teststand.json
  basis des Live-Entwurfs nachgezogen.
  MAC: PROGRAMME MIT SYMBOL, TASKLEISTE BLEIBT OFFEN, START BEIM ANMELDEN (22.10.2026, Taskleiste 1.4.2, Suite-Fassung unverändert 0.4.6; Peters
  Mac-Test über die Sitzung „8EM-Modell Tool“: Installation nach der Anleitung klappt (SU-I14 im Kern bestanden), aber „Schließe ich die Kachel-Ansicht,
  verschwindet … die Taskleiste“, „kein Symbol für die Suite oder Taskleiste bei den Programmen“, Autostart beim macOS-Start gewünscht; Sicherungen
  editor-backups/2026-10-22_*_blog-mac-programme). URSACHE: Die Mac-Anleitung installierte nur die Suite; die Taskleiste, die Peter startete, war die
  Taskleiste IM BROWSER (Konto › Darstellung) – sie gehört zum Browserfenster. Die Desktop-Taskleiste hätte ein eigenes Installationsskript gebraucht, das
  sie zudem per `nohup` aus dem Terminal startete und nur ein AppleScript-Programm „MC4Y-Taskleiste.app“ ohne Symbol anlegte. ÄNDERUNGEN: (1) taskleiste/
  installieren-macos.command neu: Programm `~/Applications/MC4Y Taskleiste.app` (Info.plist, Startprogramm Contents/MacOS/mc4y-taskleiste = Bash-Skript,
  das Python mit der Taskleiste per exec startet und bei Fehlern sichtbar meldet (osascript display alert), Symbol mc4y-taskleiste.icns, Anzeigename
  „MC4Y Taskleiste“/„MC4Y Taskbar“ über de.lproj/en.lproj InfoPlist.strings + LSHasLocalizedDisplayName, lsregister), altes „MC4Y-Taskleiste.app“ wird
  entfernt; Rückfrage „Soll die Taskleiste beim Anmelden am Mac automatisch starten? [J/n]“ (nur mit Terminal; ohne Terminal = an), Optionen --autostart,
  --ohne-autostart, --ohne-start, --starten; Start über `/usr/bin/open` (LaunchServices, nicht als Kind des Terminals); Deinstaller sucht Python wie der
  Installer (nicht mehr `command -v python3` – das hätte die Xcode-Werkzeuge angeboten), entfernt beide App-Namen, LaunchAgent (+ launchctl remove).
  (2) mc4y_taskleiste.py 1.4.2: MAC_APP/MAC_AGENT, mac_app(), mac_agent(); LaunchAgent ruft das Startprogramm im App-Paket (sonst Python direkt) mit
  RunAtLoad, LimitLoadToSessionType Aqua, ProcessType Interactive (macOS zeigt dann unter Anmeldeobjekte die App); Einstellungen am Mac: Knopf
  „Systemeinstellungen › Anmeldeobjekte …“ (anmeldeobjekte_oeffnen: /usr/bin/open x-apple.systempreferences:com.apple.LoginItems-Settings.extension bzw.
  bis macOS 12 Accounts.prefPane) mit Tooltip AUTOSTART_MAC; Beenden-Rückfrage nennt Launchpad/Spotlight/Programme-Ordner; Linux-Autostart mit Name[de]
  und Icon. (3) install/macos/installieren.command: suite_app_schreiben = `/Applications/MC4Y-Suite.app` (MC4Y_APP in gemeinsam.sh; Startprogramm
  `exec /usr/bin/open "$ADRESSE"`, LSUIElement, mc4y-suite.icns aus install/macos/, root:wheel) bei JEDER Installation und Aktualisierung (Peters vorhandene
  Suite bekommt es mit --aktualisieren); taskleiste_festlegen fragt (nur Einzelplatz bzw. --taskleiste; im Probelauf ohne Frage, mit --still nicht)
  „Desktop-Taskleiste für <Benutzer> gleich mit einrichten?“ bzw. „… gleich mit aktualisieren?“ und nach dem Start beim Anmelden; taskleiste_einrichten
  am Ende (programme_abschluss in pruefen_und_abschluss, vor KURZANLEITUNG/Protokollkopie): Zip aus $PROGRAMM/public/client als SUDO_USER in /tmp
  entpacken (ditto), voreinstellung.json mit $ADRESSE, dann `sudo -u <Benutzer> -H bash …/installieren-macos.command --lang … --autostart|--ohne-autostart
  --starten|--ohne-start` (--ohne-start, solange die Suite noch kein Konto hat – json_wahr STATUS_JSON eingerichtet –, sonst ginge die Anmeldung ins Leere;
  Hinweis, die Taskleiste nach dem ersten Konto zu starten); Fehler (z. B. kein Python) → Hinweis „--aktualisieren --taskleiste“, Suite läuft weiter.
  Neue Optionen --taskleiste, --ohne-taskleiste, --ohne-autostart (Hilfe DE/EN). Deinstaller entfernt MC4Y-Suite.app (nur mit unserer Kennung in der
  Info.plist) und nennt den Deinstaller der Taskleiste. Kopf „Einzelplatz auf einem echten Mac erprobt (Oktober 2026); Server noch nicht“.
  (4) installer_dienst.py: lauf() mit stdin DEVNULL (Rückfragen der Skripte warteten sonst unsichtbar), Mac-Suite-Skript bekommt --ohne-taskleiste (der
  Installer richtet die Taskleiste selbst ein), Taskleisten-Skript --autostart. (5) Linux: taskleiste/installieren-linux.sh mit Rückfrage/Optionen wie am
  Mac, Name „MC4Y Taskbar“/Name[de] „MC4Y Taskleiste“, Keywords, update-desktop-database, Start per setsid; Suite install/linux/installieren.sh schreibt den
  Menüeintrag auch beim Aktualisieren (Einzelplatz). (6) Portal (portal.js): Darstellung-Seite erklärt, dass die Browser-Taskleiste mit dem Fenster schließt
  (Link Desktop-Taskleiste); Download-Text nennt `bash installieren-macos.command` und Startmenü/Launchpad/Anwendungsmenü (Übersetzungen sprache/texte/0096.json,
  `py sprache/sprache.py suite`). SYMBOLE: einmalig erzeugt mit scratchpad icns_bauen.py (PyMuPDF, ohne Pillow): Suite = Logo im abgerundeten Quadrat
  (Big-Sur-Raster 824/1024, Radius 185), Taskleiste = Navy-Quadrat mit rundem Logo und goldener Leiste mit vier Feldern; ICNS mit 10 PNG-Einträgen (icp4,
  icp5, ic11, ic12, ic07, ic08, ic13, ic09, ic14, ic10 – icp6 bewusst weggelassen, Größe in den Quellen nicht eindeutig); liegen als
  mc4y-suite/install/macos/mc4y-suite.icns bzw. mc4y-suite/taskleiste/mc4y-taskleiste.icns (client_paket packt alle Dateien von taskleiste/).
  DOKU DE/EN: install/macos/LIESMICH/README (Schritte 11–17, „So starten Sie … später“, Probleme, Aktualisieren, Entfernen, Fachleute 6/7), taskleiste/
  LIESMICH/README („So starten Sie die Taskleiste später“, Mac-Schritte 5–7, Linux), INSTALLATION/INSTALL (Abschnitte 5, 9, 10, 11, 12, 13), handbuch(-en).html
  (Schnellstart + h3#spaeter-starten, Abschnitte 6 und 16), LIESMICH.md/README.md; Blog „Suite installieren“ und „Taskleiste“ – Live-Entwurf = Testfassung
  (gleicher Text, Kopf des Entwurfs), DE/EN, aktualisiert/de_stand 2026-10-22, _teststand.json-basis der vier Live-Dateien nachgezogen (scratchpad
  blog_sync_mac.py). Datenschutz unverändert (die Mac-Programme speichern nichts Neues). PRÜFUNG: statisch 0/0, installer_test 61/61, Taskleiste
  Bildschirm 19/19 · Sprache 29/29 · Verbindung 35/35 · Selbsttest 7/7 · Oberfläche 52/52, `sprache/taskleiste.py pruefen` 0 Fehler, api_test 640/640,
  ICNS-Struktur geprüft und Bilder angesehen; Pakete neu (paket_bauen). NICHT geprüft: alles auf einem echten Mac (kein Mac, kein bash hier) – Prüfliste
  SU-I18 in tests/TESTPLAN.md Abschnitt 7 (u. a.: wie macOS den Anmeldeobjekt-Eintrag nennt). Peters installierte Suiten und Taskleiste unverändert.
  ALLES AUF E: (18.10.2026, Peters Rüge „Ich hatte Dir gesagt, die Daten sollen alle auf E:; C ist für das Betriebssystem“ – Gedächtnis
  speicherorte-<benutzer>.md): <server> liegt jetzt KOMPLETT auf E: – Programm E:\MC4Y-Suite, PHP E:\PHP, Daten E:\MC4Y-Suite-Daten, Sicherungen
  E:\MC4Y-Suite-Sicherungen (Zertifikat unverändert <zertifikat-fingerabdruck>; Protokolle Dokumente\MC4Y-Suite-Installationen\<server>_2026-10-18_10-33-28).
  Dafür neu: installieren-server.ps1 -Umziehen [-AltesPhpEntfernen] (alte Installation aus dem physicalPath der Website erkannt, konfig.php +
  Datenordner übernommen, Datenbank vorher gesichert, nach erfolgreichem Start alter FastCGI-Eintrag, alter Programmordner und – wenn unbenutzt –
  altes PHP entfernt, Protokolle nach <Programm>\frueher); auf-server-installieren.ps1 -Programm/-PhpZiel/-Umziehen/-AltesPhpEntfernen/-Auswahl,
  ergänzt „<benutzer>“ ohne Domäne zu <benutzer>@<Domäne des Servers> (sonst WinRM 0x8009030e). SEIT 27.10.2026 macht auf-server-installieren.ps1 (Zeile ~107)
  AUCH die abwärtskompatible Form „<domaene>\<benutzer>“ zum UPN „<benutzer>@<domaene>“, wenn der NetBIOS-Teil zur DNS-Domäne des Servers passt (erste Stelle
  der Server-Domäne, Groß/Klein egal) – vorher akzeptierte der Installer nur den UPN, nicht „DOMÄNE\name“ (Kerberos fand ohne Realm-Zuordnung keine
  Anmeldedaten, 0x8009030e; Peters Fund). Bleibt unverändert: fremde Domäne (ANDERE\admin), lokales Konto (.\admin), bloßer Hostname ohne Domäne. Geprüft mit
  sechs Benutzernamen + Parser/ASCII. WICHTIG: Beim Aktualisieren bleibt PHP, wo es läuft –
  ein mitgegebenes PHP-Paket wird dann NICHT nach C:\PHP entpackt (beide Skripte; vorher Gefahr). Peters PC (Einzelplatz) liegt noch auf
  C:\Program Files / C:\PHP / C:\ProgramData – und BLEIBT dort (Peters Entscheidung 21.10.2026: „nein“ auf die Frage nach E:). Beim Aktualisieren
  die Orte auf C: beibehalten; „Alles auf E:“ gilt nur für <server>.
  AUSWAHL UND INSTALLATIONSANGABEN (18.10.2026): auswahl.txt im Programmordner (Zeilen `aus public/werkzeuge/<datei>.html`, `aus
  public/assets/module/<id>.js`, `aus public/client/mc4y-taskleiste.zip`; nur diese Muster werden gelöscht) – alle Installer (-Auswahl bzw. --auswahl)
  wenden sie nach dem Kopieren an, auch bei jeder Aktualisierung; rechte.php katalog() zeigt nur Werkzeuge/Module, deren Dateien vorhanden sind
  (werkzeug_installiert), rechte_zeilen nur installierte. installation.json im Programmordner (format 'mc4y-installation': art, version, programm,
  daten, sicherungen, php, adresse, port, host, zertifikat, aktualisiert; ohne Geheimnisse, ohne Administratorrechte lesbar). -ErgebnisDatei auch bei
  installieren.ps1. Mit -ErgebnisDatei zeigt installieren.ps1 das Kennwort des Systemkontos NICHT an (die Mitschrift erscheint im Browser).
  Platzhalter im Probelauf: -PhpZip auf eine noch nicht vorhandene Datei = „[Probelauf] PHP entpacken nach <PhpZiel> (das Paket lädt der Installer)“.
  GRAFISCHER INSTALLER (18.10.2026, Peters Auftrag „grafischer, im Browser ausführbarer Installer … erste Auswahl Browser only, Windows 11 Client
  lokal, Windows Server, Linux, Apple; Erstinstallation vs. Update mit Datenübernahme; Module auswählbar, bei Browser only nur die im Browser laufenden,
  andere ausgegraut; fehlende Voraussetzungen installieren, vorher mitteilen und bestätigen lassen“): mc4y-suite/installer.html (Paketwurzel; sechs
  Schritte Zielsystem → Art (neu/update/umzug, vorhandene Installation über api 'erkennen') → Orte (feldOrdner mit Laufwerk-Knöpfen vorschlagAuf,
  Prüfung api 'ordner' → Codes, Texte ORDNER_TEXT/ordnerText) → Module (katalogListe + Taskleiste; grund(): Browser graut Module/Client aus) →
  Voraussetzungen (api 'planen' → [{id, status ok|installieren|manuell|fehlt|fehler|info, version, ziel, quelle, groesse, code}], Texte vorText,
  PLAN_FEHLER; Häkchen „einverstanden“ Pflicht bei „installieren“; Probelauf-Ausgabe aufklappbar; Häkchen „Nur Probelauf“) → Installieren (api
  'starten'/'stand', Phasen PHASEN, Protokoll, Ergebnis mit Adresse/Startseite/Code). api() wertet NUR Text in `fehler` als Fehler (Listen gehören zum
  Ergebnis). Modi: helfer (#t=<Schlüssel>), planung (ohne Helfer → Antwortdatei mc4y-installation.json, format 'mc4y-installation-antworten', die der
  Helfer aus der Paketwurzel oder Downloads übernimmt), demo (#demo=windows|server|browser|linux|macos|update[&bild=N], gleiche Kennungen wie die Helfer,
  echter Katalog wenn erreichbar). HELFER WINDOWS install/windows/installer-dienst.ps1 (Start Installer-starten.bat; ASCII; TcpListener 127.0.0.1
  Zufallsport, Schlüssel 48 hex, Host-/Origin-Prüfung, CSP; -OhneBrowser -PortDatei für Tests; beendet sich nach 2 Std. ohne Anfrage bzw. per 'beenden',
  räumt fertige Aufträge in %TEMP%\mc4y-installer weg): Get-Installationen (X:\MC4Y-Suite je Laufwerk + Program Files; installation.json, sonst Protokolle),
  Api-Planen (PHP am gewählten Ort bzw. releases.json von windows.php.net → neuestes 8.x nts-vs17-x64 mit sha256, oder Paket in voraussetzungen/ mit .sha256;
  VC++ aus der Registry; IIS; Port; WinRM 5985 bei Fernwartung; Python mit tkinter (Find-Python wie Taskleisten-Installer) bzw. winget; Probelauf des
  Skripts mit Auswahl), Ablauf -Auftrag (eigener Prozess: Download PHP mit Prüfsumme, vc_redist von aka.ms mit Authenticode „CN=Microsoft Corporation“,
  dann Install-Browser | Install-Lokal (-Erhoeht per Start-Process -Verb RunAs, Transkript wird mitgelesen, Zeilen „Einrichtungscode“ nicht ins Protokoll,
  Ergebnis aus -ErgebnisDatei; Argumente als Liste, im erhöhten Teil in eine Hashtabelle umgesetzt) | Install-Fern (auf-server-installieren.ps1 im
  SICHTBAREN Fenster mit Anmeldedialog, Ablage Dokumente\MC4Y-Suite-Installationen, Mitschrift fenster.txt mitgelesen) und Install-Taskleiste (Python per
  winget --scope user, Taskleisten-Zip mit voreinstellung.json, installieren-windows.ps1)). „Nur im Browser“: Werkzeuge aus public/werkzeuge OHNE
  Suite-Anschluss (Skript-Tag ../assets/anschluss.js…<!--MC4Y-SUITE--> und /*MC4Y-SUITE-HAKEN*/ entfernt → byte-gleich mit den Einzelwerkzeugen),
  index.html (Startseite aus dem Installer, Kategorien), logo-mc4y.jpg, mc4y-werkzeuge.json {format 'mc4y-werkzeuge', version, werkzeuge, alle};
  Aktualisieren entfernt nur abgewählte Katalog-Werkzeuge, eigene Dateien bleiben; optional Desktop-Verknüpfung (.url) und Öffnen (p.oeffnen).
  HELFER LINUX/MACOS install/installer_dienst.py (Start installer-starten.sh bzw. .command als normaler Benutzer; Python 3.6+, nur Standardbibliothek,
  keine f-Strings; gleiche Schnittstelle; erkennt Installationen über /etc/mc4y-suite/installation.conf; planen = installieren.sh/.command --probelauf
  --still, „[Probelauf] (Empfohlene) Pakete installieren: …“ → Voraussetzung 'pakete'; Installation als ein Skript erhoeht.sh mit Ausgabe in eine
  Datei, erhöht über pkexec (Linux mit Oberfläche) bzw. osascript „with administrator privileges“ (macOS), sonst sudo im Terminal; python3-tk/
  python3-tkinter im erhöhten Teil, python-tk per brew als Benutzer; ohne Oberfläche Hinweis auf ssh -L). NICHT auf echten Linux-/macOS-Systemen
  geprüft, ebenso NICHT: echte Installation über die Benutzerkontensteuerung, Fernwartung über den Installer, Downloads aus dem Netz (nur Planen).
  Prüfstand tests/installer_test.py (45 Prüfungen, D7; seit 21.10.2026 57 mit IN-L01 … L06 Sprache), Testfälle SU-I01 … I08 (… I15). Paket: paket_bauen.py nimmt installer.html, Installer-starten.bat,
  installer-starten.sh/.command (0755), installer_dienst.py, installer-dienst.ps1 mit. Doku: INSTALLATION.txt Abschnitt 2, handbuch.html #grafisch,
  LIESMICH.md „Grafischer Installer“. Blog-Entwurf tool-mc4y-suite-installieren um Abschnitt „Am einfachsten: der grafische Installer“ ergänzt, neues Bild
  tool-mc4y-suite-installieren.jpg (Vorführung Schritt 1 + 5 per Edge headless, scratchpad blogbild_installer.py); Datenschutzerklärung Abschnitt 7
  Satz zu den Downloads des Installers (Sicherung editor-backups/*_blog-suite-installer).
  ORTE FREI WÄHLBAR, VERSCHIEBEN BEIM AKTUALISIEREN (20.10.2026, Peters Kritik „Im Installer werden immer noch Verweise auf C: angegeben, obwohl
  die Suite inkl. Daten auf E: liegt. Nimm bei der Update-Installation die alte Konfiguration … Datenablage auf andere Verzeichnisse verschieben …
  grundsätzlich wählbar, wohin die Suite installiert wird, wohin die Datenablage und wo die Sicherung hin, inkl. Pfadangaben“). installer.html:
  ORTE = programm, php, daten, sicherungen; Neuinstallation OHNE Vorgabe (VORGABEN leer, Feld „noch leer“ gelb, Knopf laufwerkAlle data-lwalle
  „Alle Orte auf Laufwerk …“); update = alle Orte aus S.inst (nur Anzeige); Art umzug auch für den Windows-Einzelplatz („Aktualisieren und
  Datenablage bzw. Sicherungen verschieben“): Felder datenNeu (Ordnerprüfung art 'daten_neu': Codes gleich_bisher, im_daten, nicht_leer) und
  sicherungenNeu, Server zusätzlich programmNeu/phpNeu (nur gemeinsam); leer = bleibt; orteOk verlangt mindestens einen neuen Ort, orteHinweis
  erklärt gesperrtes „Weiter“; zusammenfassung „bleibt/neu (bisher …)“; Ergebnis zeigt Sicherungen. Demo: Installation auf E:\MC4Y-Suite/E:\PHP,
  umzug ohne programmNeu = wie update. HELFER installer-dienst.ps1: Get-SkriptArgumente gibt bei update/umzug -DatenNeu/-SicherungenNeu weiter
  (windows: -Aktualisieren; server: -Aktualisieren bzw. mit programmNeu -Umziehen), Api-Planen lädt PHP/VC nur, wenn das Programm umzieht,
  Install-Fern reicht DatenNeu/SicherungenNeu durch; installer_dienst.py (macOS) trägt bei neuer Installation den Sicherungsordner per cli ein.
  SKRIPTE: installieren.ps1 + installieren-server.ps1 -DatenNeu/-SicherungenNeu (nur mit -Aktualisieren bzw. Server auch -Umziehen; geprüft schon im
  Probelauf: leer/neu, nicht im bisherigen Datenordner, Laufwerk vorhanden, Sicherungen nicht im neuen Datenordner) → am Ende datenordner-verschieben.ps1
  (Probelauf nur Ausgabe). installieren.ps1 -Aktualisieren übernimmt Programm/PHP/Port aus der Aufgabe und Daten/Sicherungen/PHP aus konfig.php bzw.
  installation.json (ohne Administratorrechte im Probelauf), protokolliert die BISHERIGEN Orte statt der C:-Vorgaben, entpackt beim Aktualisieren kein
  PHP-Paket; interaktive Neuinstallation fragt auch den Programmordner, Vorschläge für Daten/PHP auf dessen Laufwerk (E: → E:\MC4Y-Suite-Daten, E:\PHP).
  installieren-server.ps1: PhpZiel-Vorschlag auf dem Laufwerk des Programms, Daten-Vorschlag ebenso, Probelauf liest Orte aus installation.json,
  Kurzanleitung/installation.json/Ergebnis mit dem tatsächlichen Sicherungsordner (sichEff: angegeben → installation.json → cli status → Datenordner).
  datenordner-verschieben.ps1 neu geschrieben: -Ziel und/oder -Sicherungen, findet die Installation selbst (Find-SuiteProgramm in suite-betrieb.ps1:
  physicalPath der IIS-Website bzw. Arbeitsordner der Aufgabe; Get-SuiteInstallation fällt darauf zurück; auch deinstallieren[-server].ps1 nutzen
  es), zählt den Inhalt erst bei angehaltener Suite, vergleicht Prüfsummen von suite/cmdb/loesungen-DB, stellt konfig.php (+ php.ini) um, Sicherungen
  im alten Datenordner ziehen mit, neuer Sicherungsordner mit den Rechten des Datenordners + Kopie vorhandener mc4y-sicherung_*.zip + cli einstellung
  sicherung_ordner, schreibt installation.json. Prüfstand tests/datenordner_verschieben_test.ps1 (12 Prüfungen; Kopie des Skripts mit Platzhaltern für
  Suite-Betrieb, echte cli.php, Wegwerf-Ordner – fasst NIE die echte Aufgabe/IIS an), installer_test.py 51 Prüfungen (IN-O10 … O13, IN-W10/W11).
  Doku: INSTALLATION.txt (2, 4, 9), handbuch.html (#grafisch, #einzelplatz, #ablage, #aktualisieren), LIESMICH.md, TESTPLAN SU-I09 … I11. NICHT geprüft:
  echtes Verschieben mit Administratorrechten (Peters Installationen unverändert).
  CMDB, SCHRITT 1 (18.10.2026, Peters Auftrag „CMDB, die aktuelle IT-Komponenten verwaltet und sie Incidents/Problems/Changes bzw. im 8EM-Modeller der
  TAP zuordnen kann – Schritt 1: Erfassung und Datenbank, lokal oder auf dem Server in einem Unterverzeichnis der Suite“; dazu „kannst Du mit Wireshark
  zusammenarbeiten und von dort Daten importieren?“; SUITE_VERSION '0.4.1 (Stufe 4)', DB_SCHEMA der Suite unverändert). Ein aktiver Netzscanner/Crawler
  und Agents gehören NICHT zu Schritt 1 (nicht gebaut) – Erfassung nur von Hand und über Importe vorhandener Listen; ITSM-/8EM-Anbindung ist Schritt 2.
  Arbeitsmodul 'cmdb' (bauen.py MODULE + MODUL_TEXT, Rechte wie die übrigen Module: 1 lesen, 2 bearbeiten und importieren, 3 zusätzlich endgültig
  löschen). SERVER app/cmdb.php: EIGENE SQLite-Datei <Datenordner>/cmdb/cmdb.sqlite (cmdb_db, WAL, Schema über PRAGMA user_version = CMDB_SCHEMA 1 –
  neue Fassungen als Block vor dem Hochsetzen einfügen) mit Tabellen ci (Stammfelder CMDB_FELDER, merkmale JSON, manuell = Liste von Hand geänderter
  Felder, quelle, zuletzt_gesehen, rev, Papierkorb geloescht/geloescht_am, uid), ci_adresse (art ipv4|ipv6|mac|dns, UNIQUE (art, wert) – eine Adresse
  gehört genau einer Komponente, cmdb_adresse_setzen lässt sie beim Import mit Hinweis zum neuen Gerät wandern), ci_teil (CMDB_TEILARTEN software, dienst,
  freigabe, datentraeger, partition, geraet, schnittstelle), ci_verlauf (jede Änderung mit Person, Quelle, alt/neu), importlauf. Kategorien CMDB_KATEGORIEN
  (12, angelehnt an die Komponentenarten des 8EM-Modellers), Status CMDB_STATUS, Merkmal-Vorschläge CMDB_MERKMALE, Platzhalter-Seriennummern
  CMDB_SERIE_LEER zählen nicht. Werte vereinheitlicht (cmdb_mac aa:bb:…, cmdb_ip mit inet_pton/ntop, cmdb_dns klein, cmdb_kurz = Name ohne Domäne). Anzeigename
  cmdb_anzeige($c, $adr, $pdo) = Name › Hersteller + Modell › erste Adresse (IPv4, IPv6, MAC, DNS) › „Komponente <uid>“ – gleich wie anzeige() in cmdb.js.
  Aktionen cmdb_start (Kennzahlen je Kategorie/Status, zuletzt geändert, letzte Importe), cmdb_liste (Suche über Felder, Adressen, Merkmale, Bestandteile;
  Filter Kategorie/Status/Papierkorb, Sortierung), cmdb_ci, cmdb_speichern (rev-Prüfung → Konflikt 409, Verlauf, Feld wird „manuell“), cmdb_loeschen/
  zurueckholen/endgueltig (≥3), cmdb_import, cmdb_export (CSV/JSON). IMPORT: Die Oberfläche liest die Datei und schickt einheitliche Einträge (höchstens
  CMDB_MAX_IMPORT 5000); cmdb_zuordnen: Seriennummer → MAC → Kurzname → IP (mehrere Treffer = „mehrdeutig“, nicht übernommen); cmdb_anwenden ergänzt nur
  leere bzw. nicht von Hand geänderte Felder (Beschreibung von Hand bleibt), Adressen/Merkmale/Bestandteile kommen hinzu, neue Komponenten nur mit Häkchen;
  probe = gleicher Ablauf in einer Transaktion mit ROLLBACK (Vorschau zeigt genau, was geschieht). Sicherung (sicherung.php) nimmt cmdb/cmdb.sqlite als
  VACUUM-INTO-Momentaufnahme (ohne -wal/-shm) und prüft sie („ok · CMDB ok“); Speicherorte (betrieb.php) nennen „CMDB“; die Update-Sicherung
  der vier Installationsskripte (installieren.ps1, installieren-server.ps1, linux/installieren.sh, macos/installieren.command) kopiert
  cmdb/cmdb.sqlite(-wal/-shm) zusätzlich nach <_update-sicherung>\<Zeit>_vor-<Fassung>\cmdb, wenn vorhanden. OBERFLÄCHE public/assets/module/
  cmdb.js (#/m/cmdb, /liste, /ci/<id>, /neu, /import, /papierkorb): Übersicht mit Kacheln je Kategorie, Liste + Detail (Felder, Adressen, Merkmale,
  Bestandteile, Verlauf; „✎“ = von Hand geändert; bis 1000 px springt die Ansicht zum Detail – zumPanel), Import-Assistent: Datei/Ziehen/Einfügen
  (CSV/TSV mit Trennererkennung, .xlsx mit eigenem ZIP-Leser, Text, CMDB-JSON; UTF-16/UTF-8/Windows-1252), FORMATE liste | ad (Get-ADComputer, deaktivierte
  wahlweise weg, LastLogonDate/DistinguishedName als Merkmal MERKMAL_NAME, Description → Beschreibung) | dhcp (Get-DhcpServerv4Lease bzw. Konsole) | wireshark-endpunkte (Statistiken › Endpunkte, IPv4/
  IPv6/Ethernet) | wireshark-pakete (Paketliste als CSV, mit eth.src/eth.dst → IP ↔ MAC; alle Adressen einer eindeutigen MAC = EIN Eintrag (proMac,
  z. B. IPv4 + IPv6); eine MAC mit > 3 IP-Adressen gilt als Router und wird nicht zugeordnet) | wireshark-namen (Aufgelöste Adressen; Herstellerkürzel wie „Microsoft_0a:01:21“ werden nicht als Name übernommen) | cmdb-json; Spalten-
  zuordnung ZIELE, Netzfilter (Standard STANDARD_NETZE = private IPv4-Bereiche + fc00::/7, IPv6 per BigInt), Häkchen neu anlegen/Kategorie/zuletzt gesehen,
  Vorschau mit Filter-Chips und Auswahl je Zeile, Übernahme in einem Schritt; Wireshark-Dateien liefern nur Adressen und Namen, nie Inhalte der
  Aufzeichnung. Export CSV (eine Zeile je Komponente) und JSON (vollständig, wieder einlesbar). suite.css Block „Modul CMDB“ (.cm-*; Import-Vorschau mit
  Mindestbreiten, damit sie am Handy im Rahmen scrollt). GEPRÜFT: api_test 528/528 (Abschnitt „CMDB: …“: Rechte, Speichern/Konflikt/Verlauf, Adressen
  eindeutig, Import mit Vorschau ohne Schreiben, Abgleich Seriennummer/MAC/Name/IP, mehrdeutig, von Hand geändert bleibt, Papierkorb, Export, Sicherung
  mit CMDB); statisch 0 Fehler; im Browser (Wegwerf-Suite, erfundene Dateien aus `py tests/cmdb_testdateien.py`) mit Maus und Tastatur: AD → DHCP → Excel-
  Inventar → Wireshark-Endpunkte/-Pakete/-Namen nacheinander (Abgleich, Router-Erkennung, Netzfilter), Bearbeiten mit Verlauf und Schutz von Hand
  geänderter Felder, Papierkorb/Zurückholen, Neuanlage, Export → Import ohne Verlust, 375 px ohne Querscrollen. NICHT geprüft: echte AD-/DHCP-/
  Wireshark-Exporte aus Peters Netz, die installierten Suiten (PC, <server> – Aktualisierung nur mit Peters OK). Doku: LIESMICH.md „CMDB“, handbuch.html
  #cmdb, public/datenschutz.php (CMDB-Daten), TESTPLAN SU-C01 … C08; Blog-Entwurf 2026-10-18-tool-cmdb.md, Entwurf tool-mc4y-suite ergänzt.
  OFFEN (Schritt 2, nur mit Peters Auftrag): Zuordnung zu ITSM-Tickets und zum TAP/IT-Komponenten im 8EM-Modeller; Agents bzw. Netzerkennung nur nach
  eigener Abstimmung (keine verborgenen Hintergrundprozesse, siehe „Keine Schadsoftware-Muster“).
  CMDB ↔ ITSM (18.10.2026, Peters „erst die Anbindung an das ITSM-Modul. Jedem Incident, Problem oder Change sollen ein oder mehrere CIs aus der CMDB
  zugeordnet werden können … verschiedene Reports, die sich auch auf die CMDB beziehen“; die 8EM-Anbindung folgt später – Peter hat dort erst größere
  Ergänzungen vor; SUITE_VERSION '0.4.2 (Stufe 4)', DB_SCHEMA und CMDB_SCHEMA unverändert). SERVER: aktion_cmdb_auswahl (uids ≤ 2000, Muster
  [A-Za-z0-9_-]{1,64}, liefert Kurzangaben auch aus dem Papierkorb + „fehlt“, Recht ≥ 1), aktion_cmdb_ci auch mit uid und zusätzlich 'tickets' =
  cmdb_itsm_tickets($s, $uid) (af_dateien($s, 'itsm', '.itsm.json') – nur lesbare Dateien; null ohne ITSM-Zugang; je Ticket art, nr, titel, status, offen
  nach CMDB_ITSM_ENDE, erstellt, datei, adresse = af_wz_adresse + '&suite-zeige=<Nr>'; höchstens 300, neueste zuerst), aktion_kontext nennt module.cmdb
  (nur wenn installiert). anschluss.js: MC4YSuite.cmdb {recht(), start, liste, auswahl, ci, anlegen(felder, adressen), adresse(uid) = BASIS +
  '#/m/cmdb/uid/<uid>'}; nach dem Öffnen per #suite-oeffnen löst #suite-zeige=<Nr> das Ereignis 'mc4y-suite-zeige' aus (das ITSM zeigt das Ticket).
  cmdb.js: Route uid/<Kennung> → ci/<Nr>, Detail-Abschnitt „🛠 Tickets im ITSM-Werkzeug“ (ticketsHtml, .cm-tickets in suite.css). Das ITSM-Werkzeug
  selbst: siehe itsm-editor.html „CMDB-ANBINDUNG“. GEPRÜFT: api_test 535/535 (7 neue Prüfungen: Auswahl gefunden/fehlt/ungültig, 403 ohne Recht,
  > 2000 abgelehnt, uid-Aufruf und 404, Papierkorb gekennzeichnet, ITSM-Datei → Ticketliste mit Sprungadresse, Kontext mit/ohne CMDB-Recht).
  LÖSUNGSDATENBANK + CMDB-HERKUNFT (18.–20.10.2026, SUITE_VERSION '0.4.3 (Stufe 4)', DB_SCHEMA der Suite unverändert, CMDB_SCHEMA 2; Auftrag siehe itsm-editor.html
  „LÖSUNGSDATENBANK …“). CMDB: Spalten herkunft/herkunft_info (Umstellung < 2 per ALTER TABLE, Herkunft aus dem ersten Verlaufseintrag – cmdb_herkunft_aus_quelle),
  CMDB_HERKUNFT hand|itsm|excel|csv|ad|dhcp|wireshark|json|crawler (crawler nur vorgesehen), cmdb_herkunft_art(format, datei), Filter 'herkunft' ('-' = unbekannt),
  Sortierung 'erstellt', cmdb_start mit herkunft_n/herkunftarten, Export mit erstellt/herkunft/herkunft_info/herkunft_text; cmdb_speichern nimmt herkunft {art 'itsm',
  info} (tolerant auch {herkunft: 'itsm'}), alles andere = 'hand'; Importe vermerken die Art bzw. Dateiendung. cmdb.js: Kasten „Herkunft“ (Links filtern), Filter,
  Spalte, Detail .cm-herkunft (Herkunft, Angelegt, Zuletzt geändert, Zuletzt gesehen), CSV. LÖSUNGSDATENBANK: Modul 'loesungen' (bauen.py MODULE/MODUL_TEXT), Oberfläche
  public/assets/module/loesungen.js = iframe .ls-rahmen auf werkzeuge/itsm-editor.html#nur=loesungen&eingebettet[&loesung=] (prüft per HEAD, ob das Werkzeug installiert
  ist); Server app/loesungen.php mit eigener SQLite <Daten>/loesungen/loesungen.sqlite (LS_SCHEMA; kategorie, loesung mit suchwoerter-JSON, version ≤ LS_MAX_VERSIONEN 50,
  anhang mit entfernt-Flag, nutzung; FTS5 loesung_fts, sonst LIKE), Anhänge loesungen/anhaenge/<Lösung>/<Anhang>; Aktionen loesungen_start/liste/passend/lesen
  (+ Rückverweise)/speichern (KB-####, rev → 409, nachtrag ändert die letzte Version)/loeschen/zurueckholen/endgueltig (Recht 3, nur aus dem Papierkorb)/versionen/
  version/wiederherstellen (samt Anhängen)/kategorie (Kreis-Sperre)/kategorie_loeschen (3)/nutzung/anhang_hoch (roh)/anhang_text/anhang_loeschen; Auslieferung
  datei.php?loesung_anhang= (inline nur sichere Typen, sonst CSP sandbox). ls_bewerten = kbBewerten (siehe ITSM) – beide gleich halten; ls_wortliste statt
  array_keys(ls_woerter()): Zahlwörter („2026“) wurden sonst zu int und brachen unter strict_types mit 500 ab (am 20.10. gefunden, api_test prüft es). anschluss.js:
  MC4YSuite.loesungen {recht, start, liste, passend, lesen, speichern(a, basis, kommentar, nachtrag), loeschen, zurueckholen, endgueltig, versionen, version,
  wiederherstellen, kategorieSpeichern/-Loeschen, nutzung, anhangHoch/-Text/-Loeschen, dateiUrl, adresse} und cmdb.anlegen(felder, adressen, herkunft); im nurKb-Modus
  Zugang über das Modulrecht auch ohne ITSM-Recht, keine Pille. Sicherung: VACUUM-INTO-Momentaufnahme + Prüfung „Lösungsdatenbank ok“ (Anhänge als Dateien im ZIP),
  Speicherorte nennen sie, Update-Sicherung in allen vier Installationsskripten nach …/<Zeit>_vor-<Fassung>/loesungen. LIESMICH, handbuch.html (#loesungen, Herkunft
  bei #cmdb), datenschutz.php nachgezogen. GEPRÜFT: api_test 581/581 (Abschnitte „CMDB: Herkunft und Datum“, „Lösungsdatenbank: …“), Wegwerf-Suite im Browser (Modul,
  Kategorie + Lösung mit Bild-Anhang, Übernahme aus dem Ticket, CI mit Herkunft itsm in Übersicht/Filter/Liste/Detail, Export mit Anhang), Pakete neu gebaut. Die
  INSTALLIERTEN Suiten (PC, <server>) sind NICHT aktualisiert – nur mit Peters OK (installieren.ps1 -Aktualisieren -OhneBrowser -OhneWarten bzw.
  auf-server-installieren.ps1 … -Aktualisieren).
  8EM-ABGLEICH (20.10.2026, SUITE_VERSION '0.4.4 (Stufe 4)', Schemata unverändert; Auftrag siehe 8em-modeller.html „FASSUNG 19“): app/cmdb.php CMDB_KATEGORIEN
  + Anwendung / IT-System, Plattform, Architekturplan (TAP), Software / Dienst (16), CMDB_STATUS + idee, projekt, einfuehrung, abschaltung, abgeschaltet (aktiv =
  im Betrieb), CMDB_STATUS_AUS ausgemustert + abgeschaltet (Liste blendet sie ohne Wahl aus), Herkunft '8em'. Aktion cmdb_abgleich (Recht 2, ≤ CMDB_ABGLEICH_MAX
  3000, probe/neu): Zuordnung cmdb_8em_suchen = CMDB-Kennung → Merkmal „8EM-Kennung“ (= ref „Modell|Element“) → gleicher Name in der Kategorie ohne 8EM-Kennung
  (mehrere = mehrdeutig); im Papierkorb → 'papierkorb' (nicht neu angelegt); Status und Merkmale nachgeführt, übrige Felder nur, wenn leer. anschluss.js
  MC4YSuite.cmdb.abgleich(eintraege, quelle, opt); cmdb.js (Symbole, Status-Klassen, CMDB-JSON mit quelle '8EM-Modeller' = Herkunft 8em); ITSM i2_daten.js CI_STATUS
  mit den Phasen, CI_AUS, Kategorien/Symbole, i8_cmdb.js Filter. api_test 592/592 (Abschnitt „CMDB: Abgleich mit dem 8EM-Modeller“), Pakete neu (paket_bauen),
  LIESMICH „Abgleich mit dem 8EM-Modeller“, handbuch.html #cmdb-8em, datenschutz.php „CMDB und 8EM-Modeller“.
  INSTALLIERT 0.4.4 (20.10.2026 abends, Peters „Suiten installieren“): PC 21:38 (installieren.ps1 -Aktualisieren -OhneBrowser -OhneWarten per Hilfsskript mit
  Transkript, Sicherung C:\ProgramData\MC4Y-Suite-Daten\_update-sicherung\2026-10-20_21-38-28_vor-0.4.4; der PC bleibt auf C:\Program Files/C:\PHP/C:\ProgramData –
  laut Peter 21.10.2026 auch künftig dort; die erste Windows-Rückfrage wurde abgebrochen, erst nach Peters erneutem OK wiederholt) und <server> 21:38–21:41
  (auf-server-installieren.ps1 -Server <server> -Aktualisieren -OhneWarten im sichtbaren Fenster, Peter tippte die Anmeldung; Programm
  E:\MC4Y-Suite, PHP E:\PHP, Daten E:\MC4Y-Suite-Daten unverändert; Protokolle Dokumente\MC4Y-Suite-Installationen\<server>_2026-10-20_21-38-53). Nur lesend
  geprüft: PC 0 Fehler/0 Warnungen (Programmdateien = Projekt), <server> 0 Fehler/0 Warnungen. Vorher hatte Peter beide selbst auf 0.4.2 gebracht (19.10.).
  Taskleiste auf Peters PC auf 1.3.0 (auf sein OK, installieren-windows.ps1 aus dem entpackten Zip, läuft). ANZEIGEFEHLER behoben (21:43): beim Aktualisieren
  ohne -Programm nannte auf-server-installieren.ps1 als „Ziel“ und installieren-server.ps1 als erste „Programm:“-Zeile den Vorschlag C:\Program Files\MC4Y-Suite,
  obwohl am Ort der laufenden Installation (E:) aktualisiert wurde ($zielProg bzw. Ausgabe erst nach dem Blick in die IIS-Konfiguration). Die installierten Kopien
  dieser zwei Skripte (install\windows auf PC und <server>) haben den Stand bis zum nächsten -Aktualisieren noch nicht.
  CMDB-ERKENNUNG: AGENTEN UND CRAWLER (22.10.2026, SUITE_VERSION '0.4.7 (Stufe 4)', CMDB-Schema 3; Peters Auftrag über die Sitzung „8EM-Modell Tool“:
  „Crawler und Agents für Windows 10/11, macOS ab 12, Linux … auch für Raspi 3/4/5 …, Android … und Home Assistant … Agents liefern auf Anforderung oder
  periodisch … Crawler überführt die Daten in die CMDB … System, Hauptspeicher, Festplatten und Partitionen, Betriebssystem und Version, installierte
  Software, …“; Guardrails der Sitzung: nur Bestandsaufnahme, keine Angriffe/Anmeldeversuche, keine Schadsoftware-Muster, sichtbar installiert, nur an die
  eingetragene Suite über HTTPS, Testen gegen Wegwerf-Suite/nachgebildete Netze. Sicherung editor-backups/2026-10-22_*_cmdb-erkennung (Blog/Datenschutz/Doku).
  SERVER: CMDB-Schema 3 (cmdb.sqlite): erk_netz, erk_teilnehmer (uid ag…/cr…, hash = SHA-256 des Geräteschlüssels, ci_id = feste Zuordnung, jetzt, status),
  erk_schluessel (Einrichtungsschlüssel, nur SHA-256; art agent|crawler|beide), erk_lauf, erk_oui, erk_versuch; cmdb_schema sichert vorher (db_vorher_sichern
  'cmdb', 3). Herkunft CMDB_HERKUNFT um 'agent' ergänzt, 'crawler' = „durch einen Netzscan gefunden“. app/erkennung.php (Endpunkt-Logik + Admin-Aktionen),
  public/agent.php = Endpunkt (POST JSON, nur HTTPS, Ratenbegrenzung 60/15 min je IP, 12 MB). cmdb_anwenden erweitert: $opt['ci_id'] feste Zuordnung (sonst über
  Merkmal „Agent-Kennung“, dann Serie/MAC/Name/IP), $opt['teile_quelle'] ersetzt die Bestandteile dieser Quelle vollständig (Agent liefert je Meldung die ganze
  Liste). start.php: require erkennung.php; AKTIONEN erk_start/erk_laeufe (Recht 1), erk_jetzt (2), erk_netz_speichern/-loeschen, erk_schluessel_neu/-loeschen,
  erk_teilnehmer_speichern/-entfernen, erk_oui_import (3). ENDPUNKT: anmelden (Einrichtungsschlüssel → eigene Kennung + Geräteschlüssel 256 Bit, nur in der
  Antwort), puls (jetzt + Netze für den Crawler), melden (inventar Agent / ergebnisse Crawler), abmelden. Inventarformat 'mc4y-inventar' v1 (system/bs/cpu/
  speicher/datentraeger/partitionen/netzwerk/software/dienste/freigaben/geraete/merkmale) – KEINE Benutzernamen/Dateiinhalte. Crawler: fehlender Hersteller aus
  der MAC (ERK_OUI_FEST eingebaut + erk_oui hochladbar). cmdb.js: Reiter „📡 Erkennung“ (Agenten/Crawler-Liste mit Status/jetzt, Netze CRUD, Einrichtungsschlüssel
  + Einrichtungsdatei mc4y-erkennung.json, Läufe, OUI-Upload, Paket-Downloads). sprache: 130 neue Suite-Texte übersetzt (sprache/texte/0097.json, py sprache/
  sprache.py suite). PROGRAMME in mc4y-suite/erkennung/ (nicht in der Suite ausgeführt, nicht eigenmächtig verteilen): python/ (mc4y_erkennung.py + mc4y_agent.py
  + mc4y_crawler.py, nur Standardbibliothek, ab 3.8; Installer installieren.sh (systemd Linux/Raspberry Pi), installieren-macos.command (launchd), installieren-
  termux.sh (Android/Termux-job-scheduler) + Deinstaller; bilingual --lang), windows/ (PowerShell, geplante Aufgabe SYSTEM, -ExecutionPolicy RemoteSigned nach
  Unblock-File, kein Bypass/EncodedCommand für die Aufgabe; -Sprache de|en): mc4y-win-netz.ps1 (Netz/Protokoll) UND mc4y-win-inventar.ps1 (Bestandsaufnahme) sind
  GETRENNT, weil Bitdefender eine einzelne .ps1, die installierte Software liest UND über HTTP sendet, als Schnüffel-Muster einstuft und quarantäniert –
  mc4y-win-netz.ps1 (ohne Registry-Enum) + mc4y-win-inventar.ps1 (ohne Netz) überleben; ein gesperrter Rest mc4y-win-kern.ps1 liegt noch im Ordner (nicht lesbar,
  nicht löschbar, wird nirgends verwendet; statisch_pruefen überspringt unlesbare .ps1 als Hinweis). homeassistant/ (custom_components/mc4y_cmdb, config_flow;
  Sammelkern sammeln.py + mc4y_client.py ohne HA testbar; meldet HA-Host + verwaltete Geräte, Kategorie „Gebäude- und Haustechnik“). bauen.py erkennung_pakete()
  → public/client/mc4y-erkennung.zip (windows+python, OHNE den Rest) und mc4y-cmdb-homeassistant.zip; paket_bauen.py kopiert beide nach website-test/downloads/
  experimentell/software. GUARDRAILS: sichtbar installiert, Deinstaller, lesbare Einstellungen (Windows C:\ProgramData\MC4Y-CMDB, sonst /etc/mc4y-cmdb bzw.
  ~/.config/mc4y-cmdb, 0600) und Protokoll, keine verborgene Shell, kein Autostart außerhalb des Installers, kein Nachladen, TLS nie aus; Crawler greift nichts an,
  SNMP nur lesend mit eingetragener Community, IPv6 nur über die Nachbartabelle. Hinweis für Peter: Ein PowerShell-Inventar-Agent ähnelt im Muster einem
  Schnüffelprogramm – auf Windows-Endgeräten mit strengem Virenschutz ggf. den Agent-Ordner als Ausnahme aufnehmen oder den Python-Agenten nutzen.
  GEPRÜFT gegen eine Wegwerf-Suite (nur lokal/127.0.0.1): api_test 661/0 (Abschnitt „CMDB: Erkennung (Agenten und Crawler)“ – Schlüssel, Anmeldung, Puls/jetzt,
  Meldung mit Software-Ersatz statt Doppeln, feste Zuordnung heilt nach Papierkorb, Crawler-Netze/Hersteller-aus-MAC/401, OUI-Import, Entfernen); Python-Agent/
  -Crawler 16/16 (Software ersetzt, OUI Raspberry Pi, Scan-Bausteine, IPv6 nicht abgesucht); Windows-Agent mit ECHTEM Inventar (199 Software, RAM, feste Zuordnung)
  und Windows-Crawler (Scan 127.0.0.1/32); Home-Assistant-Sammelkern 9/9 (Kategorie haustechnik, Geräte/Integrationen, Ersetzen); Erkennung-Reiter im Browser
  (Schlüssel erzeugt, Einrichtungsdatei). NICHT geprüft: echte Linux-/macOS-/Android-/Home-Assistant-Geräte, Peters Netz, echter Netzscan, die installierten
  Suiten (PC/<server> unverändert; Aktualisierung nur mit Peters OK). Datenschutz (public/datenschutz.php), Handbuch DE/EN (#cmdb-erkennung), LIESMICH.md,
  Blog 2026-10-18-tool-cmdb (Live-Entwurf + Testfassung DE/EN, Abschnitt „Automatisch erfassen: Agenten und Crawler“) nachgezogen.
  NACHBESSERUNG 22.10.2026 nachmittags (Peters Klarstellung über „8EM-Modell Tool“: „Crawler und Agents laufen in meinem Netz, unter meiner Verantwortung …
  je Unternehmen konfigurierbar, nur im Unternehmensnetz“; EIGENER Blog-Beitrag statt nur Abschnitt; Hinweis auf Virenscanner und Firewalls; Sicherung
  editor-backups gibt es nicht – Doku-Änderungen): (1) GEPRÜFT ohne Fund: Pakete enthalten KEINE voreingestellte Suite-Adresse (bauen.py erkennung_pakete packt
  feste Dateilisten, kein voreinstellung.json), Agenten/Crawler senden nur an die bei der Einrichtung eingetragene --adresse, keine Telemetrie/Update-Prüfung
  bei my-coaching4you.de, keine Online-Herstellerabfrage (OUI nur lokal). (2) Suite-Datenschutz public/datenschutz.php: Absatz CMDB-Erkennung auf kurze, neutrale
  Fassung gekürzt (wie die anderen Module), „die Daten verlassen deren Netz nicht“; Website-Datenschutzerklärung UNVERÄNDERT (selbst betriebene Programme,
  laufen nicht auf der Website – Gedächtnis „Datenschutz und Blog automatisch“). (3) EIGENER Blog-Beitrag 2026-10-22-tool-crawler-agents.md DE + en + Testfassungen
  (Test = status veröffentlicht, Live = Entwurf), Bild website/blog/files/tool-crawler-agents.jpg (Schaubild per PyMuPDF, scratchpad blogbild_crawler.py);
  Abschnitte „In Ihrem Netz, in Ihrer Verantwortung“ und „Virenscanner und Firewall“ (AV-Ausnahme, Quarantäne erkennen, gesperrter Rest erst nach Neustart,
  zwei getrennte Windows-Teile bzw. Python-Agent; Firewall: Agenten nur ausgehend HTTPS 443, Crawler ICMP/geprüfte TCP-Ports/SNMP UDP 161 in die Netze inkl. IPv6,
  Server nur HTTPS eingehend, gesperrter Ping = unsichtbar, IDS/IPS abstimmen + Zeitplan außerhalb der Arbeitszeit + gedrosselt). Der CMDB-Beitrag (DE/EN, live+test)
  verweist jetzt nur noch knapp darauf. (4) Dieselben Hinweise in erkennung/LIESMICH.txt (DE) + README.txt (EN) + homeassistant/LIESMICH.txt und im Handbuch
  handbuch.html/handbuch-en.html (#cmdb-erkennung). (5) Neu gebaut: mc4y-suite/bauen.py (mc4y-erkennung.zip/mc4y-cmdb-homeassistant.zip), sprache.py suite (datenschutz
  im Auszug nicht enthalten – Absatz bleibt wie die Nachbarmodule), paket_bauen.py → website-test/downloads/experimentell/software (mc4y-suite.zip 0.4.7,
  mc4y-erkennung.zip 40088). GEPRÜFT: statisch 0/0 (69 Hinweise alt), api_test 661/0, website_pruefen 0 Fehler (Test 0 fehlende Ziele). HOCHGELADEN auf
  <test-domain> (353 Dateien, 0 Fehler, 0 gelöscht, gespeicherter Zugang; Protokoll sftp/protokolle/*_test_hoch.txt); online byte-genau geprüft: Pakete,
  Blog DE/EN (Virenscanner/Firewall), CMDB-Verweis, Bild.
  (6) ANLEITUNGEN FÜR LAIEN (22.10.2026 abends, Peters „Schritt für Schritt für Anfänger, das gilt für alle Anleitungen“, über „8EM-Modell Tool“):
  erkennung/LIESMICH.txt (DE) + README.txt (EN) + homeassistant/LIESMICH.txt (DE+EN) komplett neu in der Suite-Anleitungsstruktur – Welcher Weg passt
  zu mir? / Was Sie brauchen (inkl. Rechte + Python) / Schritt für Schritt je System mit genauem Wortlaut und „Sie sehen jetzt …“ / So prüfen Sie, ob
  es geklappt hat / Häufige Probleme (Virenscanner mit Ausnahme/Quarantäne/Neustart/Python-Agent; Firewall ausgehend HTTPS 443, Crawler ICMP/TCP/SNMP
  UDP 161 inkl. IPv6, Server nur eingehend; IDS/IPS; Python fehlt; Schlüssel ungültig; selbst signiertes Zertifikat --cafile/-Zertifikat) / Aktualisieren
  und Entfernen / Für Fachleute (Einstellungen+Protokoll lesbar, nur Bestandsdaten, --intervall/-Intervall, SNMP-Community je Netz in der Suite, keine
  Voreinstellung/Telemetrie). ASCII (ue/ae). Pakete neu gebaut (bauen.py, paket_bauen.py; mc4y-erkennung.zip jetzt mit LIESMICH 9705 B/README 8888 B,
  im ZIP geprüft). GEPRÜFT: statisch 0/0, Testbau sauber (342 Seiten, keine defekten Links), website_pruefen 0 Fehler (Test 0 fehlende Ziele). HOCHGELADEN
  auf <test-domain> (346 Dateien, 0 Fehler, 0 gelöscht, gespeicherter Zugang, Helfer selbst gestartet und beendet; Protokoll sftp/protokolle/
  2026-10-22_*_test_hoch.txt); online byte-genau geprüft (Pakete, Blog DE/EN, CMDB-Verweis, Bild) und der neue Anleitungstext im ausgelieferten ZIP.
  (7) EINSTEIGER-TIEFE + ECHTE UMLAUTE (22.10.2026 abends, Peters Rüge über „8EM-Modell Tool“: die Erkennungs-Anleitungen seien noch Kurzfassungen für
  Fachleute und durchgehend ae/oe/ue/ss statt Umlaute – „Schritt für Schritt für Anfänger, das gilt für alle Anleitungen“): erkennung/LIESMICH.txt (DE),
  README.txt (EN) und homeassistant/LIESMICH.txt (DE+EN) komplett neu in der Suite-Anleitungsstruktur (install/windows bzw. macos) – „Welcher Weg passt zu
  mir?“ (Agent/Crawler/Home Assistant) / „Was Sie brauchen“ / „Schritt 1 Einrichtungsschlüssel in der Suite (Verwaltung › CMDB › 📡 Erkennung)“ / „Schritt 2
  entpacken + mc4y-erkennung.json daneben“ / „Schritt 3“ je System mit genauem Wortlaut und „Sie sehen jetzt …“ (Windows: ZIP „Alle extrahieren“, PowerShell
  per Windows-Taste – ohne Admin, das Skript elevatet selbst –, cd über die Explorer-Adressleiste, Start als `powershell -ExecutionPolicy Bypass -File
  .\installieren.ps1 …` = Policy nur für diesen Aufruf, UAC mit „Ja“; Linux/Raspberry Pi: unzip, cd, sudo sh; macOS: Terminal ⌘+Leertaste, cd + Ordner
  hineinziehen, bash, Kennwort ohne Zeichen; Android: Termux + Termux:API aus F-Droid, `pkg install python termux-api`, `termux-setup-storage`, Dateien aus
  ~/storage/downloads holen, bash installieren-termux.sh; Home Assistant: Ordner custom_components/mc4y_cmdb per File-editor-Add-on/Samba/SSH, Neustart,
  Integration „MC4Y CMDB“ hinzufügen) / „Schritt 4 So prüfen Sie …“ / „Häufige Probleme“ (Virenscanner, Firewall, IDS/IPS, Python fehlt, Schlüssel ungültig,
  keine Verbindung, selbst signiertes Zertifikat) / „Aktualisieren und Entfernen“ / „Für Fachleute“ (die alten Befehlszeilen + Optionen). ECHTE Umlaute in
  den DE-Dateien (nur .ps1/.sh/.command bleiben ASCII; README.txt ist Englisch). HACS NICHT behauptet (die HA-Integration ist ein einfacher
  custom_components-Ordner, kein HACS-Repo). GEPRÜFT: statisch 0/0, Testbau sauber, mc4y-erkennung.zip enthält LIESMICH 13,9 KB (mit Umlauten/ExecutionPolicy/
  termux-setup-storage) und README 12,7 KB. HOCHGELADEN auf <test-domain> (346 Dateien, 0 Fehler, 0 gelöscht, Helfer selbst gestartet und beendet);
  Pakete online byte-genau.
  VERTEILER „Geräte verbinden" (22.10.2026 abends, Baustein der CMDB-Erkennung; Peters Auftrag über „8EM-Modell Tool", Soforthilfe-Nachbau): Agenten/Crawler
  per WinRM auf mehrere Windows-Rechner auf einmal ausrollen, aus der Suite heraus. Peters Wahl: Ansicht im Reiter CMDB › Erkennung + lokaler Helfer; Reihenfolge
  1 Windows/WinRM (fertig) → 2 Linux/Raspberry Pi/macOS (SSH) → 3 Home Assistant (später). SERVER: NICHTS neu – die Ansicht mintet den Einrichtungsschlüssel über
  erk_schluessel_neu, Geräte melden sich wie gehabt über agent.php. DATEIEN mc4y-suite/erkennung/verteiler/: verteiler-win.ps1 (EIN Ziel per WinRM, verallgemeinert aus
  dem Ausrollen vom 22.10.; Kennwort NUR über die Umgebung MC4Y_PW, nie Befehlszeile; Fortschritt als „@MC4Y <json>“-Zeilen; -Probelauf = nur verbinden + OS/Aufgabe
  prüfen; New-PSSession → Dateien kopieren → installieren.ps1 auf dem Ziel mit -ExecutionPolicy Bypass, die WinRM-Sitzung hat ein volles Admin-Token, keine UAC),
  verteiler-dienst.ps1 (lokaler HTTP-Helfer auf 127.0.0.1, roher TcpListener wie installer-dienst.ps1, Token im Header x-mc4y-token, CORS + Access-Control-Allow-Private-
  Network für die Suite-Herkunft; kooperativer Zustandsautomat ein Ziel nach dem anderen – Kindprozess verteiler-win.ps1, Kennwort über dessen Umgebung, Ausgabe async;
  API info/start/stand/abbrechen/beenden; öffnet die Suite-Ansicht mit ?helfer=Port.Token, Adresse aus verteiler.json {adresse}), Verteiler-starten.bat, LIESMICH.txt
  (DE+EN). ASCII-only, PS 5.1-tauglich, Parser OK. ANSICHT public/assets/module/cmdb.js: Route 'verteiler', Funktionen vertHelfer/vertApi/verteilerSeite/vertZeichnen/
  vertStatusZeichnen/vertStart/vertPollen/vertEingabe/vertKlick, Dispatch über '#cmVert', Einstieg „Mehrere Windows-Rechner auf einmal verbinden → Geräte verbinden" im
  Erkennung-Reiter. Ohne Helfer: Download client/mc4y-verteiler.zip + „Einrichtungsdatei (verteiler.json)". Mit Helfer: Rolle (Agent/Crawler), Rechner (einer je Zeile),
  Admin-Konto + Kennwort (gilt für alle Ziele, NICHT gespeichert – nur im Speicher), Intervall, „Verbindung prüfen" (Probelauf) + „Ausrollen", Live-Status je Ziel;
  Schlüssel live über erk_schluessel_neu, Zertifikat per fetch von der Suite als base64 (zertifikatText) durchgereicht. PAKET: bauen.py erkennung_pakete() baut zusätzlich
  public/client/mc4y-verteiler.zip (MC4Y-CMDB-Verteiler/ mit den Helfer-Dateien + windows/ = installieren.ps1 & mc4y-*.ps1; Find-ErkOrdner .\windows). CSP (WICHTIG, Peters
  Freigabe „A" am 22.10.): app/kern.php UND app/router.php connect-src 'self' → connect-src 'self' http://127.0.0.1:* http://localhost:* (nur Verbindungen, nur Loopback,
  keine fremden Skripte) – sonst blockt die Suite-eigene CSP den Browser-Abruf zum lokalen Helfer (im Test bestätigt: „violates CSP connect-src 'self'"). Gilt für alle
  installierten Suiten beim nächsten Update; api_test-CSP-Prüfung ggf. anpassen (Werkzeugseiten bleiben connect-src 'self'). GEPRÜFT: Engine-Selbsttest 11/11 (scratchpad
  verteiler_selbsttest.ps1: HTTP/Token/CORS/Preflight/PNA + Zustandsautomat gegen .invalid), Paket entpackt (Helfer findet erkOrdner=True/worker=True), Browser-Rauchtest
  gegen eine Wegwerf-Suite (Ansicht lädt fehlerfrei, info liefert Rechner/Domäne „✓ verbunden", Probelauf gegen .invalid rendert „✕ fehler" mit WinRM-Meldung – Start/Poll/
  Status über die echte UI). node fehlt auf dem Rechner → JS strukturell + im Browser geprüft. OFFEN: echter Lauf gegen <server> nach Suite-Aktualisierung (zugleich die
  letzte CORS/PNA-Prüfung im echten https-Browser; Rückfall, falls ein Browser blockt: Helfer liefert die Ansicht selbst same-origin aus), Blog crawler-agents (DE/EN) um
  „Geräte verbinden" ergänzen, handbuch DE/EN, TESTPLAN SU-V*.
  STUFE 2 (SSH, 22.10. nachts, Peter „1 und 2"): verteiler-ssh.ps1 (EIN Linux-/Raspberry-Pi-/macOS-Ziel per ssh.exe/scp.exe – schlüsselbasiert BatchMode=yes,
  Host-Schlüssel accept-new; sudo per „sudo -S" über die SSH-Standardeingabe mit optionalem Kennwort aus env MC4Y_PW, sonst „sudo -n"; ruft installieren.sh bzw.
  installieren-macos.command mit --adresse/--schluessel/[--crawler]/[--intervall]/[--cafile PEM aus base64-DER]/[--community]/--lang; -Probelauf = uname+python+
  vorhanden). HELFER: Find-PyOrdner + $script:SshWorker, Api-Start nimmt os (windows|linux|macos)/sshKey/sshPort (+cerB64), Pump-Auftrag verzweigt nach $A.os
  (windows→verteiler-win, sonst→verteiler-ssh), Api-Info meldet ssh/pyOrdner. cmdb.js: OS-Auswahl (Radios name=vertOs), bei SSH Felder SSH-Benutzer/sudo-Kennwort/
  privater Schlüssel/SSH-Port + angepasste Hinweise/Validierung (pw bei SSH optional); vertStart schickt os/sshKey/sshPort. bauen.py: mc4y-verteiler.zip enthält jetzt
  verteiler-ssh.ps1 + python/ (installieren.sh, installieren-macos.command, mc4y_*.py, deinstall). DOKU Stufe 2 angepasst: public/datenschutz.php, handbuch DE/EN,
  TESTPLAN SU-V05/06/07 – Blog crawler-agents bewusst noch bei „Windows auf einmal" (SSH-Ausrollen erst öffentlich bewerben, wenn gegen ein echtes Linux-Ziel bestätigt).
  GEPRÜFT: SSH-Weiche-Selbsttest 4/4 (scratchpad verteiler_ssh_test.ps1: info ssh/pyOrdner, Linux-Probelauf gegen .invalid → „fehler" mit SSH-Meldung), Browser-Test gegen
  Wegwerf-Suite (OS→Linux → SSH-Felder erscheinen, Probelauf über die UI → „Keine SSH-Verbindung", keine Konsolenfehler); OpenSSH-Client auf Peters PC vorhanden.
  OFFEN: echter Lauf gegen ein Linux/Raspberry-Pi-Ziel (Peter, SU-V07) und <server>-Update für BEIDE Stufen (Peters Admin-OK; spielt den gesamten Suite-Stand ein).
  Stufe 3 (Home Assistant) noch offen.
  KENNWORT-WIEDERHERSTELLUNG (22.10.2026, SUITE_VERSION '0.4.8 (Stufe 4)', DB_SCHEMA 12; Peters Auftrag über die Sitzung „8EM-Modell Tool“: „Kann mich
  [in der Suite auf <server>] nicht anmelden und mir ist aufgefallen, es gibt keine Passwort-Rücksetzroutine“ – ohne Mail, zweisprachig, alle Systeme):
  Es gibt bewusst KEINE Rücksetzung per E-Mail. Am Server erzeugt die betreuende Person einen RÜCKSETZCODE für ein Konto; damit setzt die
  Kontoinhaberin/der Kontoinhaber selbst ein neues Kennwort. DB-Schema 12: Tabelle `kennwort_code` (benutzer_id PRIMARY KEY, hash = SHA-256 des
  normalisierten Codes, ablauf = jetzt()+1800, versuche, erstellt) – nur der PRÜFWERT, nie der Code. konto.php: KENNWORT_CODE_ALPHABET (32 Zeichen ohne
  0/O/1/I/L), kennwort_code_normalisieren (Großschreibung, Bindestriche/Leerzeichen egal), kennwort_code_erzeugen($login) (12 Zeichen in 3 Gruppen,
  ON CONFLICT ersetzt je Konto; Domänenkonto → SuiteFehler 'domaene'; protokolliert 'kennwort_code_erzeugt' NUR mit Anmeldenamen, NICHT den Code) und
  kennwort_wiederherstellen($login, $code, $kennwort) (öffentlich, Rate-Begrenzung je IP wie `anmelden` über anmeldeversuche; prüft ablauf > jetzt und
  versuche < 5, hash_equals; abgelaufen/5× falsch → Code gelöscht; bei Treffer kennwort_regeln ZUERST (Regelverletzung entwertet den Code nicht), dann
  hash/pw_wechsel=0/fehlversuche=0/gesperrt_bis=0 setzen, kennwort_code + sperren + alle sitzungen des Kontos löschen, 'kennwort_wiederhergestellt' ohne
  Code). start.php: öffentliche Aktion `kennwort_wiederherstellen` ('o'); verwaltung.php aktion_kennwort_wiederherstellen. cli.php: `kennwort-code
  <Anmeldename>` (gibt den Code NUR aus, nicht ins Protokoll; zweisprachig; Rückgabewert 1 bei unbekanntem Konto/Domänenkonto). SKRIPTE (sichtbares
  Fenster, keine Mitschrift, Code nur im Fenster): install/windows/kennwort-zuruecksetzen.ps1 (+ Kennwort-zuruecksetzen.bat; ASCII; UAC-Selbstelevation,
  dot-sourced suite-betrieb.ps1 → Get-SuiteInstallation + Invoke-SuiteCli kennwort-code; -Pause hält das erhöhte Fenster offen), install/linux/
  kennwort-zuruecksetzen.sh und install/macos/kennwort-zuruecksetzen.command (dünne Hüllen: gemeinsam.sh → installation_laden + `suite_cli kennwort-code`
  OHNE Log; sudo-Selbstaufruf; echte Umlaute). OBERFLÄCHE portal.js seiteAnmelden: Link „Kennwort vergessen?“ zeigt einen Kasten „Kennwort zurücksetzen“
  (Erklärung mit Hinweis Domänenkonto, Anmeldename/Rücksetzcode/neues Kennwort zweimal; Kennwort-Vergleich im Browser; Erfolg → grüne .meld.ok, Anmeldename
  vorbelegt). Verwaltung › Benutzer: der Kennwort-Block ist ein eigenes <fieldset> „Kennwort“ (Setzen hebt die Sperre auf), Häkchen „muss das Kennwort bei
  der nächsten Anmeldung ändern“ (pw_wechsel) war schon vorhanden. ÜBERSETZUNG: sprache/texte/0098.json (19 Paare, inkl. Absatz mit `<1>Rücksetzcode</1>`);
  'meld ok' entfernt (classList statt className), Cache `.cache-schluessel.json` geleert → mc4y-suite 99,9 % (`pruefen` 0 Fehler). DOKU: handbuch.html/
  handbuch-en.html #kennwort (11a), LIESMICH.md (Stufe 4), public/datenschutz.php (Code nur als SHA-256, nicht protokolliert), TESTPLAN SU-PW01…PW04 (bis 23.10.2026 doppelt als SU-W01…W04 vergeben).
  GEPRÜFT: api_test 675/0 (Abschnitt „Kennwort-Wiederherstellung“: cli erzeugt Code/nicht im Protokoll, falsch/zu kurz/einmalig/abgelaufen (ablauf
  zurückdatiert)/5× falsch, alte Sitzung beendet, altes Kennwort weg, neues gilt + Sperre aufgehoben, unbekanntes Konto), statisch 0/0, Browser-Ablauf in
  einer Wegwerf-Suite (8096) DE und EN: „Kennwort vergessen?“ → Code aus `cli kennwort-code` → neues Kennwort gesetzt → angemeldet; englische Fassung
  „Forgot password?/Reset password/Recovery code“ mit Fettdruck im Absatz. NOCH ZU TUN: beide installierten Suiten auf 0.4.8 aktualisieren (DB stellt beim
  ersten Aufruf auf Schema 12 um; `_vor-datenbank-12` wird angelegt), danach ggf. auf <server> den Rücksetzcode für Peters Konto erzeugen (Code sieht nur
  Peter, kein Mitschnitt, nicht auslesen). api_test der Fassung-10-Umstellung erwartet jetzt Schema 12 (`_vor-datenbank-12`, user_version 12).
  ZWEISPRACHIGE EINTRÄGE IN WIKI UND LÖSUNGSDATENBANK, TASKLEISTE DE|EN (21.10.2026, SUITE_VERSION '0.4.5 (Stufe 4)', DB_SCHEMA 11, LS_SCHEMA 2,
  ITSM-Format 4, Taskleiste 1.4.0; Peters Wortlaut über die Sitzung „8EM-Modell Tool“: „Bei WIKI und LösungsDB sind die Einträge auch auf englisch anzubieten,
  wobei diese beim Eintragen auch parallel in beiden Spracheinträgen gepflegt werden … ich trage was in der deutschen Einstellung ein, wird das nach Englisch
  übernommen und umgekehrt. Danach kann ggf. mit Hilfe der Editierfunktion und eines Übersetzungsprogramms die Übersetzung händisch durchgeführt werden“;
  Sicherungen editor-backups/2026-10-21_12-53-13_suite-zweisprachig, *_blog-zweisprachig-wiki-ldb-taskleiste). URSACHE der deutschen Module bei Peter: Die
  INSTALLIERTEN Suiten (PC 0.4.4, <server>) haben noch kein assets/sprache.js und ein älteres portal.js – der Projektstand war bis auf kleine Reste englisch.
  REGELN (je Seite bzw. Lösung zwei Fassungen, gleiche Logik in wiki.php, ls_folgen (loesungen.php) und kbSetzen (i9_loesungen.js)): neuer Eintrag = beide
  Fassungen gleich, die andere „folgt“ (Spalte/Feld folgt = Sprache der noch unveränderten Kopie); Speichern von L, wenn M folgt → M wird mitkopiert; Speichern
  der folgenden Fassung L → L wird eigenständig, ohne Kennzeichen; beide eigenständig → Optionen aktuell (eigenes Kennzeichen weg) und andere_pruefen (Standard an:
  M bekommt „⚑ Übersetzung prüfen“). Eine Übersetzung wird NIE überschrieben. Aktionen „✓ Ist aktuell“, „⧉ Aus der … Fassung übernehmen“ (L wird wieder Kopie),
  Vergleich (beide Fassungen nebeneinander + Zeilenvergleich ab dem Stand des letzten Abgleichs stand_de/stand_en = Versions-id der anderen Sprache). Versions-
  nummern (rev) zählen je Seite über BEIDE Sprachen (Konfliktprüfung). Gewählte Fassung: Feld „sprache“ der Anfrage, sonst „_sprache“ (schicken portal.js und
  anschluss.js bei JEDER Anfrage mit). SERVER Wiki (app/wiki.php neu geschrieben): wiki_seite + titel_en/inhalt_en/folgt/pruefen_de/pruefen_en/stand_de/stand_en,
  wiki_version.sprache, wiki_bereich.name_en/beschreibung_en (nur Anzeige), FTS wiki_fts mit 4 Spalten, englischer Titel je Bereich eindeutig; Verweise [[…]]
  lösen zuerst Titel der Sprache, dann der anderen; Umbenennen ändert Verweise in Kopien beider Sprachen; Startseite eines neuen Bereichs mit eigenem DE- und
  EN-Text (WIKI_START_TEXT, folgt ''); Export-ZIP mit Ordnern Deutsch/ und English/; neue Aktionen wiki_fassung (aktuell|pruefen|uebernehmen), wiki_vergleich.
  Lösungsdatenbank (app/loesungen.php): Spalten en (JSON der englischen Felder LS_SPR_FELDER), folgt, ueb_pruefen ('de,en'), kategorie.name_en; Suchwörter = Vereinigung
  beider Fassungen (ls_suchwoerter = kbFelderWoerter – mit ls_bewerten = kbBewerten IMMER gleich halten; seit 21.10.2026 enthält LS_STOPP auch englische
  Füllwörter = KB_STOPP_EN im ITSM-Werkzeug; seit 21.10.2026 nachmittags zusätzlich englische Wortformen – SUITE_VERSION '0.4.6 (Stufe 4)', LS_SCHEMA 3 baut die
  gespeicherten Wortlisten einmal neu, Einzelheiten beim ITSM-Werkzeug „ENGLISCHE WORTFORMEN“), Auszug aus der gewählten bzw. der anderen Fassung,
  passend/liste mit sprache, Filter „uebersetzen“; ältere Programme ohne en → ls_folgen rechnet die Regeln auf dem Server. UMSTELLUNG beim ersten Aufruf: vorher
  automatische Kopie db_vorher_sichern (kern.php) per VACUUM INTO nach <Daten>/_update-sicherung/<Zeit>_vor-datenbank-11/suite.sqlite bzw.
  …_vor-loesungen-2/loesungen.sqlite (einmal je Fassung; scheitert sie, bleibt die Datenbank unverändert), danach bestehende Einträge = deutsch, englische
  Fassung = Kopie (folgt 'en'), je Seite eine Version „Englische Fassung angelegt“. OBERFLÄCHE: wiki.js (Auswahl „🌐 Sprachfassung“ DE/EN, folgt der Sprache der
  Oberfläche, ⚑ im Baum + Zähler, Hinweiskästen, „⇄ DE / EN“, Editor nennt die Fassung, „🌐 … Fassung daneben“ mit ⧉ Kopieren für Übersetzungsprogramme,
  Häkchen zur anderen Fassung, Versionen je Sprache/beide, Bereichsname auf Englisch; B/I statt F/K im Englischen); ITSM i9_loesungen.js (Auswahl in Leiste und
  Maske, kbSetzen, Vergleichsdialog, CSV mit Spalte Sprachfassung, Kategorienamen englisch, Fassung-3-Dateien → Format 4 mit englischer Kopie; Beispiel KB-0001/0002
  übersetzt, KB-0003 mit „Übersetzung prüfen“), i10_dokumente.js (Export je Sprache oder „Beide Sprachen in einem Dokument“). WEITERE ENGLISCH-KORREKTUREN dabei:
  portal.js P.datum auf Englisch DD/MM/YYYY; Modul-Neuzeichnen bei Sprachwechsel (Listener 'mc4y-sprache' → route(), nicht bei ungesicherten Änderungen,
  Modul-Option eigeneSprache für Wiki und Lösungsdatenbank); kalender.js Wochentage/„Week“ je Sprache; betrieb.php Platzhalter der Befehle je _sprache;
  handbuch-en.html Platzhalter englisch; einmal.php reicht &lang= weiter (#/oeffnen?…&lang= → Werkzeug mit ?lang=); Laufzeit sprache/sprache.js findet Texte
  mit Vorzeichen UND Schlusszeichen („⧉ … (übersetzt).“ – DEKO-Rückfall mit Schlusszeichen); Ausleseregel SQL-Filter erweitert (SUITE_AUSZUG 5); Reste in
  sprache/werkzeuge/mc4y-suite.json und itsm-editor.html.json „texte“. Übersetzungen sprache/texte/0086.json. TASKLEISTE 1.4.0 (mc4y_taskleiste.py): Sprache
  Deutsch | English, Einstellung „sprache“ in taskleiste.json ('' = wie das Betriebssystem: Windows GetUserDefaultUILanguage, macOS AppleLanguages, Linux
  LANGUAGE/LC_*/LANG – nur lesen, nichts starten), Umschalten im Griff-Menü „🌐 Sprache · Language“, in der Suche (DE | EN) und in den Einstellungen; T()/D()/S()
  mit Wörterbuch-Block „# >>> MC4Y-SPRACHE-EN >>>“ … „<<<“ im Programm (Prüfung `py sprache/taskleiste.py pruefen`), Katalogtexte der Suite übersetzt,
  Systemprogramme in der Sprache des Systems; öffnet Werkzeuge mit &lang=. LIESMICH.txt/README.txt nachgezogen (README in die Installer-Dateilisten aufgenommen).
  Prüfstand `py tests/taskleiste_sprache_test.py` (TL-S01 … S14). GEPRÜFT: api_test 628/628, statisch 0 Fehler, Taskleiste Sprache 29/29 · Bildschirm 18/18 ·
  Verbindung 35/35 · Selbsttest 7/7 · Oberfläche 52/52, Wegwerf-Suite (Port 8097, Einstellung Englisch) mit automatischem Rundgang über alle Module, Dialoge,
  Kalenderansichten, CMDB-Import aller Formate (erfundene Dateien) und Verwaltung – übrig nur Daten/Pfade; Wiki mit der Maus (neue Seite auf Englisch → deutsche
  Kopie, deutsch übersetzt → eigenständig, Englisch geändert → Deutsch „⚑ Übersetzung prüfen“, Vergleich, „Ist aktuell“, 375 px ohne Querscrollen);
  Lösungsdatenbank-Modul (neue Lösung auf Englisch → deutsche Fassung folgt). TASKLEISTE UND INSTALLER VOLLSTÄNDIG DE/EN (21.10.2026 abends, Peters „Warum nur
  auf Deutsch“ / „Du hattest doch schon den Auftrag …“, über die Sitzung „Stammdaten-Anschluss“; Taskleiste 1.4.1, Suite-Fassung unverändert 0.4.6; Sicherung
  editor-backups/2026-10-21_*_taskleiste-installer-englisch, dort auch die alten Installations- und Startskripte als zip-original__*): (1) Kommandozeile der
  Taskleiste: main() → sprache_aufruf(argv) – `--sprache de|en` bzw. `--lang de|en` (aus argv entfernt), sonst sprache_von(Konfig()) (nur lesen); alle print()
  über T()/TF() (Selbsttest-Prüfnamen, „FEHL“ → „FAIL“, --fassung „· running: no“, Autostart-Hinweis); `--help` auf Englisch = Konstante HILFE_EN (sprache/
  taskleiste.py übergeht sie bei der Prüfung deutsch aussehender Texte); Übersetzungen in sprache/taskleiste-en.json. (2) Installationsskripte der Taskleiste
  (installieren/deinstallieren-windows.ps1 `-Sprache de|en`, -macos.command/-linux.sh `--lang de|en`): Sprache = Parameter, sonst feste Wahl „sprache“ in
  taskleiste.json, sonst Windows-Anzeigesprache bzw. AppleLanguages/LANG; Funktion `L` (Windows: Parameter $deutsch/$englisch und $script:EN – ein Parameter
  $en hätte $EN überdeckt, PowerShell unterscheidet keine Groß-/Kleinschreibung) bzw. `m` (Shell); .desktop-Eintrag mit Comment + Comment[de]; macOS/Linux
  rufen `--autostart an --lang …`. (3) Grafischer Installer: Startskripte Installer-starten.bat (reicht %* an den Helfer), installer-starten.sh/.command
  zweisprachig (Sprache des Systems bzw. --lang; der Mac-Hinweis nennt jetzt `bash install/macos/installieren.command` ohne sudo wie die Anleitungen),
  Konsolen beider Helfer (installer-dienst.ps1 `-Sprache`, installer_dienst.py `--lang`, konsole_englisch(); Python-Ausgabe übersteht Terminals ohne UTF-8).
  Meldungen der Helfer an die Oberfläche bleiben deutsch (PS nur ASCII) – installer.html formuliert sie in HELFER_TEXT (genau bzw. RegExp mit {0}, eingesetzte
  Teile ebenfalls, 2 Ebenen), helferText/helferZeile auf Fehler (api), x.text/x.quelle (vorText), Plan-Hinweise/-Fehler, Meldung, Phasennamen, Ergebnis-Hinweise
  und jede Protokollzeile; Protokoll als je eine <span> pro Zeile (logHtml – sonst schluckte das Muster „Programm: {0}“ das ganze <pre> samt Umbrüchen);
  skripteDeutsch() = Hinweis über dem Protokoll nur auf Englisch (deutsch geschrieben, die Sprachwahl übersetzt ihn), neu zeichnen bei 'mc4y-sprache'.
  Startseite „Nur im Browser“ jetzt DE/EN wie mc4y-werkzeuge.zip: EINE Vorlage STARTSEITE_VORLAGE in installer.html (String.raw zwischen /*STARTSEITE-VORLAGE*/
  und /*STARTSEITE-VORLAGE-ENDE*/, darin <\/script>; in installer.html nie ein wörtliches „</script>“ schreiben – '<' + '/script>'), paket_bauen.py
  startseite_vorlage() liest sie von dort (Ergebnis gleich wie vorher geprüft). Suite-Wörterbuch: „Protokoll“ = „Log“ (texte in mc4y-suite.json; vorher
  „Minutes“ aus einem Werkzeug, auch im Portal-Reiter); Zeilen mit „[“ am Anfang übergeht der Auszug – solche in „texte“ eintragen. Prüfstand: installer_test.py
  IN-L01 (alle festen Helfer-Meldungen in HELFER_TEXT) … L06, taskleiste_bildschirm_test.py TL-B20. NICHT geprüft: Shell-Skripte auf echtem macOS/Linux.
  (Die Ausgaben der Installationsskripte der Suite waren hier noch bewusst deutsch – seit 21.10.2026 abends ebenfalls zweisprachig, siehe „Sprachen der Werkzeuge“
  › „ALLES ZWEISPRACHIG“.) Datenschutzhinweise der Suite (Wiki/Lösungen in zwei Fassungen, Sprache in den
  Anfragen, Taskleisten-Sprache), Blog-Entwürfe tool-mc4y-suite, tool-itsm, tool-mc4y-taskleiste (Entwurf + Testfassungen DE/EN) nachgezogen.
  INSTALLIERT 0.4.6 + TASKLEISTE 1.4.1 AUF PETERS PC (21.10.2026 18:02, Peters „1 - ja, aber erst nach Abschluss der noch laufenden Tasks“, über die Sitzung
  „8EM-Modell Tool“): installieren.ps1 -Aktualisieren -OhneBrowser -OhneWarten per Hilfsskript mit Transkript (Sitzungs-Scratchpad update046_pc.ps1), Orte
  unverändert auf C: (C:\Program Files\MC4Y-Suite, C:\PHP 8.5.11, C:\ProgramData\MC4Y-Suite-Daten). Sicherungen: _update-sicherung\2026-10-21_18-02-39_vor-0.4.6
  (suite.sqlite, cmdb\cmdb.sqlite, loesungen\loesungen.sqlite) und automatisch beim ersten Aufruf …\2026-10-21_18-02-46_vor-datenbank-11. Danach nur lesend
  geprüft: suite.sqlite user_version 11, quick_check ok, wiki_seite mit titel_en, 4 Benutzer; cmdb.sqlite 2; loesungen.sqlite noch 1 (0 Lösungen – stellt sich beim
  ersten Öffnen des Moduls mit eigener Sicherung …_vor-loesungen-3 auf 3 um); kein fehler.log, php.exe als mc4y-systemuser, Aufgaben MC4Y-Suite läuft,
  MC4Y-Suite-Sicherung bereit; tests/suite_installation_pruefen.py 0 Fehler/0 Warnungen (144 Programmdateien = Projekt). Taskleiste von 1.3.0 auf 1.4.1:
  installieren-windows.ps1 aus dem entpackten public/client/mc4y-taskleiste.zip (Scratchpad), voreinstellung.json übernommen, `--fassung` „läuft: 1.4.1“,
  keine Meldung von Bitdefender.
  INSTALLIERT 0.4.6 AUF <server> (21.10.2026 18:13, nach Peters ausdrücklichem „ja“ – der erste Versuch war von der Sicherheitsprüfung der Sitzung abgelehnt
  worden): auf-server-installieren.ps1 -Server <server> -Aktualisieren -OhneWarten -Benutzer <benutzer>@<domaene> im sichtbaren Fenster, Peter
  tippte das Kennwort (ein Lauf davor scheiterte mit 0x8009030e am vertippten Namen „hoenigüe“ – nichts geändert; seitdem -Benutzer vorbelegen). Programm E:\MC4Y-Suite,
  PHP E:\PHP 8.5.11, Daten E:\MC4Y-Suite-Daten, Sicherungen E:\MC4Y-Suite-Sicherungen unverändert, Zertifikat <zertifikat-fingerabdruck> weiter genutzt; Datenbank vorher gesichert
  nach E:\MC4Y-Suite-Daten\_update-sicherung\2026-10-21_18-12-47_vor-0.4.6 (die Umstellung auf Datenbank 11 legt beim ersten Aufruf zusätzlich …_vor-datenbank-11
  an – auf dem Server nicht nachgesehen, Datenordner nur für Administratoren). Protokolle Dokumente\MC4Y-Suite-Installationen\<server>_2026-10-21_18-06-19.
  Nur lesend geprüft: tests/suite_installation_pruefen.py --server 0 Fehler/0 Warnungen (0.4.6, https, 301, 403, keine Inhalte von app/konfig, .cer abrufbar),
  assets/sprache.js, handbuch-en.html, portal.js in Projektgröße ausgeliefert.
  ZWEITES UPDATE BEIDER SUITEN (21.10.2026 abends, Peters „ja, beide Suitern und dann deplyen auf test“ – Stand mit zweisprachigen Server-Meldungen und
  Installationsskripten, Suite-Fassung weiter 0.4.6): vorher statisch 0 Fehler, Probeläufe installieren.ps1 -Aktualisieren -Probelauf mit -Sprache de/en ohne Fehler.
  PC 20:18 (Hilfsskript update046b_pc.ps1, ohne -Sprache → Deutsch nach der Windows-Sprache; Orte auf C:): Sicherung _update-sicherung\2026-10-21_20-18-53_vor-0.4.6
  (diesmal mit leeren -wal/-shm von cmdb/loesungen – angelegt von der lesenden SQLite-Prüfung um 18:02 mit mode=ro; harmlos, der Datenordner vererbt Ändern-Rechte
  an mc4y-systemuser; künftig lesende Prüfungen ohne Öffnen der Datenbanken oder mit immutable=1), Datenbank 11/cmdb 2/loesungen 1, kein fehler.log,
  suite_installation_pruefen 0/0 (144 Dateien = Projekt). <server> 20:44 (-Benutzer <benutzer>@<domaene>, Peter tippte das Kennwort): Sicherung
  E:\MC4Y-Suite-Daten\_update-sicherung\2026-10-21_20-44-51_vor-0.4.6, Orte auf E: unverändert, --server 0/0, sprache.js/handbuch-en.html/portal.js/anschluss.js
  per SHA-256 = Projekt; Protokolle Dokumente\MC4Y-Suite-Installationen\<server>_2026-10-21_20-19-41. Ausgaben der neuen Skripte deutsch und korrekt
  ausgelesen (Fertig-Zeile, Ergebnis-JSON, „Sicherungen:“ über cli status --lang de). Englisch vom Server nur außerhalb der Oberfläche geprüft (kalender.php
  ?abo=…&lang=en → „Unknown subscription link.“ auf beiden; JSON der Schnittstelle bleibt absichtlich deutsch, die Oberfläche übersetzt). Taskleiste unverändert
  1.4.1 (installierte Dateien = Projekt), nicht neu installiert.
  DRITTES UPDATE BEIDER SUITEN (21.10.2026 22:25/22:28, Peters „alle ja“ nach dem vierten Volltest – Korrekturen an Sprach-Laufzeit, englischer Datumsanzeige,
  Übersetzungen; Suite weiter 0.4.6): vorher statisch 0 Fehler, PC wich in 52 Dateien ab. PC (update046c_pc.ps1, Orte auf C:): Sicherung
  _update-sicherung\2026-10-21_22-25-45_vor-0.4.6 (Rotation entfernte die älteste, …_vor-0.3.1), Datenbankprüfung jetzt mit mode=ro&immutable=1 und Dateivergleich
  vorher/nachher – „keine Dateien angelegt“; Datenbank 11/cmdb 2/loesungen 1, kein fehler.log, suite_installation_pruefen 0/0. <server> (-Benutzer vorbelegt):
  Sicherung E:\MC4Y-Suite-Daten\_update-sicherung\2026-10-21_22-28-32_vor-0.4.6, Orte auf E:, --server 0/0, sprache.js/handbuch-en.html/portal.js/anschluss.js/
  suite.css per SHA-256 = Projekt, kalender.php englisch; Protokolle …\<server>_2026-10-21_22-26-49. Taskleiste unverändert 1.4.1, nicht neu installiert.
  VIERTES UPDATE BEIDER SUITEN (21.10.2026 23:49/23:52, Peters „ja, ohne Rückmeldung …“ – englisches Zahlen-/Datumsformat in sprache.js, Werkzeuge neu
  gestempelt; Suite weiter 0.4.6, keine Schemaänderung): PC (update046d_pc.ps1) Sicherung …\_update-sicherung\2026-10-21_23-49-31_vor-0.4.6 (Rotation entfernte
  …_vor-0.3.2), Datenbanken unbeschädigt, Prüfung ohne neue Dateien, 0/0 (vorher 52 Dateien abweichend). <server> Sicherung E:\MC4Y-Suite-Daten\_update-sicherung\
  2026-10-21_23-51-34_vor-0.4.6, Orte auf E:, --server 0/0, sprache.js/portal.js/anschluss.js/suite.css/handbuch-en.html/taschenrechner-editor.html = Projekt;
  Protokolle …\<server>_2026-10-21_23-49-53. Taskleiste unverändert 1.4.1.

## Mehrsprachigkeit der Website (seit 20.10.2026, Peters Auftrag „Sprachversionen nach Browser-Einstellung“; Website DE + EN)

Sicherung vor Beginn: editor-backups/2026-10-20_11-29-02_mehrsprachigkeit (generator/*.py+json, datenschutz, mc4y-editor.html,
CLAUDE.md, TESTPLAN.md, site.js/site.css/.htaccess/kontakt-senden.php beider Fassungen).
ENTSCHEIDUNGEN (mit Begründung): (1) Gleiche Adressen je Sprache unter /<code>/ (website/en/kontakt.html) – eindeutige Zuordnung
de ↔ en ohne Übersetzungstabelle, hreflang/Sitemap/Umschalter/Editor rechnen nur das Präfix, keine 301 nötig; englische Adressen
mit deutschen Wörtern sind für Suchmaschinen unschädlich. (2) Übersetzungen als NEUE Dateien neben Peters Inhalten (inhalte/en,
seiten/en, blog/en, menue.en.json, downloads.en.json) im gleichen Format wie die deutschen – nichts Deutsches wird überschrieben,
Test-Überlagerung (generator/test/…/en/) und „Nach Live übernehmen“ funktionieren ohne Sonderlogik. (3) Inhalte jeder Sprache
werden in deutscher Link-Schreibweise geschrieben (ab Website-Wurzel, z. B. „kontakt.html“); der Generator zeigt Links auf die
Seite der Sprache, sonst auf die deutsche (core.sprach_links; hreflang="de" am Link = bewusst deutsche Fassung; Gegenstück
links_zurueck für die Rettung direkter Änderungen, im Editor sprachLinks/sprachLinksZurueck – identische Ergebnisse geprüft).
(4) Fehlt eine Übersetzung (oder steht sie auf status „entwurf“), gibt es die Seite in der Sprache nicht. (5) Automatische Wahl
per .htaccess (302 nach Accept-Language) statt JavaScript/Cookie: funktioniert ohne Skript, speichert nichts, Suchmaschinen sehen
beide Fassungen; feste Seitenliste in den Regeln statt Dateiprüfung auf dem Server (unabhängig von DOCUMENT_ROOT bei STRATO);
Rückweg über den Umschalter dank Referer der eigenen Domain, Lesezeichen auf Deutsch mit ?lang=de. (6) Kategorie bleibt im Blog
der deutsche Schlüssel, angezeigt wird die Übersetzung aus sprachtexte. (7) Kommentare: übersetzter Beitrag = gleiche Adresse →
gleiche Kommentare. (8) Britisches Englisch; Fußzeile der englischen Seiten nennt die KI-Übersetzung (Claude) und „im Zweifel
gilt die deutsche Fassung“ (Peter kann den Satz in sprachtexte/en.json „uebersetzung_hinweis“ ändern/leeren).
FERTIG: generator/sprachen.json (de, en gebaut; it/es/pt/fr/nl vorbereitet, „website“: false; ersatzsprache en),
generator/sprache.py, generator/sprachtexte/{de,en,it,es,pt,fr,nl}.json (Textbausteine, Programmdateien – ins Update-Zip);
core.py (Menü je Sprache menue.<code>.json, Downloads-Anzeigenamen downloads.<code>.json, Kopf/Fuß/Kontaktband/Brotkrumen/
Strukturdaten je Sprache, Sprachumschalter .lang-switch, hreflang + x-default, og:locale, SPRACHSEITEN, stand_einsetzen mit
„Last updated:“ usw.), pages_blog/pages_custom/pages_werkzeuge (code-Parameter; Werkzeuge mit ?lang=<code>), build.py
(sprachfassungen, Brotkrumen aus Überschriften, seitenstand für en/…, Rettung inhalte/en mit de_stand, Sitemap mit
xhtml:link, <code>/llms.txt, en/blog/feed.xml, .htaccess: 302-Sprachwahl nach Accept-Language mit fester Seitenliste – nur
GET/HEAD, ohne Referer der eigenen Domain, ohne ?lang=de, keine Bots –, Vary: Accept-Language, ErrorDocument /en/404.html
in <If>, /en/index.html → /en/; Hinweise „deutsche Fassung neuer“ und „ohne Übersetzung“). site.js (Texte je <html lang>,
Umschalter in fit(), Werkzeug-Rahmen behält ?lang), site.css (Umschalter auch mobil, .lang-note), website/kontakt-senden.php
(Feld sprache → /en/danke.html, englische Themen). Übersetzungen (Claude, 20.10.2026; je JSON „de_stand“): generator/inhalte/en/
(33 Standardseiten inkl. Rechtstexte mit Hinweis „deutsche Fassung maßgeblich“), generator/seiten/en/ (12), generator/blog/en/
(alle 48 veröffentlichten Beiträge), menue.en.json, downloads.en.json; Kontaktband der Übersetzungen per Skript vereinheitlicht
(= core.contact_band(code="en")). Übersetzungsrichtlinie (Begriffe wie „8-Level Model (8EM)“, Facilitation, systemic board,
Dreamer – Realist – Critic, Datumsform „17 September 2026“): Notizordner der Sitzungsgruppe mehrsprachigkeit-uebersetzungsrichtlinie.md –
bei neuen Übersetzungen einhalten. TESTFASSUNG (20.10.2026 abends, Peters „Du hast noch nicht alle Blog-Einträge übersetzt“): alle 23 nur
im Test veröffentlichten bzw. dort geänderten Beiträge haben eine englische Testfassung generator/test/blog/en/<datei>.md (Übersetzung der
DEUTSCHEN TESTFASSUNG, de_stand = deren aktualisiert; _teststand.json basis per core.teststand_basis, Mindmap mit basis der Live-Übersetzung;
Sicherung editor-backups/2026-10-20_22-33-06_blog-en-testfassung). „Nach Live übernehmen“ nimmt sie mit dem deutschen Beitrag mit. Der
einzige Live-Entwurf ohne Testfassung (tool-ressourcengarten) hat seit 20.10.2026 eine englische Fassung generator/blog/en/… mit status
entwurf (Peters Wunsch). Auf Peters „ja“ in den deutschen UND englischen Testfassungen korrigiert (Sicherung editor-backups/
2026-10-20_22-43-24_blog-korrekturen-test): macOS-Anleitungen HTML-PY-Editor und Taskleiste (Terminal: ditto, xattr, bash statt
„Rechtsklick › Öffnen“), Suite (CMDB ↔ 8EM nicht mehr „auf der Liste“), Stammdaten (Feldhilfe nur in Personen-/Kontaktfeldern), Kunden
(„das Sie beraten“), 8EM (ITIL® = PeopleCert-Gruppe). Mindmap-Editor liegt im Test unter ressourcen/software: Testfassungen
seiten/mindmap-tool.html (+ en), downloads.json (Umzug → 301) und downloads.en.json (Anzeigename) wie beim Verschieben im Editor.
„DE NEUER“ NUR BEI TEXTÄNDERUNG (seit 20.10.2026 abends, Peters „ja“; Sicherung editor-backups/2026-10-20_23-00-19_de-neuer-nur-text):
vorher zählte der Vergleich mit de_stand das Seitendatum aus seitenstand.json, in das auch die vom Generator eingesetzten Teile eingehen
(Blog-Vorschau der Startseite, Download-Listen) – jeder neue Beitrag/Download machte deutsche Seiten „neuer“. Jetzt führt build.seitenstand
je Seite zusätzlich text (core.text_kennung = inhalt_kennung ohne <section aria-labelledby="h-blog"> und mit collapse_downloads) und
textdatum (gleicher Text → altes textdatum, auch wenn der Editor „aktualisiert“ neu setzt; geänderter Text → Angabe bzw. heute; erster
Lauf = datum). sprach_hinweise (stand_de) und der Editor (Übersicht, „DE neuer“, „Aus der deutschen Fassung anlegen“ → de_stand) nehmen
textdatum, sonst datum. „Stand: …“, Sitemap und JSON-LD nutzen weiter datum. Vorher einmal de_stand von index, experimentell-zum-
ausprobieren, ressourcen, mindmap-tool auf 2026-10-20 nachgezogen (Text seit der Übersetzung unverändert). Website-Editor zählt jede *.html in der Wurzel
von website-test als Seite: Nextcloud-Konfliktkopie und alte Werkzeugkopie soziales-atom-editor.html (26.09.) lagen dort und erschienen als
„ohne Übersetzung“ – nach editor-backups/2026-10-20_22-56-24_website-test-reste-und-de-stand verschoben (nicht gelöscht). Stand danach:
Seiten 44/44, Blog Test 70/70 und Live 48/48, nichts „DE neuer“.
UPLOAD TEST 20.10.2026 23:14 (Peters „Lade den Teststand hoch“ + „Werkzeuge kopieren, dann hochladen“): vorher nach website-test/downloads/
experimentell/software kopiert (nur Test, nicht Live): bodenanker, itsm, kanban, kunden, projektauswertung, projektmanagement, stammdaten,
taschenrechner, uml, vertrieb (je *-editor.html) sowie freehtml-editor.html und html-py-editor.zip aus ../freehtml-editor; Anzeigenamen dafür
in generator/test/downloads.json und downloads.en.json (Sicherung editor-backups/2026-10-20_23-10-59_test-downloads-werkzeuge). Danach
Linkprüfung Test 0 fehlende Ziele. Hochgeladen über den SFTP-Helfer (selbst gestartet, danach beendet; gespeicherter Zugang, kein Passwort
eingegeben): 420 Dateien, 0 Fehler, nichts gelöscht (Protokoll sftp/protokolle/2026-10-20_23-14-06_test_hoch.txt). Online geprüft:
/en/, /en/blog/, englische Atom-/Tool-Beiträge, Werkzeug-Rahmenseite mit ?lang=en (Kunden-Navigator englisch), 301 alter Mindmap-Pfad.
UPLOAD TEST 21.10.2026 18:01 (Peters Freigabe über die Sitzung „8EM-Modell Tool“: „… update die Testumgebung und instaliere sie auf <test-domain>“):
vorher freehtml-editor.html und html-py-editor.zip (Build 16:12, mit „Übersetzung überprüfen“) nach website-test/downloads/experimentell/software
(nur Test; Sicherung der alten Kopien editor-backups/2026-10-21_*_test-downloads-freehtml), statische Prüfung 0/0, Testbau zweimal, Website-Prüfung
0 Fehler (Test 0 fehlende Ziele). Hochgeladen über den bereits laufenden SFTP-Helfer (seit 10:41, nicht beendet; gespeicherter Zugang, keine Rückfrage):
405 Dateien (55,7 MB), 0 Fehler, nichts gelöscht – Protokoll sftp/protokolle/2026-10-21_18-01-43_test_hoch.txt. Online geprüft: Start DE/EN (Bauzeit
18:00), englischer Blogbeitrag (Taskleiste), en/werkzeuge/kanban-editor.html mit ?lang=en, FreeHTML-/HTML-PY-Download byte-gleich, Blogbild
„Suite installieren“ DE und EN byte-gleich und in den Beiträgen eingebunden.
UPLOAD TEST 21.10.2026 (2) 20:05 (Peters „ja, danach auf die Testdomain hochladen“, über die Sitzung „8EM-Modell Tool“, nach Abschluss der Sitzungen
Website/Blog, Werkzeuge DE/EN – Stempel 19:44, Suite-Pakete 19:45 – und FreeHTML – Build 20:03): freehtml-editor.html und html-py-editor.zip nach
website-test/downloads/experimentell/software (Sicherung der alten Kopien editor-backups/2026-10-21_20-04-44_test-downloads-freehtml), statisch 0/0,
Website-Prüfung zweimal Live/Test 0 Fehler (Test 0 fehlende Ziele). Hochgeladen über den laufenden SFTP-Helfer (gespeicherter Zugang, keine Rückfrage;
Skript scratchpad test_hochladen.py: Aktion plan mit nurGeaendert, loeschen false, bauen false, dann start/status): 398 Dateien (55,4 MB), 0 Fehler, nichts
gelöscht – Protokoll sftp/protokolle/2026-10-21_20-05-56_test_hoch.txt. Online geprüft: Start DE (Bauzeit 20:05)/EN mit Umschalter, Blogbeiträge „Suite
installieren“ EN (`--lang en`, „Done. Address“) und DE (`--lang de`, „Fertig. Adresse“), HTML-PY EN („Done.“), en/werkzeuge/kanban-editor.html?lang=en
(Website-Umschalter, iframe mit ?lang=en, Werkzeug mit DE|EN-Knopf), Downloads freehtml-editor.html, html-py-editor.zip, mc4y-suite.zip, mc4y-werkzeuge.zip,
kanban-editor.html byte-gleich. Der eingebaute Browser durfte die Testdomain nicht öffnen – Prüfung über HTTP.
UPLOAD TEST 21.10.2026 (3) 22:26 (Peters „alle ja“ über die Sitzung „8EM-Modell Tool“ auf „Testfassung neu hochladen?“ und „Website-Editor im
Test-Download-Ordner austauschen?“; Anlass: vierter Volltest der Testplan-Sitzung mit neuem Stempel/Bau): website-test/downloads/experimentell/software/
mc4y-editor.html (Peters alte, nur deutsche Kopie vom 08.10., 206 202 Byte) durch die aktuelle zweisprachige Fassung aus der Projektwurzel ersetzt (427 840 Byte;
Sicherung editor-backups/2026-10-21_22-25-05_test-download-mc4y-editor; website/downloads unberührt – dort liegt keine Kopie); FreeHTML-Build in website-test war
schon aktuell (22:00, Testplan-Sitzung). Statisch 0/0, Website-Prüfung zweimal 0 Fehler (Test 0 fehlende Ziele). Hochgeladen wie (2): 397 Dateien (56,0 MB),
0 Fehler, nichts gelöscht – Protokoll sftp/protokolle/2026-10-21_22-26-11_test_hoch.txt. Online: mc4y-editor.html, mc4y-suite.zip, freehtml-editor.html
byte-gleich; Kanban-, Kunden- und ITSM-Werkzeug per Edge headless (--dump-dom, ?lang=en#beispiel) englisch mit Daten TT/MM/JJJJ. FUND (an „8EM-Modell Tool“
gemeldet, nicht behoben – Bereich Werkzeuge): ITSM-Übersicht zeigt auf Englisch geplante Changes im deutschen Kurzformat „26.10. 18:00“.
UPLOAD TEST 21.10.2026 (4) 23:49 + 23:52 (Peters „ja, ohne Rückmeldung auf <test-domain> hochladen“ über die Sitzung „8EM-Modell Tool“; Anlass: Datums-
und Zahlenformate der englischen Anzeige, Werkzeuge neu gestempelt): FreeHTML-Build 23:44 und mc4y-editor.html (neue Fassung 23:33, 438 061 Byte) nach
website-test/downloads/experimentell/software (Sicherung editor-backups/2026-10-21_23-48-33_test-downloads-upload4), statisch 0/0, Website-Prüfung 0 Fehler,
Upload 397 Dateien (56,9 MB), 0 Fehler – Protokoll sftp/protokolle/2026-10-21_23-49-28_test_hoch.txt. Danach FreeHTML-Build 23:50 (endgültige Sprach-
Laufzeit, Kommazahlen der deutschen Kontrastanzeige; Sicherung editor-backups/2026-10-21_23-51-46_test-downloads-freehtml-2350), erneut geprüft (0/0, 0 Fehler)
und hochgeladen: 342 Dateien (10,0 MB), 0 Fehler, nichts gelöscht – Protokoll sftp/protokolle/2026-10-21_23-52-29_test_hoch.txt. Online: Bauzeit 23:52;
mc4y-editor.html, freehtml-editor.html (587 277), html-py-editor.zip (239 081), mc4y-suite.zip, kunden-editor.html byte-gleich; per Edge headless (Skript
scratchpad online_formate.ps1) Kunden-, Vertriebs-Navigator, Kanban, ITSM auf Englisch: Daten TT/MM/JJJJ bzw. TT/MM (ITSM-Kurzformat jetzt „26/10“), Beträge
„€6,312.95“, keine deutschen Formate mehr.
UPLOAD TEST 22.10.2026 11:35 (Peters „ja, ohne Rückmeldung auf <test-domain> hochladen“ über die Sitzung „8EM-Modell Tool“; Anlass: Mac-Programmsymbole,
Taskleiste 1.4.2, Anleitungen und Blog „Suite installieren“/„Taskleiste“ der Stufe-4-Sitzung, Pakete von ihr um 11:30 neu gebaut): statisch 0/0, Website-Prüfung
zweimal 0 Fehler (Test 0 fehlende Ziele). Der SFTP-Helfer lief nicht mehr (Prozess vom 21.10. beendet, .sftp-helfer.json veraltet) – selbst mit der echten
python.exe (pythoncore-3.14-64, paramiko vorhanden, gespeicherter Zugang in %APPDATA%\MC4Y) gestartet, nach dem Upload beendet und die Tokendatei entfernt.
Hochgeladen wie (4): 346 Dateien (34,1 MB), 0 Fehler, nichts gelöscht – Protokoll sftp/protokolle/2026-10-22_11-35-29_test_hoch.txt. Online: mc4y-suite.zip
(14 472 253), mc4y-taskleiste.zip (1 592 454, mit mc4y-taskleiste.icns), mc4y-werkzeuge.zip byte-gleich; Blogbeitrag „Suite installieren“ DE mit „So starten Sie
die Suite und die Taskleiste später“ (Bauzeit 11:34) und EN mit „How to start the suite and the taskbar later“.
UPLOAD TEST 23.10.2026 21:20 (Phase 5a der Pipeline über „8EM-Modell Tool“, nach dem fünften Volltest und der Blog-Prüfung): Suite-Kette frisch
(`sprache.py suite` 3587 Texte, `mc4y-suite/bauen.py` 50 Werkzeuge + 7 Module, `paket_bauen.py` → mc4y-suite.zip 0.4.8, mc4y-taskleiste.zip,
mc4y-werkzeuge.zip, mc4y-erkennung.zip, mc4y-cmdb-homeassistant.zip); FreeHTML-Build 20:43 war aktuell (neuer als alle Quellen, Kopien = Build).
Statisch 0 Fehler/0 Warnungen, Website-Prüfung zweimal 0 Fehler (Test 0 fehlende Ziele). SFTP-Helfer selbst gestartet (echte python.exe), Plan ohne
Rückfrage und ohne Löschungen, 402 Dateien (62,6 MB), 0 Fehler – Protokoll sftp/protokolle/2026-10-23_21-20-32_test_hoch.txt; Helfer danach beendet,
Tokendatei entfernt. Online byte-gleich (scratchpad online_pruefen.py): die sieben Pakete inkl. freehtml-editor.html/html-py-editor.zip, Blog
crawler-agents DE/EN, suite-installieren DE, mc4y-suite EN, en/werkzeuge/kanban-editor.html, Start DE/EN (Bauzeit 21:19), kanban-editor.html?lang=en mit
Sprachwahl. Installierte Suiten (PC, <server>) NICHT angefasst.
UPLOAD TEST 24.10.2026 14:08 (Phase „alles auf die Testdomäne“ über „8EM-Modell Tool“, nach der CMDB-Erweiterung; Pakete hatte die Suite-Sitzung um 13:07
gebaut): vorher geprüft, dass mc4y-suite.zip die Wurzel MC4Y-Suite-0.4.9 und SUITE_VERSION „0.4.9 (Stufe 4)“ hat (kern.php = Projekt). Statisch 0/0, Testbau
(342 Seiten, 71 von 72 Beiträgen, keine defekten Links), Website-Prüfung zweimal 0 Fehler (Test 0 fehlende Ziele). SFTP-Helfer selbst gestartet, Plan ohne
Rückfrage und ohne Löschungen, 347 Dateien (32,9 MB), 0 Fehler – Protokoll sftp/protokolle/2026-10-24_14-08-11_test_hoch.txt; Helfer beendet, Tokendatei
entfernt. Online byte-gleich (scratchpad online_pruefen2.py): sieben Pakete (Suite 0.4.9 auch im Online-Zip), Blog tool-cmdb DE/EN, Start DE/EN (Bauzeit 14:07),
en/werkzeuge/kanban-editor.html, werkzeuge/itsm-editor.html, kanban-editor.html?lang=en mit Sprachwahl. Installierte Suiten NICHT angefasst.
NACHTRAG 17:53: Die CMDB-Sitzung hatte danach einen Migrationsfehler behoben und mc4y-suite.zip neu gebaut (17:49, weiter 0.4.9, app/ und public/assets/
= Projekt). Abgleich „nur geänderte“: 1 Datei (mc4y-suite.zip, 14 793 135 Byte), 0 Fehler, nichts gelöscht – Protokoll sftp/protokolle/2026-10-24_17-53-16_test_hoch.txt;
online byte-gleich (SHA-256 409c1d43…), übrige Pakete unverändert gleich; Helfer beendet, Tokendatei entfernt.
UPLOAD TEST 24.10.2026 20:35 (Peters Freigabe über „8EM-Modell Tool“: Suite 0.4.10 auf die Testdomain; Pakete von der Suite-Sitzung gebaut): vorher geprüft
mc4y-suite.zip 14 794 212 Byte, Wurzel MC4Y-Suite-0.4.10, SUITE_VERSION „0.4.10 (Stufe 4)“, app/ und public/assets/ = Projekt. Statisch 0/0, Testbau 342 Seiten
ohne defekte Links, Website-Prüfung zweimal 0 Fehler (Test 0 fehlende Ziele). Upload „nur geänderte“, keine Löschungen: 345 Dateien (34,4 MB, darunter mc4y-suite.zip,
mc4y-taskleiste.zip, mc4y-werkzeuge.zip), 0 Fehler – Protokoll sftp/protokolle/2026-10-24_20-35-42_test_hoch.txt; Helfer selbst gestartet/beendet, Tokendatei
entfernt. Online byte-gleich: alle sieben Pakete (mc4y-suite.zip SHA-256 ab320c48f8de91f0…5894), Blog tool-mc4y-suite DE/EN, tool-cmdb, Start DE/EN,
Werkzeug-Rahmenseiten, kanban-editor.html?lang=en. Installierte Suiten NICHT angefasst.
CMDB-ERWEITERUNG + FASSUNG 0.4.9 (24.10.2026, SUITE_VERSION '0.4.9 (Stufe 4)', CMDB_SCHEMA 7; Haupt-DB DB_SCHEMA bleibt 12): installierte Software ist ein eigenes
CI je Produkt + Major-Release (ci_installation verknüpft Software-CI ↔ Gerät; Maske am Software-CI, „💻 Installiert auf n Geräten (m melden aktuell)“); Gruppen/Suiten
als eigenes CI (Kategorie „Gruppe / Suite“, Klassifizierung abstract, Mitgliedschaft n:m über ci_gruppe, von Hand beidseitig + automatisch über gruppe_regel – Produkt→Suite,
vorbelegt Office/Microsoft 365/LibreOffice/Adobe; manuelle Zuordnung hat Vorrang); Kostenmodul (anschaffungskosten + Lizenz/Wartung) bei JEDEM CI; Wochenlauf
`cli.php cmdb-aufraeumen` entfernt Installationen stiller Geräte (cmdb_einstellung geraet_still_tage, Standard 30) – als OS-Aufgabe in allen Installern (Windows
„MC4Y-Suite-CMDB-Aufraeumen“ sonntags 03:30, Linux systemd-Timer/cron, macOS launchd; Deinstaller entfernen sie) UND beim Öffnen der CMDB höchstens wöchentlich automatisch.
Nur CMDB-Dateien geändert (app/cmdb.php, erkennung.php, start.php, cli.php, public/assets/module/cmdb.js, suite.css, Installer); ITSM/8EM unberührt. api_test 707/0, statisch 0/0.
MIGRATIONSFEHLER BEHOBEN (24.10.2026): Die Software-CI-Migration (Schema 6) rief cmdb_gruppe_auto → `ci_gruppe`, bevor Schema 7 die Tabelle anlegt → auf echten Vor-7-Daten
mit „Software als Bestandteil“ brach die Migration ab („no such table: ci_gruppe“, jeder CMDB-Aufruf „Interner Fehler“). Fix: cmdb_gruppe_auto ist No-op, solange ci_gruppe
fehlt; Schema 7 ruft nach dem Anlegen der Tabellen + Regeln einmal cmdb_regeln_anwenden. Regressionsfall im api_test (Vor-7-DB ohne Schema-7-Tabellen). Reproduziert und
behoben; Paket 0.4.9 mit Fix neu gebaut (Testdomain-Upload 17:53, s. o.).
INSTALLIERT 0.4.9 AUF <server> (24.10.2026, von Peter selbst „per Update von der Testdomain“ = heruntergeladenes mc4y-suite.zip 0.4.9): nur lesend geprüft
(`py tests/suite_installation_pruefen.py --server https://<server>`): Version 0.4.9 (Stufe 4) = Projekt, https/301/403/404, Zertifikat abrufbar – 0 Fehler/0 Warnungen.
Die read-only-Prüfung kann die gefixte app/cmdb.php NICHT per Hash bestätigen (app/ wird nicht ausgeliefert); ob der Migrationsfehler wirklich weg ist, bestätigt nur Peter durch
einmaliges Öffnen der CMDB – FUNKTIONALE CMDB-BESTÄTIGUNG AUSSTEHEND. Falls Peter das Paket VOR dem 17:53-Upload geladen hätte, wäre noch die fehlerhafte Fassung installiert –
dann erneut aktualisieren. PC vermutlich unverändert (Peter hat ihn nicht erwähnt); Desktop-Taskleiste bewusst nicht aktualisiert. Installiert/aktualisiert nur durch Peter
(Production-Deploy-Sperre dieser Sitzung); keine installierte Suite von mir angefasst.
Neu im Test veröffentlichte Beiträge: englische
Testfassung im selben Ordner anlegen (Editor „Sprache: Englisch“ im Test-Stand bzw. Datei + teststand_basis). Beispielfragen der 8EM-
Fragefunktion stehen im Englischen deutsch mit Übersetzung (der Modeller versteht Fragen nur auf Deutsch).
EDITOR (mc4y-editor.html): Auswahl „Sprache“ in der Kopfzeile (S.lang, localStorage 'mc4y-editor-sprache', grüne Markierung bei
Übersetzungen). inh…-Funktionen nehmen den deutschen Pfad + optional lang und rechnen per sprachRel/sprachOrdner um (idempotent).
Bei einer anderen Sprache: Liste mit Übersetzungsstand (Badges „Übersetzung fehlt“, „DE neuer“ = seitenstand.json-Datum der
deutschen Seite bzw. aktualisiert/datum des deutschen Beitrags > de_stand, „Entwurf“), Zusammenfassung oben; Klick auf fehlende
Übersetzung → „Aus der deutschen Fassung anlegen“ (Kopie mit status „entwurf“, de_stand, quelle); Formular mit Status (Seiten) und
Häkchen „entspricht der deutschen Fassung vom …“ (setzt de_stand), „Deutsche Fassung ansehen“, „Übersetzung löschen“ (nur die
Sprache); Kopieren/Umbenennen nur im Deutschen (Umbenennen zieht Übersetzungen mit, verweiseAendern/dlVerweiseAendern ändern
alle Sprachen); Sofort-Patch nach WEBL()/… mit sprachLinks, Download-Listen mit Anzeigenamen der Sprache (dlTitelInfo), Menü-Reiter
bearbeitet menue.<code>.json (ohne Datei: deutsches Menü als Vorlage), Reiter „Dateien“ bearbeitet Anzeigenamen in
downloads.<code>.json (dlBearbeitet; Umzüge bleiben in downloads.json, dlVerschieben nimmt Anzeigenamen aller Sprachen mit),
„Nach Live übernehmen“ listet die Unterordner der Sprachen. Generator überspringt Übersetzungen mit "status": "entwurf"
(inhalte/<code>/*.json, seiten/<code>/*.json; Blog wie gehabt über status).
PARALLELE SPRACHFASSUNGEN (21.10.2026, Peters Auftrag über die Sitzung „8EM-Modell Tool“: „… wird das nach Englisch übernommen und umgekehrt.
Danach kann ggf. … die Übersetzung händisch durchgeführt werden. Mach das analog … bei meinem Editor, wenn neue Seiten, Menüpunkte oder Blog-
Einträge angelegt werden“; Sicherung editor-backups/2026-10-21_12-53-06_parallel-sprachen; WIKI/Lösungsdatenbank und FreeHTML machen andere
Sitzungen). Block „Parallele Sprachfassungen“ in mc4y-editor.html (webSprachen/andereSprachen = Ausgangssprache + sprachen.json „website“: true).
NEU ANLEGEN in einer Sprache (Seite, Beitrag, Menüpunkt, „Seite kopieren“) legt in jeder anderen Website-Sprache sofort eine Fassung an
(parallelAnlegen; nur neue Dateien): gleicher Inhalt, Kennzeichen uebersetzung "offen", quelle_sprache, <quelle>_stand (de_stand bzw. en_stand),
kopie = Kennung des Inhalts (inhaltsKennung: cyrb53 über INHALT_FELDER + textInhalt = sichtbarer Text ohne <time>, href/src/alt, Marker).
Status der Kopien: immer „Entwurf“. ERSATZSEITEN (seit 21.10.2026 nachmittags, Peters Entscheidung „Ziehe die Editoren gleich und verwende
Übersetzung überprüfen“ + Wahl „Sofort sichtbar mit Hinweis“ = Verhalten des FreeHTML-Editors; Sicherung editor-backups/2026-10-21_16-01-57_
ersatzseiten-ueberpruefen): build.ersatzseiten() erzeugt für jede Seite (Standard- und eigene Seiten, nicht Blog/Werkzeuge), die es in einer
Website-Sprache nicht als veröffentlichte Übersetzung gibt (fehlt, Entwurf oder Kopie „offen“), wohl aber in einer anderen (Vorrang Ausgangssprache,
dann Ersatzsprache), eine Ersatzseite unter der Adresse dieser Sprache: Kopf/Menü/Fuß in der Sprache der Adresse, Inhalt der vorhandenen Fassung in
<div lang="…"> (Stand-Datum in dessen Sprache: inhalt_lang), Hinweisbalken (.ueb-offen, sprachtexte „uebersetzung_offen“, vor <main>, außerhalb
des Fingerabdrucks), noindex, ohne Sitemap, ohne hreflang-Gegenstück, nicht in der automatischen Sprachwahl und nicht in llms.txt (ueb_offen/ersatz →
core.OFFEN). Läuft NACH rescue_outside_changes (Ersatzseiten werden nie nach generator/inhalte übernommen). Ein halb übersetzter Entwurf erscheint nie –
dort steht der Text der Ausgangsfassung. Blogbeiträge: keine Ersatzseite, sie erscheinen erst übersetzt. Veröffentlichen einer noch unveränderten
Kopie nur nach Rückfrage (kopieVeroeffentlichen; „OK“ = Kennzeichen offen/kopie weg, normale Übersetzung; „Abbrechen“ = bleibt Entwurf). Die frühere
Auswahl „gleich veröffentlichen“ (localStorage 'mc4y-editor-kopien') ist entfallen. Englische Vorlagen für neue Seiten/Beiträge, wenn auf Englisch
angelegt. „Seite kopieren“ (nur in der Ausgangssprache, wie Umbenennen): gibt es eine Übersetzung der Originalseite, wird sie mitkopiert (Entwurf,
„Übersetzung überprüfen“), sonst Kopie „offen“. SPÄTERE ÄNDERUNGEN (parallelNachziehen nach jedem Speichern, nur Fassungen, deren quelle_sprache bzw.
Ausgangsfassung die gespeicherte Sprache ist – quelleDerFassung): unveränderte Kopie (Kennung passt) wird mitgezogen (bleibt Entwurf); bearbeitete
Kopie = Übersetzung begonnen (save entfernt „offen“ und kopie); übersetzte Fassung bekommt uebersetzung "pruefen" = Anzeige „Übersetzung überprüfen“
(kennzeichenSetzen, Text bleibt; der interne Wert heißt weiter "pruefen"). Häkchen „Übersetzung entspricht der … Fassung vom …“ + Speichern entfernt
es und setzt <quelle>_stand. Begriffe gleich im FreeHTML-Editor („Übersetzung offen“, „Übersetzung überprüfen“, englisch “Review translation”). Auf Englisch angelegte Seite: deutsche Kopie hat quelle_sprache "en"; die deutsche Adresse zeigt bis zur
Übersetzung die englische Fassung als Ersatzseite (core.de_ersatz bleibt als Rückfall für Links). MENÜ (menuAbgleich beim Speichern des Menü-Reiters;
Einträge bekommen beim Laden eine vorübergehende Kennung _k): Aufbau (Reihenfolge, Ziele, Knopf, Spalten) gilt für alle Sprachen mit eigener
Menüdatei, neue Einträge kommen mit dem Text dieser Sprache und „Übersetzung offen“ in die anderen Menüs, unveränderte „offen“-Texte folgen,
geänderte Texte unmarkierter Einträge machen die anderen „Übersetzung überprüfen“ (nicht, wenn hier gerade ein „offen/überprüfen“-Eintrag übersetzt
wurde), Einträge nur einer Sprache bleiben an ihrer Stelle; Ändern des Textes im Menü-Reiter entfernt das Kennzeichen (.mueb). „Offen“-Einträge
zeigen auf die Ersatzseite und sind sichtbar; core._menu_verborgen blendet nur noch Einträge ohne jede veröffentlichte Seite aus (Schutz vor toten
Links). LÖSCHEN: Ausgangsfassung löschen löscht abhängige
Fassungen mit (Rückfrage nennt sie), Übersetzung löschen nur diese; Menüeinträge verschwinden in allen Sprachen, wenn es die Seite nirgends mehr
gibt. TEST/LIVE: Kopien landen im gleichen Arbeitsstand (generator/test/…/en/ mit teststand_basis); „Nach Live übernehmen“ fasst eine Seite als
„(DE + EN)“ zusammen und übernimmt alle Sprachdateien. LISTE: Badges „Übersetzung offen“, „Übersetzung überprüfen“, Zusammenfassung je Sprache; build.py
sprach_hinweise meldet je Sprache „… Seiten übersetzt, noch nicht übersetzt: n“, die Ersatzseiten und Menüeinträge offen/überprüfen. Kopfzeile: nur die Sprachauswahl wird bei wenig Platz schmaler
(vorher überdeckte sie im Test-Stand bei 1024 px den Knopf „Live“).
GEPRÜFT 21.10.2026 im OPFS-Probeprojekt mit der Maus: neue deutsche Seite → englische Kopie + Menüeintrag; Kopie zieht mit; englischer Text →
Übersetzung begonnen; deutsche Änderung → „prüfen“; Häkchen entfernt es; neue Seite und neuer Beitrag in der englischen Ansicht → deutsche
Entwurfskopie; Menütext übersetzt (kein Fehlalarm in der anderen Sprache – dabei gefundener Fehler behoben); Kopieren mit vorhandener Übersetzung;
Löschen mit Kopien und Menüeinträgen; Test-Stand + „Nach Live übernehmen“. Nach dem Umbau auf Ersatzseiten (nachmittags): Dialog ohne Auswahl, Kopie
als Entwurf, Status „Entwurf (Website zeigt die deutsche Fassung)“, unveränderte Kopie veröffentlichen → Rückfrage („Abbrechen“ = Entwurf, „OK“ =
Übersetzung), deutsche Änderung → „Übersetzung überprüfen“ in Liste und Zusammenfassung. Generator in einer Wegwerf-Kopie (scratchpad
pruef_parallel.py/setup_parallel.py, 41 Prüfungen: Ersatzseiten für veröffentlichte Kopie, Entwurf mit halber Übersetzung, fehlende Übersetzung einer
Standardseite und englisch angelegte Seite; Hinweis, noindex, lang, hreflang, Sitemap, Menü, llms.txt, Sprachwahl, Blog); echte Builds je zweimal,
Website-Prüfung 0 Fehler/0 Warnungen (die echte Website hat derzeit keine Ersatzseiten – alle Seiten sind übersetzt).
GEPRÜFT 20.10.2026: Probebauten in Wegwerf-Kopie (alter und neuer Generator verglichen: deutsche Ausgabe nur um Umschalter, hreflang,
og:locale:alternate, inLanguage-Liste erweitert); echte Builds Live/Test je zweimal ohne Fehlalarm; `py tests/website_pruefen.py`
0 Fehler/0 Warnungen (englische Spiegel der fehlenden Werkzeug-Downloads in tests/bekannt.json); statische Prüfung ohne Fehler im
Generator/PHP/JSON/Blog (nur veraltete Stempel der Werkzeuge – Bereich der Werkzeug-Sitzung); im Vorschau-Browser: englische
Startseite Desktop/375 px, Umschalter, 67 interne Ziele 200, Rahmenseite mit ?lang=en und Parameter-Weitergabe, englischer
Blogbeitrag mit Sprungleiste „On this page“, deutsche Seite bei 1280 px (nav-tight); Editor im OPFS-Probeprojekt: Sprachwahl,
Übersetzungsstand, „Aus der deutschen Fassung anlegen“ mit der Maus, Speichern einer englischen Seite (Metadaten bleiben,
Rücklauf = Datei), Blog/Menü/eigene Seite auf Englisch, Wechsel zurück auf Deutsch. NICHT geprüft: Sprachwahl über .htaccess
auf echtem Apache/STRATO, Kontaktformular-Versand der englischen Seite.
BEDIENUNG für Peter: englische Texte im Editor unter „Sprache: Englisch“ ändern, dann wie gewohnt bauen; neue Seite/Beitrag/Menüpunkt
entsteht seit 21.10.2026 automatisch in beiden Sprachen (Kopie „Übersetzung offen“, Entwurf; die Website zeigt dort bis dahin den Originaltext
mit Hinweis) → in der anderen Sprache übersetzen, Status „Veröffentlicht“; ältere Seiten ohne Übersetzung über „Aus der deutschen Fassung anlegen“. Weitere Sprache
(z. B. Italienisch) auf der Website: in sprachen.json „website“: true setzen und Übersetzungen anlegen (Textbausteine für it/es/pt/fr/nl
liegen schon in sprachtexte/); Browser dieser Sprache bekommen dann ihre Fassung, sonst Englisch.
Prüfliste der Übersetzer (Begriffe, Tippfehler im Deutschen, z. B. „61 Bücher“ statt ~89, Neuigkeiten-datetime): Notizordner der
Sitzungsgruppe, mehrsprachigkeit-website-pruefhinweise.md. Blog-Entwurf 2026-10-20-website-englisch.md (Bild tool-website-englisch.jpg).
VOLLSTÄNDIG ZWEISPRACHIG (21.10.2026 abends, Peters „wenn ich sage zweisprachig alle Module, Tools und Blogeinträge, dann meine ich das auch
so“ über die Sitzung „8EM-Modell Tool“; Sicherung editor-backups/2026-10-21_18-08-33_zweisprachig-website):
• BLOG: jeder Beitrag hat eine englische Fassung im selben Status/Arbeitsstand – veröffentlichte (blog/en), Testfassungen (test/blog/en),
  Live-Entwürfe (22 neue generator/blog/en/*.md mit status entwurf, de_stand = Datum der deutschen Fassung, basis in _teststand.json); „DE neuer“ 0.
  Englische Beiträge zitieren Bildschirm- und Skripttexte ENGLISCH (Installationsskripte seit 21.10.2026 abends zweisprachig: Befehle in den englischen
  Anleitungen mit `--lang en`, Wortlaute wie mc4y-suite/INSTALL.txt bzw. README.txt der Systeme), deutsche Beiträge deutsch (Hinweis auf `--lang de`);
  Sicherung editor-backups/2026-10-21_19-42-52_blog-skripte-englisch (Suite installieren, Suite, Taskleiste, HTML-PY – je Live-Entwurf und Testfassung).
• KONSOLE: generator/sprache.py konsole()/k(de, en)/k_name(code) – Sprache aus MC4Y_SPRACHE (de|en), sonst Windows-Oberflächensprache
  (GetUserDefaultUILanguage, 0x07 = Deutsch) bzw. LANG/locale; nicht Deutsch → Englisch. build.py gibt ALLE Meldungen über K(de, en) aus
  (Abschlusszeile „… pages created (x of y blog posts published) … no broken internal links.“), pages_blog/core Fehlermeldungen ebenso.
  AUSWERTENDE PROGRAMME setzen MC4Y_SPRACHE=de (tests/website_pruefen.py) bzw. werten beide Sprachen aus (sftp_helfer.website_bauen:
  FEHLER beim Bauen/ERROR while building, trotzdem erzeugt/created anyway, ACHTUNG – Links/WARNING – links, ACHTUNG – diese Seiten/WARNING –
  these pages, Seiten erzeugt/pages created) – bei neuen Meldungen, die ausgewertet werden, beide Fassungen pflegen. Website-bauen*.command
  schließen je nach LANG deutsch/englisch; Website-bauen-TEST.bat ohne eigenes echo (build.py nennt den Upload-Hinweis selbst).
• SFTP-HELFER: Anfragen des Editors tragen `sprache` (de|en, aus MC4YSprache.lang); sftp_helfer.py: _SPRACHE (thread-lokal, je Anfrage in
  do_POST, je Übertragung am Anfang von auftrag_ausfuehren), T(de, en) für alle Meldungen an den Editor und das Protokoll, K(de, en) für das
  Fenster (Konsolensprache wie build.py), grund_text() übersetzt nur die Anzeige des Upload-Grunds (Schlüssel neu|geändert|alle bleiben, der
  Editor wertet sie aus). Fenstertitel „MC4Y SFTP-Helfer / SFTP helper“. Fehlermeldungen des Betriebssystems (WinError …) bleiben in dessen Sprache.
• MC4Y-EDITOR UND BESUCHER-AUSWERTUNG: Knopf DE | EN mit der Laufzeit der Werkzeuge (sprache/sprache.js, Schlüssel 'mc4y-sprache-ui').
  Eingesetzt von `py editor-sprache.py` (Projektwurzel; editoren-version.py schließt beide weiter aus) → Block MC4Y-SPRACHE nach <title>;
  `--offen` listet Texte ohne Übersetzung. Übersetzungen sprache/texte/website-werkzeuge.json (eigener Name), Einstellungen
  sprache/werkzeuge/mc4y-editor.html.json (ausnahmen .item .t = Seitentitel, #hl Quelltext, #lkPage/select.mlink/#f_menue Seitenlisten,
  #fontSelect; csv aus; texte = Sprachadjektive deutsch/deutsche/… → German …, an/aus/Herunterladen/Hochladen werkzeugeigen; zusatz =
  Muster, die der Auszug nicht findet) bzw. besucher-auswertung.html.json. NACH JEDER TEXTÄNDERUNG in den beiden Dateien: `py editor-sprache.py
  --offen`, fehlende in website-werkzeuge.json ergänzen (Regeln sprache/UEBERSETZEN.md), `py editor-sprache.py`. SCHUTZ DES INHALTS: Der
  Bearbeitungsrahmen (#frame, srcdoc) und das Vorschaufenster dürfen NIE übersetzt werden – ein Skript VOR dem Block merkt window.MC4Y_ROH
  {srcdoc, open}; rohSrcdoc(f, html) setzt f.__mc4ySprache = true und nutzt den Roh-Setter, rohOpen() das Roh-window.open; zusätzlich trägt das
  <html> des Rahmens translate="no" (schützt auch, wenn ein Rahmen per appendChild entsteht und die Laufzeit ihn beim Laden übersetzen würde,
  und hält Chromes eigene Übersetzung fern; gespeichert wird nur der Inhalt, nie dieses Attribut); uiEn() für Texte, die
  der Editor selbst baut („Write here …“). Regeln für neue Oberflächentexte: keine verschachtelten Inline-Elemente in Hinweisabsätzen (die Laufzeit
  übersetzt Absätze nur mit einer Ebene <i>/<b>/<span>); veränderliche Teile (Pfade, Arbeitsstand) in eigene Elemente (z. B. <i class="wsweb"
  data-unter="/files">) oder als Muster mit {0} in zusatz; keine zusammengesetzten Pluralformen („Eintr${n===1?'ag':'äge'}“) – ganze Wörter;
  zwei Sätze mit unterschiedlicher Bedingung in getrennten <span>. Besucher-Auswertung: EN()/LOC() erzeugen Zahlen (en-GB), Daten
  (TT/MM/JJJJ), Monats-/Wochentagsnamen, Zeile „Eingelesen“ (dateiInfo), Tooltip und CSV-Dateinamen selbst; auf 'mc4y-sprache' neu gezeichnet.
  Kopfzeile des Editors: ab ≤ 1240 px ohne Beschriftungen „Arbeitsstand/Sprache“, ≤ 1000 px ohne „MC4Y-Editor“ (Platz für DE | EN, feste Höhe 56 px).
• ANLEITUNG.en.md = englische Fassung von ANLEITUNG.md (Hinweis „If in doubt, the German version applies“) – bei Änderungen an ANLEITUNG.md mitziehen.
• website/wartung-cron.php: Bericht deutsch/englisch (Aufruf mit Argument en bzw. MC4Y_SPRACHE).
• site.webmanifest je Sprache (en/site.webmanifest mit lang/start_url), Download-Anzeigenamen downloads.en.json für alle Dateien/Ordner.
GEPRÜFT 21.10.2026: Helfer-Meldungen DE/EN per Wegwerf-Instanz (MC4Y_SFTP_TOKENDATEI usw. im Scratchpad) und im Editor (Verbindung testen →
englische Fehlermeldung des Helfers); Editor im OPFS-Probeprojekt auf Englisch: alle Reiter, alle Dialoge, Sprache Englisch, fehlende Übersetzung,
„Copy to live“ – MC4YSprache.offen() ohne Oberflächenreste; Speichern auf Englisch → gespeicherte Seite und gepatchte website-test/kontakt.html
unverändert deutsch; 1024 px ohne Querscrollen; Besucher-Auswertung mit erfundener Logdatei DE ↔ EN; Builds je zweimal, website_pruefen 0 Fehler;
englische Konsole des Builds. Vorschau-Browser: Ereignis „close“ von <dialog> wird dort nie ausgelöst – Passwortdialog des Server-Reiters lässt
sich nur in Chrome/Edge prüfen (kein Fehler des Editors). Die Kopie website-test/downloads/experimentell/software/mc4y-editor.html (Peters Download)
wurde am 21.10.2026 22:25 auf Peters „ja“ durch die zweisprachige Fassung ersetzt (siehe UPLOAD TEST 21.10.2026 (3)); künftig nur auf Peters Wunsch austauschen.

## Sprachen der Werkzeuge (seit 20.10.2026, Peters Auftrag „Die Tools sollen natürlich auch in Deutscher und Englischer Sprachversion zur Verfügung stehen, wobei ein Auswahlbutton für Deutsch oder Englisch zur Verfügung steht“)

Gilt für ALLE Dateien, die `py editoren-version.py` stempelt (*-editor.html, *-modeller.html, fragebogen-*.html; nicht mc4y-editor.html,
nicht FreeHTML/HTML-PY – die haben eigene Mehrsprachigkeit; Suite-Portal siehe „SUITE-PORTAL“ unten). Einzelheiten: `sprache/LIESMICH.md`,
Übersetzungsregeln und Glossar: `sprache/UEBERSETZEN.md`. Sicherung vor Beginn: editor-backups/2026-10-20_11-44-44_sprache-vorher.
ENTSCHEIDUNGEN: (1) EINE gemeinsame Laufzeit `sprache/sprache.js`, die editoren-version.py als Block `<!--MC4Y-SPRACHE-->…<!--/MC4Y-SPRACHE-->`
hinter `<meta name="mc4y-build">` einsetzt (zählt NICHT zum Inhalt/Build-Stempel, RE_SPRACHE in normalized(); sprache_block() fängt jeden Fehler
ab – Stempeln wird nie verhindert). Die Werkzeug-Quellen (werkzeug-kern, *-quellen …) bleiben unverändert deutsch; übersetzt wird beim Stempeln
bzw. im Browser. (2) Übersetzt wird zur Laufzeit im DOM (MutationObserver, Muster mit `{0}`, Absätze mit Auszeichnungen `<1>…</1>`, Meldungen
alert/confirm/prompt, title/placeholder/aria-label/alt/label/Knopf-value, Platzhalter auch in Textbereichen). Eingaben, Textbereiche, contenteditable
nie. Weiche Trennzeichen (hyph) zählen beim Nachschlagen nicht. (3) Sprache: Klick DE|EN (neben #mc4yVer in span#mc4yKopfRechts) →
localStorage 'mc4y-sprache-ui' (erst nach Klick; Endung -ui, bleibt im Suite-Portal beim Abmelden) > `?lang=de|en` (Website-Rahmenseiten
übergeben ?lang) > navigator.languages[0] de… → Deutsch, sonst Englisch. Ereignis 'mc4y-sprache'. Britisches Englisch wie die Website.
(4) Ausgaben: buildPrims/svgToPrims immer (Exporte), Druckrahmen/-fenster immer (Node.appendChild/insertBefore/… → close() des Rahmen-
dokuments übersetzt synchron; srcdoc-Setter; window.open), CSV über einen Blob-Proxy (type text/csv; nur Zellen = Wörterbucheintrag), abgeschaltet
(`"csv": false`) bei Buchhaltung (beide), Stammdaten, Vertrieb, Kunden, 8EM-Modeller (CSV wird dort wieder eingelesen). (5) Daten: Haken je
Werkzeug in `sprache/werkzeuge/<datei>.json` → `haken`: daten (Rückgabe übersetzen: emptyDoc, newDoc, blankDoc, exampleDoc, beispielProjekt,
fromPreset), beispiel (loadExample: danach globales `doc` übersetzen + neuzeichnen), m (3D-Kern: M.example), neuzeichnen (refresh/render).
Die Laufzeit erreicht `let doc`/`const M` der Werkzeuge über den gemeinsamen globalen Gültigkeitsbereich (typeof doc …) – kein eval. Ein beim
Öffnen schon angelegtes, unverändertes leeres Dokument (Vergleich ohne id/Zeit-Felder mit dem Original von emptyDoc/newDoc/blankDoc) wird
nachträglich englisch bzw. beim Zurückschalten wieder deutsch; geladene Dateien/Zwischenstände NIE. Fragebögen: nur Anzeige.
BEISPIELE DER GESCHÄFTSWERKZEUGE (21.10.2026, Peters „7 -ja“ über die Sitzung „8EM-Modell Tool“: „Die Beispiele von Kunden-Navigator und Vertriebs-
Navigator in der englischen Ansicht englisch anbieten … prüfe, ob das auch bei den übrigen Geschäftswerkzeugen sinnvoll ist … Die Buchhaltung bleibt
deutsches Steuerrecht … Gespeicherte Dateien und echte Bestände nie übersetzen“; Sicherungen editor-backups/2026-10-21_14-42-39_beispiele-englisch,
*_14-56-36_sprache-beispiele-englisch, *_15-08-08_loesungssuche-englisch): Kunden- und Vertriebs-Navigator, Kanban, ITSM, PM, Projekt-Auswertung und
Stammdaten zeigen ihr Beispiel auf Englisch – Haken beispielDaten (Erzeuger beispielDoc, beim Kunden-Navigator auch beispielKst = Beispiel-
Buchhaltung), beispielHash (#beispiel-Start), schluessel (Feldliste je Werkzeug, „eltern.kind“ z. B. branche.werte im Kundenprofil), ohne (ITSM:
loesungen/kb – eigene Sprachfassungen), neuzeichnen (Kunden: bhLaden + render). Laufzeit: BSP = zuletzt geladenes Beispiel mit Aufzeichnung [Pfad,
de, en] je übersetztem Wert und Fingerabdruck (vergleichbar); setzen() → bspUmschalten wechselt es nur, solange es unverändert ist; auch die
Coaching-Werkzeuge (beispiel/m) schalten so mit; nachBeispiel prüft, ob das Dokument wirklich ersetzt wurde (abgebrochene Rückfrage übersetzte
vorher eigene Daten). Stammdaten und Projekt-Auswertung {w: false}: nur beim Laden übersetzen, nie umschalten (dort wird das Beispiel sofort der
gespeicherte bzw. gemeinsame Bestand). Kanban setzt window.MC4Y_BEISPIEL_START = true, wenn es ohne Zwischenstand mit dem Beispiel startet. Buchhaltung
NICHT (deutsche Buchführung; die Protokollkette würde übersetzte Beispieldaten als Änderung melden). Folgeänderungen in den Quellen: Kunden-Navigator
fuellen() mit englischen Platzhaltern {salutation} („Dear Ms …“), {hello}, {contract_end}, {date} (Vorlagen im englischen Beispiel nutzen sie; deutsche
Vorlagen unberührt; Hinweis in Einstellungen › Vorlagen), Vorlagensuche über den Namen auch mit MC4YSprache.en(n) (VORLAGE_FUER, Zahlungserinnerung);
Vertriebs-Navigator fuellen() + {salutation}, {hello}, {date}, {source}, {retention}, FUNK_KW/HIER_KW mit englischen Wörtern (board member, plant manager,
project, production, plant, logistic, warehouse, dispatch, construction) – Funktion/Hierarchie und Passung im Beispiel auf Deutsch und Englisch gleich
(37 Werte geprüft); PM p7_agil Kanban-Import erkennt Rolle „Development“; ITSM: englische Fassungen für KB-0004/KB-0005 und englische Füllwörter
KB_STOPP_EN in der Lösungssuche (= LS_STOPP auf dem Server). Englische Vorlagen-/Mail-Texte stehen als `texte` in sprache/werkzeuge/kunden-
bzw. vertrieb-editor.html.json (mit Zeilenumbrüchen; Schlüssel werden normalisiert). Haken „auch“ (seit 21.10.2026 nachmittags): Pfade innerhalb eines
ohne-Teilbaums, die doch übersetzt werden (ITSM: loesungen.service/.ticketKat/.systeme.name – Namen, die außerhalb übersetzt sind; sonst passten Service und
System von Ticket und Lösung im englischen Beispiel nicht mehr zusammen). GEPRÜFT im Vorschau-Browser je Werkzeug: Start #beispiel auf
EN und DE, Umschalten hin und zurück, bearbeitetes Beispiel bleibt, Laden per Knopf, Reste-Suche über alle Ansichten (nur Eigennamen übrig),
Kunden-Kennzahlen DE = EN, englische Briefe gefüllt; Rauchtest DE 52/52 ohne Warnung, B-EN 0 Fehler (Reste nur Namen/Adressen/gleichlautende
Wörter), Schnittstellentests 14/14, api_test 628/628. Mit englischen Beispielen greifen die früheren, auf deutsche Beispieltitel zugeschnittenen Muster
(z. B. „#{0} Kundenkonto anlegen – Durchlauf {1} T …“) nicht mehr – dafür ALLGEMEINE Muster in `texte` (Kanban „{0} – Durchlauf {1} T, Bearbeitung
{2} T“, „{0} T · {1} seit {2} T“, Rolle „Oberfläche“ = „User interface“; PM/Projekt-Auswertung „{0}: Ende → Anfang“ usw. samt „… {1} AT“ = WD).
REGEL: Ein Muster muss außer Platzhaltern mindestens ein Wort enthalten – „{0} → {1}: {2}“ findet die Laufzeit nicht (Suche über Wortanfänge). (6) Gespeicherte
Dateien bleiben sprachunabhängig. Nutzertexte, die wortgleich einem Oberflächentext sind, werden nur angezeigt übersetzt (bewusst hingenommen).
(7) Buchhaltung = deutsches Steuerrecht, Begriffe erklärt; Fragebögen mit allen Pflichthinweisen; Rechtshinweise im Hilfe-Fenster englisch mit
„in case of doubt, the German version applies“ (sprache/grund.json). Werkzeugspezifische Bedeutungen in `texte` der Werkzeug-Konfiguration
(z. B. Buchhaltung Soll/Haben = Debit/Credit, PM Ist/Soll = Actual/Planned, Adressfeld „Ort“ = City; 8EM Ist/Soll = as-is/to-be).
ÜBERSETZUNGSSPEICHER sprache/texte/*.json (Teile 0001–0065 + taschenrechner.json, ~27 900 Einträge, `pruefen` 0 Fehler), erzeugt über
`py sprache/sprache.py offen` → `zeigen NNNN` → arbeit/NNNN.json → `einsetzen NNNN` (prüft Platzhalter/Auszeichnungen). Übersetzt von
Sonnet-Unteragenten nach UEBERSETZEN.md, danach `britisch.py`-Lauf (Scratchpad; color→colour, cost center→cost centre … nur wo das Wort nicht im
deutschen Schlüssel steht). Neue Texte in Werkzeugen = offen + übersetzen + stempeln; Reste aus dem englischen Rauchtest (`/__pruef/?start=alle&en=1`,
pruefstand.js spracheStart/resteSammeln/deutschRest: Rest = typisch deutsche Wörter oder < 80 % der Wörter aus den englischen Wörterbuchtexten)
mit `sprache.py reste tests/ergebnisse/letzter-rauchtest.json` übernehmen (Zahlen werden dabei zu Platzhaltern: „Phase 1 von 2“ → „Phase {0} von {1}“;
offen nimmt die zusatz-Schlüssel mit). AUSZUG (sprache.py) hochzählen, wenn sich die Ausleseregeln ändern (macht .cache-schluessel.json ungültig).
Ausleseregeln: Textbereich-Platzhalter ja, kleingeschriebene Einzelwörter aus ?:-Ausdrücken nur, wenn sie in ≥ 2 anderen Texten des Werkzeugs als
Wort vorkommen (KLEINWORT, CODEWORT), Verkettungen, die mitten in einem Tag beginnen/enden („{0}>◂ nach vorn“), werden beschnitten. Laufzeit:
deutsche Pluralendung im Platzhalter direkt hinter einem Wort („{0} Hinweis{1}“ mit „e“/„en“) wird im Englischen zu „s“; Muster werden auch über
Wortanfänge gefunden („Verweise“ → Muster „…Verweis{1}“). Prüfstand: Rauchtest und Schnittstellentests öffnen Werkzeuge ausdrücklich mit
`&lang=de` bzw. `&lang=en` (unabhängig von der Browsersprache; Schnittstellentests finden Knöpfe über deutsche Texte).
Ordner sprache/ nicht hochladen, nicht ins Update-Zip.
DATENSCHUTZ: Zeile „Sprache der Werkzeuge“ in der Speichertabelle (generator/inhalte/datenschutz.html + en/), Sicherung
editor-backups/*_datenschutz-sprachwahl-werkzeuge; speicher-bekannt.json deckt 'ls:mc4y-sprache-ui' über das Muster „…-ui“ ab.
BLOG: Entwurf 2026-10-20-tool-werkzeuge-englisch.md (Bild tool-werkzeuge-englisch.jpg). TESTPLAN: Q-14 … Q-17, Stufe B-EN.
GEPRÜFT 20.10.2026 (Protokoll im TESTPLAN): statisch 0 Fehler; Rauchtest Deutsch mit Handy und Exporten 52/52 ohne Warnung (Q-11 grün – die
Sprachwahl speichert beim Öffnen nichts); Rauchtest Englisch ohne Laufzeitfehler, Reste übernommen und übersetzt; Schnittstellentests 69/69;
Website-Prüfung 0 Fehler; Maus: Umschalten DE↔EN (Kanban), Beispiele/neue Dokumente englisch, Druckrahmen und CSV englisch, 375 px.
Werkzeugdateien werden beim Stempeln geändert, ihre Build-Stände nicht (Block zählt nicht zum Inhalt). Sicherungen editor-backups/
*_werkzeuge-vor-sprachstempel[-2], *_sprache-texte-vor-britisch, *_kompass-tabelle-englisch. NICHT geprüft: Werkzeuge über file:// in Chrome/
Edge mit englischem Browser, echte PowerPoint/Word-Öffnung englischer Exporte.
SUITE-PORTAL (20./21.10.2026, Peters „Übersetzte das Suite-Portal auch ins englische“; Suite-Fassung bleibt 0.4.4, Schemata unverändert): Pseudo-
Werkzeug `mc4y-suite` in sprache.py (suite_teile: public/*.php ohne cli/router/api, public/assets/**/*.js, app/*.php, app/katalog.json; PHP wird für den
Auszug in JS-ähnlichen Text umgeschrieben – _php_als_js: `.` → `+`, Kommentare weg, `['schlüssel']` → Name, `"$x"` → Verkettung; _php_teile erkennt
`?>` nur außerhalb von Zeichenketten; suite_schluessel filtert SQL (SQL-Muster), Kennungen und `$var`, nimmt `=>`-Werte; SUITE_AUSZUG hochzählen bei
Regeländerungen). `py sprache/sprache.py suite` schreibt `mc4y-suite/public/assets/sprache.js` (Wörterbuch ~2 700 Texte + Laufzeit, ~283 KB) – ERZEUGT,
nicht von Hand ändern. Portal: index.php bindet es VOR portal.js ein (CSP script-src 'self' → externe Datei), datenschutz.php, einmal.php,
windows-anmeldung.php, app/ablage_einrichten.php vor </body>. In Werkzeugen der Suite: mc4y-suite/bauen.py setzt `data-sprache` (sha1) am Anschluss-
Skript, anschluss.js lädt assets/sprache.js nach (spracheNachladen; erst nach DOMContentLoaded, weil der Anschluss im Kopf VOR der Laufzeit des Werkzeugs
steht) → neue Funktion `MC4YSprache.ergaenzen(d, gleich)` in sprache/sprache.js (fehlende Einträge aufnehmen, Vorrang hat das Werkzeug, danach neu
übersetzen). Seit 21.10.2026 folgt die Laufzeit dem `storage`-Ereignis des Schlüssels 'mc4y-sprache-ui' (Portal ↔ eingebettetes Werkzeug wie die
Lösungsdatenbank, weitere Tabs). Konfiguration sprache/werkzeuge/mc4y-suite.json (csv aus, Muster für Kalender-/Wiki-/Kanban-Meldungen, Grüße, AD-Begriffe,
Datenschutz → Privacy). Übersetzungen sprache/texte/0068–0078.json (Glossar-Absatz „Suite-Portal“ in UEBERSETZEN.md: Toolbox, Workspace, Administration,
„Personal und Coaching“ = „People and coaching“, Programm als Software = „program“ auch im britischen Englisch). Datum: `const LOC = () => … 'en-GB' : 'de-DE'` in anschluss.js,
portal.js, arbeitsplatz/aufgaben/cmdb/dokumente/wiki.js; kalender.js EN()/lang()/monatJahr(). Wiki: wiki.js schickt `sprache` bei wiki_bereich_speichern,
wiki.php legt die Startseite dann als „Start page“ mit englischem Text an (api_test prüft beides). Portal-Fußzeile in index.php umgebaut (Version-span,
Rest in einem span, damit der Satz als Ganzes übersetzt wird). Handbuch: public/handbuch-en.html (statisch, ohne Skript; portal.js tauscht beim Klick
handbuch.html ↔ handbuch-en.html), paket_bauen.py PFLICHT + handbuch-en.html und assets/sprache.js, statisch_pruefen prüft beide Handbücher.
datenschutz.php: Absatz „Sprache“ (mc4y-sprache-ui, nichts an den Server) und auf Englisch p.nur-en „In case of doubt, the German version applies“
(suite.css .nur-en). suite.css: .kopf #mc4ySprache in Navy auf hellem Kopf, eingebettet ausgeblendet. GEPRÜFT in einer Wegwerf-Suite (Port 8098) mit
`MC4YSprache.offen()`: Arbeitsplatz, Toolbox, alle sieben Module, alle Verwaltungsreiter, Konto, Einrichtung, Datenschutz – übrig nur Daten (Namen,
Pfade); Kanban in der Suite (Pille, Öffnen-Dialog englisch), Lösungsdatenbank im Rahmen folgt dem Portal in beide Richtungen, 375 px ohne Querscrollen;
api_test 594/594, statisch 0 Fehler, Pakete neu (paket_bauen). Deutsch bleiben nur noch vom Server angelegte Datennamen (werden nur übersetzt angezeigt);
die Installationsskripte sind seit 21.10.2026 abends zweisprachig („ALLES ZWEISPRACHIG“ unten). Die
Desktop-Taskleiste (ab 1.4.0, Kommandozeile und eigene Installationsskripte ab 1.4.1) und der grafische Installer samt Helfer-Konsolen sind zweisprachig. Die INSTALLIERTEN Suiten (PC, <server>) haben das noch nicht – nur mit Peters OK aktualisieren.
Blog: tool-mc4y-suite (Live-Entwurf, Testfassung DE und EN, aktualisiert 2026-10-21) um Punkt und Absatz „Deutsch und Englisch“ ergänzt (Sicherung
editor-backups/2026-10-21_00-10-38_blog-suite-englisch). Website-Datenschutzerklärung unverändert (Suite läuft nicht auf der Website; Hinweis in public/datenschutz.php).
BEKANNTE GRENZEN: Texte auf Canvas (PNG der 3D-Werkzeuge: Namen) bleiben, wie sie im Datenbestand stehen. (Word-Ausgaben, 8EM-Meilensteine und die
Wertstromstufe sind seit 21.10.2026 abends erledigt, siehe unten.)
ALLES ZWEISPRACHIG (21.10.2026 abends, Peters Wortlaut über die Sitzung „8EM-Modell Tool“: „wenn ich sage zweisprachig alle Module, Tools und Blogeinträge,
dann meine ich das auch so und nicht nur immer ein bisschen was machen, das hält auf“; Website/Blog/mc4y-editor/SFTP/Generator = Website-Sitzung, FreeHTML =
FreeHTML-Sitzung; Sicherungen editor-backups/2026-10-21_18-08-12_suite-skripte-zweisprachig (install/, installer, Doku, cli/betrieb/sicherung, Prüfstände),
*_18-26-33_uebertragungs-assistent-zweisprachig, *_anleitungen-zweisprachig). Vorgabe für alle Skripte: Notizordner der Sitzungsgruppe,
zweisprachige-installationsskripte-vorgabe.md. INSTALLATIONSSKRIPTE (alle drei Systeme): Sprache = Parameter (Windows `-Sprache de|en`, Shell `--lang de|en`
bzw. `--sprache`, auch `=en`) > Umgebung MC4Y_SPRACHE > System (Get-UICulture bzw. AppleLanguages des angemeldeten Benutzers – unter sudo von SUDO_USER bzw.
/dev/console – bzw. LANGUAGE/LC_ALL/LC_MESSAGES/LANG; C/POSIX = keine Angabe) > Englisch. Funktion `L 'deutsch' 'english'` (PowerShell: `$script:EN`,
`$SpracheKurz`; Parameter NIE $de/$en nennen; suite-betrieb.ps1 OHNE param(), wird per Punkt eingebunden, übernimmt EN/L des Aufrufers). Neustart mit
Administratorrechten bzw. sudo gibt die Sprache immer weiter; auf-server-installieren.ps1 reicht sie als letztes Element der -ArgumentList an
installieren-server.ps1 auf dem Server. Feste Vorsilben Hinweis/Note, FEHLER/ERROR, [Probelauf]/[Dry run], Protokoll/Log; Rückfragen [j/N]↔[y/N] bzw.
(j/n)↔(y/n), j/J/y/Y gelten immer; Bestätigungswort LOESCHEN bleibt (englisch „type LOESCHEN (German for DELETE)“), JA bzw. YES beim Verschieben/Wiederherstellen.
Deutsche Texte wortgleich (per AST bzw. Literal-Vergleich gegen die Sicherung geprüft). Erzeugte Dateien in der gewählten Sprache: Installationsprotokoll,
KURZANLEITUNG.txt (dabei behoben: wurde unter Windows als EINE Zeile geschrieben – Komma bindet stärker als +), Kommentare in konfig.php/php.ini/Pool/Units/plists,
Beschreibungen von Aufgaben/Konto/Firewall, .desktop (Name/Comment + [de]), Windows-Verknüpfung „MC4Y Productivity Suite.url“ bzw. „MC4Y-Produktivitaetssuite.url“
(die der anderen Sprache wird entfernt). Unverändert: Optionsnamen (-Probelauf, --aktualisieren …), Werte in installation.conf/konfig.php, Datei-/Ordnernamen,
technische Namen (Pool, Aufgaben, Zertifikat), JSON-Schlüssel von installation.json/Ergebnisdatei. ZEILEN FÜR DIE HELFER (genau so): Shell `[Dry run] Install
packages: …`, `… Install recommended packages: …`, Mac `Download PHP <V> for <Chip> (dl.static-php.dev, approx. <n> MB), verify the checksum and unpack to …`,
`PHP:          suite PHP already present (…)`, `This Mac has macOS X. The suite needs macOS …`, Homebrew-Sätze, `Address:`, Zeile `Setup code for the first
administrator (only needed from another computer):` + Code; Windows `Data:`, `Address:`, `PHP:`, `Windows Server with IIS`, `Done: https://…` (nur
auf-server-installieren), `Setup code for the first account: X`, `Dry run finished - nothing was changed.`. installer_dienst.py MUSTER/SATZ/hat()/gruppe() und
installer-dienst.ps1 $MUSTER (server, daten, adresse, fehler, fertig, code, codeZ, laerm = Transkript-Kopf und icacls auch englisch) erkennen BEIDE Sprachen
(auch alte deutsche Protokolle); Get-SkriptSprache($d) gibt jedem Skript (Probelauf, Installation, Fernwartung, Taskleisten-Installer) `-Sprache` bzw. `--lang`
der Installer-Oberfläche mit (Feld „sprache“ jeder Anfrage, installer.html spracheJetzt; Plan wird beim Umschalten neu gerechnet). cli.php: `--lang de|en`
(auch MC4Y_SPRACHE); Aufrufe, deren Ausgabe ausgewertet wird (`status` → „Sicherungen:“), immer mit `--lang de`; datenordner-verschieben erkennt zusätzlich
„Backups:“. Systemprüfung: betrieb_en()/betrieb_zahl() (Zahlen 359.8 GB, „an“ = „on“ – Eintrag in sprache/werkzeuge/mc4y-suite.json texte). NICHT auf echtem
macOS/Linux ausgeführt (kein bash) – statisch geprüft; Prüfliste für Peter im TESTPLAN (SU-I14, SU-I17).
WEITERE LÜCKEN GESCHLOSSEN: Übertragungs-Assistent (uebertragungs-assistent.py, T()/EN-Wörterbuch am Dateiende, „DE | EN“ im Fenster, `--lang`, Einstellung
„sprache“; Daten der *.uebertragung.json bleiben deutsch); 8EM-Modeller: Office-Haken (sprache.js officeHaken: XML-Texte von Word/ODF/PowerPoint/Excel in
makeZip übersetzen, Konfiguration `office: true` – 8EM, Mindmap), html-Haken txSecHtml (Vorlagen-Abschnitte des Text-Editors englisch), msKind/msParse/msDate
erkennen englische Spalten, Statuswörter und Datumsformen, Tabellenkopf „Wertstromstufe“; Vertriebs-Navigator: Recherche-Auftrag englisch (rechercheAuftragEn,
unveränderter Auftrag folgt dem Umschalter), englische Spaltennamen im Import (IMP_SYN); Kunden-Navigator: Antwort mit englischem Zitatkopf; Kunden-/Vertriebs-
Navigator: „Mustervorlagen ergänzen“ auf Englisch englisch, Doppel über deutschen ODER englischen Namen erkannt; LEERE DOKUMENTE der Geschäftswerkzeuge
(Kanban neuDoc, ITSM/Kunden/Vertrieb leeresDoc, PM leeresDoc('wasserfall', false)): Haken `daten: [{f, leer: [Argumente]}]` – Rückgabe englisch, ein beim
Öffnen schon angelegtes leeres Dokument folgt der Sprache (LEER_ARGS/LEER_KEYS), WÄHREND ein Beispiel-Erzeuger läuft (IN_BSP) bleibt die Rückgabe deutsch, damit
das Beispiel vollständig zurückschaltet. CSV DER WIEDER EINLESBAREN WERKZEUGE (Stammdaten, Kunden, Vertrieb, 8EM): Haken `csv: "kopf"` = auf Englisch nur
die erste Zeile (Spaltenköpfe) übersetzen, Werte bleiben (sonst bräche das Wiedereinlesen); neue Laufzeit-Funktion MC4YSprache.de(englisch) → deutscher
Wörterbuchtext (Rückübersetzung, REV, nur ganze Texte ohne Platzhalter, Groß/Klein egal); die Importe (Stammdaten impKopf, Vertrieb/Kunden impFeldVon über
impFeldEinfach – .map(impFeldVon) übergibt den Index –, 8EM impAuto) versuchen einen unbekannten Kopf zuerst unverändert, dann über MC4YSprache.de (nie
umgekehrt: „Name“ ist z. B. englisch auch die Übersetzung von „Bezeichnung“). Buchhaltung bleibt `csv: false`. Suite: CMDB-CSV und -Vorlage mit englischen Spaltenköpfen/Kategorien/Status (Import erkennt beide, kategorieAus über
MC4YSprache.en), Protokoll-CSV der Verwaltung, Kalender-Abo/-Export `&lang=en` („busy“, englische Fehlertexte), datei.php-Fehlertexte und Version
„(version x.y)“ (dokumente.js gibt &lang mit; kern.php anfrage_englisch/suite_text_en = Server-Texte über das Wörterbuch in public/assets/sprache.js);
„trial run“ → „dry run“ in den Installer-Übersetzungen. Prüfstände: api_test 640 (+7 Sprachprüfungen), installer_test IN-L07 … L10, datenordner_verschieben_test
20/20 (V13–V20 englisch), Übertragungs-Assistent --selbsttest DE/EN. BEWUSST DEUTSCH (Ausnahmen): Buchhaltung (deutsches Steuerrecht: Rechnungen, ELSTER,
DATEV, UStVA, Berichte; Begriffe in der Oberfläche erklärt), Daten der Übertragungsdatei für deutsche Steuerprogramme, Prüfstand/Bauskripte (tests/, *bauen.py,
editoren-version.py, paket_bauen.py), Fehlerprotokolle des Servers (error_log), Ausgaben fremder Programme (robocopy, apt, brew, Windows-Dialoge),
Daten der Werkzeuge (gespeicherte Dateien bleiben, wie sie angelegt wurden).
NACHTRAG 21.10.2026 spätabends (Hinweise der FreeHTML-Sitzung über „8EM-Modell Tool“): (1) translate="no" INNERHALB eines gemischten Absatzes wurde
übersetzt – sprache.js block(): Kinder mit ausgenommen(e) (translate="no", .notranslate, data-mc4y-sprache="aus", Ausnahmen des Werkzeugs) behalten Text und
Attribute, im Absatz-Pfad und im Rückfall „Stücke einzeln“; geprüft mit Absätzen mit/ohne Wörterbucheintrag, Umschalten hin und zurück, Textänderung durch das
Werkzeug. Danach gestempelt (editor-backups/2026-10-21_19-44-31_editoren), `py editor-sprache.py` für mc4y-editor.html/besucher-auswertung.html (MC4Y_ROH-Skript
bleibt vor dem Block; Sicherung *_19-45-18_editor-sprache), sprache.py suite, mc4y-suite/bauen.py, paket_bauen.py. Der FreeHTML-Editor nimmt die Laufzeit erst beim
Bauen mit. (2) „alles übersetzt“ im Auszug genügt nicht: RUNDGANG mit MC4YSprache.offen() – Werkzeuge über den englischen Rauchtest (resteSammeln je Ansicht),
Suite-Portal in einer Wegwerf-Suite (eigener launch.json-Eintrag, Port 8097, danach entfernt; Beispieldaten auf Englisch; alle Hauptseiten, 13 Verwaltungsreiter,
Module mit Unteransichten und Dialogen, Kalender Monat/Woche/Tag/Liste, Taskleisten-Darstellung mit allen Kategorien, Lösungsdatenbank im Rahmen, Datenschutz,
einmal.php-Fehlerseite) und installer.html?lang=en#demo=…&bild=1…6 für alle sieben Vorführungen. Übrig nur Daten (Namen, Pfade, Eingaben, Datumsangaben),
die bewusst gleichen Namen „Kunden-Navigator“/„Vertriebs-Navigator“ („Sales Navigator“ = Marke von LinkedIn), Protokoll-Kennungen der Verwaltung (Einträge bleiben,
wie protokolliert) und die Schrittleiste des Installers (Nummer und Name einzeln übersetzt – offen() meldet sie fälschlich).
DATUM AUF ENGLISCH (21.10.2026, vierter Volltest; vorher zeigten 9 von 20 gemessenen Werkzeugen auf Englisch „13.10.2026“, Suite/8EM/Website schon „13/10/2026“):
sprache.js `datumEn(s)` wandelt in der ANZEIGE „TT.MM.JJJJ“ (Tag 1–31, Monat 1–12, nicht hinter/vor Ziffer bzw. Punkt) und „TT.MM.–TT.MM.JJJJ“ in „TT/MM/JJJJ“ –
angewandt in textKnoten, attribute (nicht placeholder/label: zeigen das Eingabeformat), block (Absatz-Eintrag), prims (Grafik-Exporte) und t(); NICHT in textUeb
selbst, also nicht in CSV, Dokumentdaten (daten/wertUeb) und Eingaben. Umkehrbar wie jede Übersetzung (TXT/ATT/BLK-Einträge). Die Datumsleser der Werkzeuge
(Importe, Eingaben) erwarten weiter „TT.MM.JJJJ“ bzw. ISO. Danach Kette: editoren-version.py, editor-sprache.py, sprache.py suite, mc4y-suite/bauen.py,
Kopie nach ../../freehtml-editor/quellen/sprache/sprache.js + dort `py bauen.py`, paket_bauen.py (Sicherungen editor-backups/2026-10-21_21-21-02_volltest4-sprache-datum,
*_21-22-09_editoren, *_21-22-13_editor-sprache, *_freehtml-laufzeit). Testfall Q-24. Gleichzeitig: „{0} von {1} Projekten“ (Projekt-Auswertung, texte) und
„SWOT-Analyse“ (Analyse, texte) – fehlten im Wörterbuch; der Rauchtest sah sie nicht (Beispiel der Projekt-Auswertung nur per Knopf; „analyse“ ist auch englisch).
SÄTZE (gleicher Volltest): Meldungen, die das Programm aus zwei Sätzen zusammensetzt („Gespeichert (Version 4). Die englische Fassung ist als „Übersetzung prüfen“
gekennzeichnet.“, Wiki), blieben deutsch, obwohl jeder Satz im Wörterbuch stand. nachschlagen() zerlegt seitdem als letzte Stufe nach . ! ? … + Leerraum +
Großbuchstabe/Anführungszeichen (SATZ/SATZ_G, ohne Lookbehind wegen älterer Safari) und übersetzt NUR, wenn jeder Satz einen Eintrag hat (keine Mischtexte).
Dazu texte: „[[ ]] Seite“ (mc4y-suite.json, Wiki-Werkzeugleiste) und „Ticket-Kategorie“ (itsm-editor.html.json, Eckdaten der Lösungsdatenbank). Testfall Q-25.
Der Vorschau-Browser kann Ziele nur auf dem zugewiesenen Port öffnen: launch.json-Einträge mit fest eingetragenem Port (http.server 8322/8323) laufen bei belegtem
Port ins Leere – dann die Datei über den Prüfserver (Kopie in tests/ergebnisse/, danach löschen) öffnen.
DATUM OHNE JAHR, WOCHENTAGE, MONATE, KW, UHR (21.10.2026 spätabends; Fund der Website-Sitzung online: ITSM-Übersicht zeigte auf Englisch „26.10. 18:00“;
Auftrag über „8EM-Modell Tool“ mit Peters „behebe Fehler ohne Rückfrage“; Sicherung editor-backups/2026-10-21_22-41-37_sprache-datum-kurz). Ursache: Das ITSM
baut „TT.MM. HH:MM“ (kurzZeit) und die Kalenderachse „TT.MM.“ selbst; Monatsnamen, „KW“ und „Uhr“ standen nur in den Wörterbüchern einzelner Werkzeuge (jedes
Werkzeug bekommt nur die Einträge, die in seinem Quelltext vorkommen). Lösung ZENTRAL in sprache.js `datumEn` (Werkzeug-Quellen unverändert): DAT_KZEIT „26.10.
18:00“ → „26/10 18:00“ (Tag/Monat ein- oder zweistellig), DAT_K bzw. DAT_KSPANNE „26.10.“/„26.10.–02.11.“ → „26/10“ (OHNE Uhrzeit nur zweistellig – Gliederungs-
nummern wie „1.2.“ und „3.1.–3.4.“ bleiben; Versionen/IP-Adressen bleiben wegen der Punkt-/Ziffer-Grenzen), WT_VOR Wochentag vor einem Datum („Mo., 26.10.2026“ →
„Mon, 26/10/2026“, „Freitag, 16. Oktober 2026“ → „Friday, 16 October 2026“; nicht „So 3 Tage“), WT_SP „Mo–Fr“ → „Mon–Fri“, MON_TAG „26. Oktober 2026“ → „26 October
2026“ (Punkt der Abkürzung fällt nur vor einer Jahreszahl weg), MON_JAHR „Oktober 2026“/„Okt 26“/„Mär 2027“, EINZELN = ganzer Text ist ein Monats-/Wochentagsname
bzw. „KW“ („Okt“, „Montag“, „Mo“ – nur Namen, die sich im Englischen unterscheiden), KW_RE „KW 43“ → „Wk 43“ (wie Kanban; nicht in „LKW“), UHR_SP/UHR „18:00 Uhr“ →
„18:00“, „8–17 Uhr“ → „8:00–17:00“ („rund um die Uhr“, „Uhrzeit“ bleiben). Wörterbuch-Einträge gehen vor (datumEn läuft nach textUeb). officeXml(x, mitDatum)
wendet datumEn seitdem auch in Word-/ODF-/PowerPoint-Ausgaben an (vorher dort gar nicht), NICHT in xl/… (Excel kann wie CSV wieder eingelesen werden). Feste
Fallliste tests/pruefstand/datum-en-faelle.json (69 Fälle, Prüfaufruf in der Datei); der englische Rauchtest meldet verbliebene deutsche Formate als Warnung
(pruefstand.js datumReste: Textknoten außerhalb von Eingaben mit TT.MM.JJJJ, TT.MM., „Uhr“ nach Ziffer, „KW n“, deutsche Monatsnamen mit Zahl, Wochentag vor
Datum). Testfall Q-26. Kette: editoren-version.py (Sicherung *_22-46-07_editoren), editor-sprache.py (*_22-46-16_editor-sprache), sprache.py suite, mc4y-suite/
bauen.py, paket_bauen.py. Der FreeHTML-Editor hat die Laufzeit erst nach Kopie in seine quellen/sprache/ und neuem Bauen (FreeHTML-Sitzung). (Zahlen: siehe
„ZAHLEN UND BETRÄGE AUF ENGLISCH“ unten.)
GEPRÜFT: 69 Fälle ok (DE unverändert), ITSM im Browser (Übersicht/Liste/Board, Kalenderachse, Berichte „Wk …“, EN → DE → EN), 8EM makeZip (Word/PowerPoint
umgestellt, Excel nicht), B-EN 52/52 ohne Fehler und ohne Datumsreste, E (8 Werkzeuge + UE-01 … 07) 18/18, A 0/0, D1 640/640.
ZAHLEN UND BETRÄGE AUF ENGLISCH (21.10.2026 nachts, Peters Vorgabe „wenn ich sage zweisprachig …, dann meine ich das auch so“ und „behebe Fehler ohne Rückfrage“,
Auftrag über „8EM-Modell Tool“; Sicherung editor-backups/2026-10-21_23-09-41_sprache-zahlen): sprache.js `zahlEn` läuft an denselben Stellen wie `datumEn` und danach
(`anzeigeEn` = zahlEn(datumEn(s)): textKnoten, attribute außer placeholder/label, block, prims, t(), officeXml außer xl/). Dezimalkomma → Punkt, Tausenderpunkt →
Komma, Euro britisch VORN ohne Leerzeichen („1.234,50 €“ → „€1,234.50“, „−1.234,50 €“ → „−€1,234.50“, „12,5 T€“/„12 Tsd. €“ → „€12.5k“/„€12k“, „1,2 Mio. €“ →
„€1.2m“, „Mrd.“ → „bn“, Spanne „250–800 €“ → „€250–€800“), Prozent ohne Leerzeichen („12,5 %“ → „12.5%“). NUR EINDEUTIGE FÄLLE (ZAHL_RE + Prüfungen im Callback):
mit Dezimalkomma, mit Währung/Prozent (auch über eine Spanne Z_BIS), mit Einheit (Z_EINHEIT: km, kg, MB, h, Std., Tage, days, AT, SP, Stück …) oder mit mindestens
zwei Tausendergruppen. UNVERÄNDERT: „1.234“ ohne Einheit (Version/Kapitel möglich), „1,250“ (ganzzahliger Teil ≤ 3 Ziffern ohne Tausenderpunkt + genau drei
Nachkommastellen: englisch geschrieben oder nicht eindeutig), alles mit nicht-dreistelligen Punktgruppen (0.4.6, <server-ip>, 3.2, 12.30, 2026.10), Zahlen direkt
hinter Buchstaben/Ziffer/./,/:/_/#// („v1,2“, „INC-0001“; ein Bindestrich direkt nach einer Ziffer gilt als Strich: „2,5-3,5“ → „2.5-3.5“), vor „,Ziffer“ oder „.Ziffer“
(Listen „1,2,3“, englisch „1,234.50“ → idempotent), Funktions-/Typschreibweise „DECIMAL(10,2)“ (Buchstabe + „(“), Code-Beispiele: Text in code/kbd/samp/pre/tt behält
Datum UND Zahlen (codeArtig in textKnoten; block() nimmt für Code-Kinder den Text vor der Umstellung, rohTeil). Gespeicherte Daten, CSV, Excel, Eingaben unberührt.
HAKEN je Werkzeug (sprache/werkzeuge/<datei>.json): `"zahlenGanz": true` = auch „1.234“ ohne Einheit (buchhaltung- und buchhaltung-privat-editor: keine Versions-/
Kapitelnummern; Diagrammachse und volle-Euro-Spalten der Voranmeldung), `"zahlen": false` = gar nicht (bisher nirgends), `"zahlenAus": "<CSS-Auswahl>"` = diese
Bereiche wie Code behandeln (CODE_SEL; taschenrechner-editor: "#ergebnis,#verlauf,#speicher,#eDez,.linkbtn" – Ergebnis/Verlauf/Speicher formatiert er selbst,
Formatauswahl und Beispiel-Ausdrücke zeigen die Eingabeschreibweise; Erklärtexte werden normal umgestellt: „Add 19% VAT to €250“). TASCHENRECHNER dazu: E.dez folgt
der Sprache (dezAuto: Englisch → 'punkt', Deutsch → 'komma'), bis es in den Einstellungen gewählt wird (E.dezWahl; ältere Einstellungen mit 'punkt' gelten als Wahl),
Ereignis 'mc4y-sprache' stellt um; Rechenprobe TR-02 auf Deutsch unverändert 116/116. Buchhaltung: Rechnungs-/
Berichts-PDF (eigene PDF-Erzeugung), XRechnung, ELSTER-XML, DATEV und CSV laufen nicht über die Laufzeit und bleiben deutsch. Prüfstand: tests/pruefstand/zahl-en-faelle.json
(faelle/unveraendert/ganz/aus, Prüfaufruf in der Datei, Q-27) – bei Änderungen an zahlEn mitpflegen; englischer Rauchtest meldet „deutsche Zahlen-/Betragsformate“
(pruefstand.js ZAHL_DE/zahlReste; überspringt Code-Elemente und zahlenAus-Bereiche). GEPRÜFT: Q-27 123 Fälle + Q-26 69 Datumsfälle ok, B-EN alle 52 ohne Fehler
und ohne deutsche Zahlen-/Datumsformate, Buchhaltung EN im Browser („€6,178.14“, Achse „2,480“), Taschenrechner EN/DE + TR-02 116/116, E 69/69, A 0/0, D1 640/640.
Sicherungen editor-backups/2026-10-21_23-09-41_sprache-zahlen, *_23-25-09_editoren, *_23-33-35_editoren, *_23-33-39_editor-sprache.
„VON {0}“ UND SCHWACHE MUSTER (23.10.2026, Auftrag über „8EM-Modell Tool“: Peter wollte den Fehler „from Hand angelegt“ in der CMDB-Herkunft an der Wurzel
behoben haben; die Stufe-4-Sitzung hatte ihn nur über einen eigenen Suite-Eintrag „{0} · angelegt {1} · geändert {2}“ umgangen; Sicherung
editor-backups/2026-10-23_16-38-11_sprache-von-muster). URSACHE: Das gemeinsame Muster „von {0}“ → „from {0}“ (texte/0018.json, ausgelesen aus Bruchstücken
wie `' von ' + name` im 8EM-Modeller, ITSM und Kanban) passt auf jeden Text, der mit „von “ beginnt, und wird VOR dem Aufteilen an „ · “ geprüft – es
verschluckte „von Hand angelegt · angelegt …“ samt Rest. Im Suite-Wörterbuch wirkte es zudem über `ergaenzen` in allen Werkzeugen der Suite.
„von“ heißt je nach Stelle from/of/by. ÄNDERUNGEN: (1) texte/0018.json „von {0}“ = „von {0}“ (bewusst unverändert → in keinem Wörterbuch mehr); je Werkzeug
in sprache/werkzeuge/*.json `texte`: 8em-modeller „von {0}“ = „of {0}“ (Kennzahl-Unterzeilen `s:'von ' + n`), kunden-editor „von {0}“ = „from {0}“
(E-Mail-Absender `'von ' + adrText(m.von)`), itsm-editor „Genehmigt von {0}“ = „Approved by {0}“ und „Abgelehnt von {0}“ = „Rejected by {0}“ (Verlauf
der Change-Entscheidung; ohne diese Einträge hätte „{0} von {1}“ in der Suite „Approved of Anna“ ergeben). Kanban, ITSM und Suite brauchen „von {0}“ nicht
(ihre Texte mit „von“ am Anfang haben eigene Einträge; Protokoll-Einzelheiten wie „von anna“ bleiben wie protokolliert). (2) sprache.js: „schwache“ Muster
(fester Text = genau ein kleingeschriebenes Wort mit 2–5 Buchstaben: „von {0}“, „{0} von {1}“, „bis {0}“, „noch {0}“, „{0} neu“ …) übergehen in
musterSuchen(s, tiefe, streng=true) Platzhalterwerte mit TRENNER oder Satzgrenze (zuGross; UEBERGANGEN zählt); nachschlagen teilt dann zuerst auf (DEKO,
Trenner, Sätze) und sucht nur, wenn etwas übergangen wurde, am Ende noch einmal ohne Einschränkung (altes Verhalten). Folge: „von Hand angelegt · …“,
„… – Anna“ → „created by hand …“, „noch 3 Tage · frei“ → „3 days left · free“ (vorher „3 days · free left“); sonst gleich. Regel für Übersetzer in
sprache/UEBERSETZEN.md, Laufzeit in sprache/LIESMICH.md. Testfall Q-28, tests/pruefstand/muster-en-faelle.json (pruefen = 16 Fälle mit kleinem Wörterbuch,
pruefenWerkzeuge = echte Wörterbücher von 8EM, Kunden, ITSM, Kanban, Suite). Kette: editoren-version.py (Sicherung *_16-48-46_editoren, keine neuen Builds),
editor-sprache.py (*_16-49-10_editor-sprache), sprache.py suite, mc4y-suite/bauen.py, paket_bauen.py. GEPRÜFT: Q-28 beide Teile leer, Q-26 69/69, Q-27 0 Fehler,
`sprache.py pruefen` 0 Fehler, statisch 0/0, englischer Rauchtest aller 52 Werkzeuge vorher/nachher (mobil=0, export=0): je 0 Fehler, 10 mit Warnungen,
64 Reste – Werkzeug für Werkzeug identisch (nur Namen/Anschriften aus den Beispielen). Mit der alten Laufzeit ergaben dieselben 16 Fälle dreimal das falsche
Ergebnis (verschluckte Trenner), sonst gleich. NICHT gemacht: FreeHTML-Editor (eigene Kopie von sprache.js in freehtml-editor/quellen/sprache/ – dort kopieren
und bauen), Kopie website-test/downloads/experimentell/software/mc4y-editor.html (nur auf Peters Wunsch tauschen), Upload, installierte Suiten.

## Erkenntnisse aus der bisherigen Arbeit

- Der X5-Blog liefert Beitragstexte nur per JavaScript und setzt auf allen Beitragsseiten Canonical auf
  /blog/ – Inhalte waren daher nicht automatisch übernehmbar und für Google kaum sichtbar.
- Alte Seite: fremde Incomedia-Social-Links, unzulässiger Cookie-Hinweis, Google Fonts, Impressum mit
  falscher „Aufsichtsbehörde“ – in der neuen Seite behoben.
- Frühere Fehler, die nicht wieder auftreten dürfen: build.py löschte ganz `website/blog/` (jetzt nur
  erzeugte Dateien); Testmodus erzwang HTTPS; direkte Änderungen gingen beim Bauen verloren.
