Files
mindflux/mindfluxrefine2.md
T
AliceDarkest 3bc6c8f75c
mindflux-ci / build (push) Successful in 36s
Refine Light Mode, UX und Mail-Diagnose
2026-07-22 13:04:25 +02:00

24 KiB
Raw Blame History

Mindflux — Refine 2: Light-Mode-Konzept, Scroll-Verhalten, Restpunkte

Kontext

Folgeplan zu mindfluxrefine.md. Die Code-Verifikation (2026-07-21) bestätigt: alle „vollständig"-Claims aus Refine 1 halten der Prüfung stand; offen sind AP 1e (Glow im Light Mode), der AP-7-Rest sowie einige Zusatzfunde (s. AP R5). Dieser Plan liefert das, was Refine 1 offen ließ: ein echtes Light-Mode-Konzept, das alle Probleme an der Wurzel behebt (statt Glow-Alphas weiter zu dimmen), plus das neue Thema Scroll-Verhalten (Ergebnisanzeigen u. ä. müssen oben starten, nicht unten).

Audit-Kurzbefunde (2026-07-21, drei unabhängige Durchgänge):

  • Light-Mode-Strukturfehler: Der Light-Block in theme.css tauscht nur Farbwerte/Alphas der Glow-Tokens. Die Geometrie der Effekte (box-shadow:0 0 20px …, text-shadow:0 0 12px …, drop-shadow(0 0 8px …)) steht hart in ~70 Komponentenregeln quer durch theme.css, alle Spiele-CSS und Template-<style>-Blöcke. Ein Halo mit reduziertem Alpha bleibt ein Halo — auf Fast-Weiß liest er sich als Schmutz/Haze. Einzige strukturell adaptierte Stelle heute: --g-soft (0 0 18px dark → 0 6px 18px light, theme.css:31/53). Genau eine Regel neutralisiert einen Glow explizit im Light Mode (auth.css:127–128).
  • 5 im Light Mode kaputte Stellen: Login-Overlay weiß-auf-weiß (auth.css:123–127 × platform-neural.css:94), SyntaxSaga-Bühne bleibt schwarz (syntaxsaga.css:250–251), CodeEscape-Eingaben dunkel-auf-dunkel (codeescape.css .esc-input #080604), NetWorker-Guide-Panel dunkelnavy mit Light---muted (networker-guide.css), Farbenblind-Palette × Light = Hellgelb/Hellblau auf Weiß (accessibility.css:15–23, !important schlägt die Light-Kartenakzente).
  • Theme-Bootstrap-Lücken: color-scheme fehlt komplett (native Scrollbars/Form-Controls folgen dem UA-Default), kein theme-color-Meta, kein prefers-color-scheme-Fallback (Erstbesucher mit OS-Light starten dunkel), alle Templates hardcoden data-theme="dark".
  • Scroll: Im gesamten Projekt existiert kein einziger scrollTo/scrollIntoView/scrollRestoration-Aufruf (außer Chat-Autoscroll neuralnet.js:84, der korrekt ist). Die vier SPA-Spiele (BugHunter, CodeEscape, NetWorker, ServiceClash) ersetzen Views per innerHTML ohne Reset; der FinalProtocol-Quiz kollabiert am Ende einer langen Lernseite (Browser clampt ans neue Seitenende); SyntaxSaga/DevLab fokussieren Inputs ohne preventScroll.
  • CSS-Leichen: achievements.css, progress.css, settings.css, soziales.css werden nirgends mehr referenziert (repo-weiter Grep leer); style.css wird nur noch von finalflux.html geladen, ~90 % davon ist toter Dark-Code.

Basis-Pfad: RES = src\main\resources


Das Konzept: „Licht statt Leuchten"

Prinzip: Dark Mode und Light Mode sind Geschwister mit derselben Farbidentität, aber unterschiedlicher Physik:

Dark Mode Light Mode
Metapher Neon in der Nacht — Elemente emittieren Licht Druck/Material am Tag — Elemente werfen Schatten
Betonung farbiger Glow (Halo) Elevation (weicher, warm getönter neutraler Schatten)
Identität/Wiedererkennung Glow-Farbe Akzentfarbe direkt am Element (Border, Text, Icon) — ist durch --border-accent, Icon-Farben und Kartenakzente bereits überall vorhanden
Text-Halos ja (text-shadow) nie (liest sich als Unschärfe)
Fokus-Ringe farbig, weich farbig, crisp (Ring statt Blur — funktional, bleibt farbig)
Scrims/Overlays dunkel dunkel (funktional), aber leicht entschärft

Mechanik: Effekte werden zu Primitives — komplette box-shadow/filter/text-shadow-Werte als Tokens, nicht nur Farben. Komponenten schreiben nie mehr 0 0 20px var(--glow-s), sondern var(--g-strong). Der Light-Block tauscht dann die Kategorie des Effekts (Glow → Elevation), nicht nur die Farbe. Dark bleibt visuell gleich (Blur-Radien werden auf die Primitive normalisiert; ±4 px sind nicht unterscheidbar). --glow-s/--glow-w bleiben als Farb-Tokens bestehen — die Spiele-CSS remappen sie bereits per color-mix auf ihre Akzente, und die Primitives referenzieren sie per var(), sodass Spiel-Glows im Dark Mode automatisch die Spielfarbe behalten.


AP R0 — Aufräumen (erledigt 2026-07-22)

  1. Erledigt: Verwaiste CSS gelöscht: achievements.css, progress.css, settings.css, soziales.css. Die Referenzprüfung ergab 0 Treffer; STYLEGUIDE bereinigt.
  2. Erledigt: style.css auf eine vierzeilige Basis für die 40 statischen FinalProtocol-Lernseiten reduziert; FinalProtocol-Hub lädt sie nicht mehr.
  3. Erledigt: Nicht eingebundenes finalflux-check.js entfernt. fluxtimer.css bleibt separat offen, da die Datei in diesem Paket nicht geändert wurde.

AP R1 — Effekt-Primitives (Kern des Light-Konzepts)

R1a. Tokens in RES\static\css\theme.css — erledigt 2026-07-22

In den Dark-Block (Z. 11–32) und Light-Block (Z. 33–54) ergänzen. Schattenfarbe Light ist warmes Braun rgb(67,48,26) (zur Creme-Palette passend, kein totes Grau/Schwarz):

:root, [data-theme="dark"] {
  /* … bestehende Tokens … */
  color-scheme:dark;
  --g-text:0 0 12px var(--glow-s);              /* Text-Halo: Logo, Headline-Akzent, *-brand b   */
  --g-icon:drop-shadow(0 0 8px var(--glow-s));  /* leuchtende Glyphen/Embleme                    */
  --g-strong:0 0 20px var(--glow-s);            /* starke Betonung: aktive Nav/Tabs, Primär-Btn  */
  --g-amb:0 0 14px var(--glow-w);               /* dezenter Schein: Icon-Hover, Toggle, Burger   */
  --g-card:drop-shadow(0 0 7px var(--glow-w));  /* Spielkarten-Grundzustand                      */
  --g-card-hover:drop-shadow(0 0 16px var(--glow-s));
  --g-overlay:0 24px 60px rgba(0,0,0,.5);       /* schwebende Panels: Guide, Drawer, A11y        */
  --g-focus:0 0 0 3px var(--glow-w);            /* Fokus-Ring (bleibt in beiden Themes farbig)   */
  --scroll-thumb:var(--line);
}
[data-theme="light"] {
  /* … bestehende Tokens … */
  color-scheme:light;
  --bg-glow: radial-gradient(900px 420px at 60% -10%, rgba(214,81,22,.05), transparent 60%); /* statt .10 — Haze halbieren */
  --g-text:none;
  --g-icon:none;
  --g-strong:0 2px 10px rgba(67,48,26,.18), 0 1px 3px rgba(67,48,26,.10);
  --g-amb:0 1px 5px rgba(67,48,26,.10);
  --g-card:drop-shadow(0 2px 5px rgba(67,48,26,.10));
  --g-card-hover:drop-shadow(0 8px 18px rgba(67,48,26,.16));
  --g-overlay:0 18px 44px rgba(67,48,26,.20);
  --g-focus:0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent);
  --scroll-thumb:#d9cbb2;   /* --line #f1e8d8 ist auf #fbf6ec unsichtbar */
}

Scrollbar-Regel theme.css:64 auf background:var(--scroll-thumb) umstellen.

R1b. Mechanische Migration in theme.css

Zeile Selektor Alt Neu
79 .mf-logo .mark drop-shadow(0 0 8px var(--glow-s)) filter:var(--g-icon)
80 .mf-logo .txt b text-shadow:0 0 12px var(--glow-s) text-shadow:var(--g-text)
87 .mf-nav a.active 0 0 20px var(--glow-s) box-shadow:var(--g-strong)
94 .mf-avatar 0 0 14px var(--glow-s) box-shadow:var(--g-strong) (gleiche Farbstärke-Familie wie heute)
102/104 .mf-streak .fire / .mf-brain drop-shadows filter:var(--g-icon)
109 .mf-hello h1 .nm text-shadow:0 0 16px var(--glow-s) text-shadow:var(--g-text)
113/120/202 iconbtn-Hover / .mf-theme / .mf-burger 0 0 14px var(--glow-w) box-shadow:var(--g-amb)
128/130 .mf-card / :hover drop-shadow 7/16px filter:var(--g-card) / var(--g-card-hover)
131 .mf-card .ico drop-shadow filter:var(--g-icon)
136 .mf-card:hover .mf-play 0 0 16px var(--glow-s) box-shadow:var(--g-strong)
146 .mf-panel.accent 0 0 20px var(--glow-w) box-shadow:var(--g-amb)
151 .mf-bar > i 0 0 10px var(--glow-s) eigenes Primitive --g-bar (dark 0 0 10px var(--glow-s) / light none) — der Balken trägt seine Farbe selbst, im Light braucht er keinen Hof
154 .mf-btn 0 0 18px var(--glow-s) box-shadow:var(--g-strong)
168 Input-Fokus 0 0 0 3px var(--glow-w) box-shadow:var(--g-focus)
182 .mf-cardbox 0 0 30px var(--glow-w) Hero-Aura: Zweizeiler — Basis box-shadow:var(--g-strong), plus [data-theme="dark"] .mf-cardbox{box-shadow:0 0 30px var(--glow-w)}
223 Mobile .mf-sidebar 0 18px 50px rgba(0,0,0,.55) box-shadow:var(--g-overlay)
238 .mf-tab.active 0 0 18px var(--glow-s) box-shadow:var(--g-strong)

R1c. platform-neural.css

Hat als einzige Datei schon gute Struktur-Overrides (Z. 91 ersetzt Schwarz- durch Grünschatten — Vorbild). Nachziehen: Z. 87 Logo-Drop-Shadow im Light auf filter:none; Z. 90 äußeren 0 0 13px-Glow der aktiven Nav im Light streichen (Inset-Ring reicht). Die --glow-s-Kartenwerte (Z. 41–49/99–107) bleiben — sie speisen nach R1b nur noch Dark-Effekte.

R1d. Spiele-CSS + Guide/Widget-CSS migrieren

Gleiches Muster in allen Dateien; die Spiel---glow-s-Remaps (z. B. mindmatch.css:364) bleiben unverändert und speisen die Primitives automatisch mit der Spielfarbe. Betroffene Stellen (Audit-Inventar):

  • Text-Halos → text-shadow:var(--g-text): syntaxsaga.css:40, mindmatch.css:53, techtacular.css:48, devlab.css (.dl-tube span), devlabgame.css (Header-Titel), codeescape.css (.esc-brand b, .esc-result .score), bughunter.css (.bh-brand b, Stamp), networker.css (.nw-brand b, .nw-final-score), serviceclash.css (.sc-brand b, .sc-score), auth.css:74 (.auth-head b), style.css:89 (falls nach R0 noch vorhanden), Template-<style>: progress.html:14 (Prozentzahl).
  • Box-Glows → var(--g-strong)/var(--g-amb) nach Gewicht: syntaxsaga.css:37/70/100/125/136/200/229/233, mindmatch.css:40/106/226/237/280/287/299/324, techtacular.css:39/75/174/207/299, devlab.css:102/158/173, devlabgame.css:10/12/23/49/61/70/78/106/108, codeescape.css (Buttons/Dialog/Hero — Hero-0 0 50px als Dark-only-Zweizeiler wie .mf-cardbox), codeescape-guide.css (.esc-how .active, .esc-door.ready, .esc-input.bad), bughunter.css, networker.css, serviceclash.css (.sc-meter i), fluxtimer.css:6/7, accessibility.css:29, accessibility-placement.css:2/16, auth.css:14/34/88/98 (.neon-btn inkl.: den ::before-Conic-Blur-Rotor Z. 95/99 im Light per display:none).
  • Fokus-Ringe → var(--g-focus): syntaxsaga.css:160, devlabgame.css:35/39, CodeEscape-Inputs.
  • Schwarz-Schatten → var(--g-overlay) bzw. warm getönt: game-guide.css:72, accessibility.css:30, accessibility-placement.css:17, fluxtimer.css:8 (+ Z. 14 inset-Schwarz auf hellem Display im Light entfernen), serviceclash.css (.sc-card), bughunter.css (Drawer, dort auch die im Light unsichtbaren rgba(255,255,255,.09–.16)-Inset-Highlights auf Light-Varianten umstellen).
  • Scrims (funktional, bleiben dunkel, aber vereinheitlichen): mindmatch.css:309, techtacular.css:192, syntaxsaga.css:216, devlabgame.css:104 — im Light auf das Muster game-guide.css:138 (rgba(15,23,42,.5)) wechseln, damit sich nicht zusätzlich der warme --bg-glow-Radial über den Scrim legt (var(--bg-glow) aus den Scrim-Deklarationen entfernen).
  • Hint-Glow game-guide.css:155/160–161 (#ffd54a): Light-Override [data-theme="light"] .mm-card.hinted { box-shadow:0 0 0 2px #8a6a06, 0 4px 12px rgba(67,48,26,.18); filter:none; } (Warn-Ton aus dem Light-Block).

R1e. Chart/Dashboard-<style>-Blöcke

dashboard.html:14/16/18/20/29/31/43/46/171, achievements.html:12–16, admin/dashboard.html:17–19, impressum.html:12, profile.html:11: alle text-shadow/drop-shadow/0 0 Npx-Vorkommen auf die Primitives umstellen (Suchmuster: grep -n "glow\|text-shadow\|drop-shadow" RES\templates -r).


AP R2 — Kaputte Light-Stellen (kritische Punkte erledigt 2026-07-22)

R2a. Login/Registrierung: Overlay weiß-auf-weiß (kritisch)

Ursache: auth.css:123–127 setzt für Light #fff-Text/weiße Ghost-Border (Annahme: kräftiges Orange-Panel), aber auf der Plattform übersteuert platform-neural.css:94 den Overlay-Grund mit Fast-Weiß. Fix in platform-neural.css (Light-Sektion, höhere Spezifität durch .sn-platform gewinnt gegen auth.css):

html[data-theme="light"].sn-platform body .overlay-panel h3 { color:#10291f; text-shadow:none; }
html[data-theme="light"].sn-platform body .overlay-panel p  { color:#3c554a; }
html[data-theme="light"].sn-platform body .ghost-btn        { color:#167a52; border-color:rgba(22,122,82,.55); }
html[data-theme="light"].sn-platform body .ghost-btn:hover  { background:rgba(22,122,82,.10); }

R2b. SyntaxSaga: Bühne bleibt schwarz (kritisch)

syntaxsaga.css:250–251 hardcodet #07100b + grüne Scanlines. Light-Block ergänzen (Palette: helles Bühnen-Grün zur Light---ss-accent #277d49):

[data-theme="light"] body.ss-body {
  background:radial-gradient(1100px 500px at 50% -10%, rgba(39,125,73,.08), transparent 60%), #eef6f0;
}
[data-theme="light"] body.ss-body::before {
  background:repeating-linear-gradient(0deg, rgba(16,64,40,.05) 0 1px, transparent 1px 4px);
}

R2c. CodeEscape: Terminal-Eingaben & Dark-Inks (kritisch)

  • .esc-input, .esc-select (#080604): Light-Override [data-theme="light"] .esc-input, [data-theme="light"] .esc-select { background:#fffaf0; color:var(--text); border-color:var(--border); } — Dark behält den Terminal-Look.
  • Hardcodierte Dark-Inks auf Akzentflächen durch das Token ersetzen (funktioniert in beiden Themes): .esc-btn:hover/.primary #160d00 → var(--accent-ink); codeescape-guide.css .esc-fragment small #160d00 → var(--accent-ink); .esc-how .done b #06120a → var(--accent-ink).
  • Tote Scanlines rgba(255,255,255,.018) (codeescape.css, theme.css:138): im Light-Override auf rgba(67,48,26,.04) drehen oder streichen.

R2d. NetWorker-Guide: dunkles Panel (kritisch)

networker-guide.css (.nw-guide color-mix(#0b2540 74%, var(--surface))): Light-Block ergänzen:

[data-theme="light"] .nw-guide { background:var(--surface); border-color:#d3e2f0; box-shadow:var(--g-soft); }

(Textfarben laufen dort über var(--muted)/var(--text) und stimmen dann automatisch.)

R2e. Farbenblind-Palette × Light (kritisch)

accessibility.css:15–23 erzwingt helle Okabe-Ito-Akzente per !important — auf weißen Karten < 1.5:1. Neuen Satz html[data-theme="light"][data-mf-color="safe"] mit abgedunkelten Okabe-Ito-Entsprechungen ergänzen (gleiche Zuordnung Spiel→Farbfamilie wie im Dark-Satz, Werte rechnerisch ≥ 4.5:1 auf Weiß, beim Umsetzen mit Kontrast-Tool gegenprüfen):

Okabe-Ito (dark) Light-safe
#e69f00 Orange #8a5f00
#56b4e9 Himmelblau #19607f
#009e73 Grün #007054
#f0e442 Gelb #7a6a00
#0072b2 Blau #0072b2 (bleibt)
#d55e00 Vermillion #a94500
#cc79a7 Purpur #9c4f79

Die zugehörigen Glow-Alphas entfallen im Light automatisch über die Primitives (R1).

R2f. Kleinere Light-Fixes

  1. Cancel-Hover mindmatch.css:128–129, techtacular.css:94 (#fff auf Blassrosa): Light-Override [data-theme="light"] .mm-cancel:hover, … → background:var(--danger); color:#fff; (Light-Danger #be123c, 7:1). Dark bleibt unverändert.
  2. A11y-Widget-Inks: accessibility-placement.css:7–15 hardcodet dunkle --mf-a11y-ink-Werte — im Light sind die Akzente selbst dunkel. Eine Regel ergänzt: html[data-theme="light"] body { --mf-a11y-ink:#fff; } (gewinnt per Spezifität gegen die Skin-Zuweisungen). Panel-Kopf optional angleichen: [data-theme="light"] .mf-a11y-head { background:var(--surface-2); color:var(--heading); }.
  3. finalflux-Altlast: finalflux.css:345 (#0b0d1b-Hardcode) funktioniert nur über die fragile background:transparent-Kaskade aus platform-neural.css:61. Hardcode durch var(--bg)-basierte Deklaration ersetzen, damit die Seite auch ohne sn-platform beide Themes überlebt.

Erledigt: Login-Overlay, SyntaxSaga-Bühne, CodeEscape-Eingaben und Akzent-Inks, NetWorker-Guide, Farbenblind-Palette für Light Mode, A11y-Widget-Inks sowie der FinalProtocol-Hintergrund. Die noch offenen kleineren Cancel-Hover- und Detaileffekte werden zusammen mit der vollständigen CSS-Migration aus R1 abgeschlossen.


AP R3 — Theme-Bootstrap (erledigt 2026-07-22)

In RES\static\js\theme.js:

  1. Erledigt: prefers-color-scheme wird verwendet, wenn noch keine lokale Auswahl gespeichert ist.
  2. Erledigt: theme-color wird beim initialen Anwenden und Umschalten auf #0a0806 bzw. #fbf6ec aktualisiert.
  3. Erledigt: color-scheme wird im Dark-/Light-Tokenblock gesetzt.
  4. Teilweise erledigt: geänderte FinalProtocol-Assets tragen neue Versionen; die übrigen Template-Referenzen werden bei ihrer jeweiligen Migration angehoben.

AP R4 — Scroll: Ergebnisanzeigen & Ansichten starten oben (weitgehend erledigt 2026-07-22)

Es gibt projektweit keinen Scroll-Reset. Drei Schichten (zentraler Helper + gezielte Aufrufe), Chat-Autoscroll neuralnet.js:84 bleibt unangetastet:

R4a. Helper in theme.js (läuft auf jeder Seite)

window.MindfluxUX = {
  toTop: function () { window.scrollTo(0, 0); },
  focusQuiet: function (el) { if (el) el.focus({ preventScroll: true }); }
};

Umsetzungshinweis: Der zentrale Helper ist vorhanden. Die vier SPA-Spiele setzen history.scrollRestoration nur auf Spielrouten auf manual; ein zentraler, ereignisbasierter Übergangs-Reset deckt Ansichtswechsel ab und lässt Antwort- bzw. Zeilenfeedback unverändert. FinalProtocol, SyntaxSaga, DevLab, MindMatch und TechTacular haben die zusätzlich beschriebenen gezielten Resets/Fokusoptionen. Offen für die visuelle E2E-Prüfung: die asynchron gerenderten Ergebniswechsel von CodeEscape, NetWorker und BugHunter müssen im Browser gegen die Scroll-Checkliste validiert und bei Bedarf direkt in den jeweiligen Render-Funktionen präzisiert werden.

R4b. SPA-Spiele — Reset bei jedem Ansichtswechsel

Muster pro Spiel: Mini-Wrapper function setView(html){ view.innerHTML = html; window.scrollTo(0,0); }, alle Render-Funktionen darüber. Zusätzlich nur in diesen vier Skripten beim Init: if ('scrollRestoration' in history) history.scrollRestoration = 'manual'; window.scrollTo(0,0); — verhindert, dass der Browser nach Reload/Back die alte, untere Position auf den neu aufgebauten Katalog anwendet. (Nicht global in theme.js: auf Lernseiten/Dashboard ist die Back-Position erwünscht.)

Spiel Funktionen
BugHunter (bughunter.js) renderBriefing :29, renderHunt :36 (nur bei Fallwechsel, nicht bei Zeilen-Re-Render), showDebrief :82, renderResult :88
CodeEscape (codeescape.js) renderLobby :6, renderRoom :8, renderResult :17
NetWorker (networker.js) renderCatalog :3, renderIntro :5, renderLevel :6, finish :11
ServiceClash (serviceclash.js) renderCatalog :2, renderRun :4 (nur bei Ticket-/Indexwechsel, nicht beim Feedback-Re-Render), finish :9

R4c. FinalProtocol-Quiz (finalflux-test.js)

  • finishIfDone() Z. 101–104: nach testResult.hidden = false → elements.testResult.scrollIntoView({ block: 'start' }); (Auswertung beginnt oben im Blick, statt dass der Viewport ans geclampte Seitenende fällt).
  • restartTest() Z. 151–158: nach dem Wiedereinblenden → elements.testRoot.scrollIntoView({ block: 'start' });

R4d. Fokus ohne Scroll-Sprung

  • syntaxsaga.js: renderMission() — window.scrollTo(0,0) vor Z. 209, dann Z. 210 first.focus({preventScroll:true}). (nextBtn.focus() Z. 297 bleibt — Nutzeraktion am selben Ort.)
  • devlabgame.js: renderTask() Z. 243 — window.scrollTo(0,0) am Ende; Z. 83 und Z. 233 focus({preventScroll:true}).
  • codeescape.js:8/15 bleiben (Fokus am Türterminal ist dort korrekt).

R4e. Niedrige Prio (nur Mobile relevant)

mindmatch.js newGame() :332 und techtacular.js newGame() :265: window.scrollTo(0,0) — Desktop-Bodys sind overflow:hidden, mobil scrollt die Seite.


AP R5 — Carry-over: Rest aus Refine 1 + Zusatzfunde der Verifikation

Aus Refine 1 (dort AP 7 / offen):

  1. Bean Validation: @Valid + Constraints auf alle Request-DTOs — BugHunterApiController:17–18, DevLabApiController:37/43, SyntaxSagaApiController:32/38, TechtacularApiController:50/75, CodeEscapeApiController (4 DTOs), NetworkerApiController (2), ServiceClashApiController (2); NeuralNetApiController:14/19 Map<String,String> → typisierte DTOs.
  2. Spring Boot 3.4.4 → aktuelles 3.4.x-Patch (pom.xml:11), Testlauf.
  3. CSRF: Blanket-Exemption /api/** (SecurityConfig:46) durch Cookie-basierte Tokens ersetzen.
  4. Web-Layer-Test für GET /profile/export.

Zusatzfunde (neu): 5. UserController:25/45 bindet die Entity User direkt als @RequestBody ohne Validierung → auf DTO + @Valid umstellen (gehört in die Runde von Punkt 1). 6. 5 Admin-Templates ohne Footer: admin\bughunter.html, admin\bughunter-hunt.html, admin\codeescape.html, admin\codeescape-room.html, admin\codeescape-scenario.html → th:replace="~{fragments/layout :: footer}" ergänzen. 7. HSTS explizit für Prod konfigurieren statt Spring-Default (SecurityConfig:56–57). 8. Optional (unverändert Phase 2): BugHunter progressive Hinweise (Refine 1, AP 4d).


Reihenfolge

  1. AP R0 Aufräumen → 2. AP R1 Primitives + Migration (theme.css zuerst, dann platform-neural, dann Spiele-CSS, dann Templates) → 3. AP R2 kaputte Light-Stellen → 4. AP R3 Bootstrap → 5. AP R4 Scroll (unabhängig von 1–4, parallelisierbar) → 6. AP R5 Carry-over → 7. Gesamtverifikation. STYLEGUIDE.md (Abschnitt „Glow, Schatten & Radien" → Primitives-Tabelle) und CHANGELOG nach R1–R3 nachziehen.

Verifikation

  • Build: mvn test bleibt grün (54/54); App-Boot gegen Wegwerf-Postgres wie gehabt.
  • Grep-Gates nach R1: grep -rnE "0 0 [0-9]+px var\(--" RES\static\css → Treffer nur noch in den Primitive-Definitionen von theme.css; grep -rn "text-shadow:0 0" RES\static\css RES\templates → nur --g-text-Definition; grep -rn "rgba(0,0,0,.5" RES\static\css → nur Scrims/--g-overlay-Definition.
  • Visuell (hell UND dunkel): Dashboard (Karten + Hover), Login/Registrierung (insb. Overlay-Panel!), Settings, Progress, Soziales, NeuralNet, alle 9 Spiele (insb. SyntaxSaga-Bühne, CodeEscape-Eingaben, NetWorker-Guide), FinalProtocol-Lernseite + Quiz, A11y-Panel. Dark Mode muss vor/nach R1 visuell deckungsgleich sein (Stichprobe per Screenshot-Vergleich).
  • A11y-Kombinationen: Light × Hochkontrast (bleibt bewusst dunkel), Light × Farbenblind (neue dunkle Okabe-Ito-Werte sichtbar, Kontrast-Tool ≥ 4.5:1), Light × Mono.
  • Native Controls: Light Mode → helle Scrollbars und helle <select>-Dropdowns (Chrome + Firefox); mobile Adressleiste folgt theme-color.
  • Erstbesuch: localStorage leeren, OS auf Light stellen → Seite startet hell; OS Dark → dunkel; Umschalter persistiert weiter.
  • Scroll: je SPA-Spiel: langes Board nach unten scrollen → Aktion abschließen → Ergebnis-/Folge-View beginnt oben; ServiceClash-Antwort-Feedback und BugHunter-Zeilenauswahl dürfen nicht nach oben springen; FinalProtocol: langen Lernartikel bis zum Quiz scrollen, Quiz abschließen → Auswertung oben im Viewport; Reload mitten im Spiel → Katalog startet oben; SyntaxSaga/DevLab: Missions-/Taskwechsel startet oben, Fokus springt nicht; NeuralNet-Chat scrollt weiterhin ans Ende.

Offene Punkte (unverändert aus Refine 1, vor Launch)

  • Echte Impressumsdaten (impressum.html:44–52) + §1 Datenschutzerklärung.
  • Support-/Feedback-Adressen bestätigen (fragments/layout.html:88–89).

Nachtrag 2026-07-22 — E-Mail-Diagnose (erledigt)

  • Der Versand von Bestätigungs- und Reset-Links protokolliert nun korrelierbare Ereignisse: Token-Ausstellung, SMTP-Übergabe begonnen/angenommen/fehlgeschlagen, erneute Anforderung und erfolgreiche Verifikation. Logs enthalten User-/Token-IDs sowie einen gekürzten E-Mail-Fingerprint, niemals den Link oder das Roh-Token.
  • Wichtig für die Analyse: SMTP_SUBMISSION_ACCEPTED bedeutet nur, dass der konfigurierte SMTP-Server die Nachricht angenommen hat. Spamfilter, Bounces und die finale Postfachzustellung müssen zusätzlich im Mailserver bzw. über DSN geprüft werden.
  • Der Registrierungsabschluss meldet keinen Versand-Erfolg mehr, falls der gerade angelegte Benutzer beim Versand nicht geladen werden kann oder bereits als bestätigt vorliegt; der Grund erscheint als VERIFICATION_SKIPPED im Betriebslog.