diff --git a/.planning/quick/260907-ipz-anrede-vereinheitlichen-duzen-auf-siezen/260907-ipz-PLAN.md b/.planning/quick/260907-ipz-anrede-vereinheitlichen-duzen-auf-siezen/260907-ipz-PLAN.md
new file mode 100644
index 0000000..1217829
--- /dev/null
+++ b/.planning/quick/260907-ipz-anrede-vereinheitlichen-duzen-auf-siezen/260907-ipz-PLAN.md
@@ -0,0 +1,322 @@
+---
+quick_id: 260907-ipz
+title: Anrede vereinheitlichen (Duzen auf Siezen) und Einzahl korrigieren
+type: execute
+wave: 1
+depends_on: []
+autonomous: false
+files_modified:
+ - apps/web/src/messages/de.json
+ - apps/web/src/messages/en.json
+ - apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx
+ - apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx
+ - apps/web/src/components/modules/module-access-gate.test.tsx
+
+estimate:
+ tokens: 22000
+ raw_tokens: 22000
+ tasks: 2
+ confidence: low
+
+must_haves:
+ truths:
+ - "Die vier bisher duzenden deutschen Texte siezen: leere Gruppenliste, Modul-Aktivierungsdialog, Modul-Sperrseite und Marktplatz-Hinweis."
+ - "Kein Wert in de.json enthaelt mehr eine Du-Form (du, dich, dir, dein*)."
+ - "Der Loeschdialog einer Gruppe mit genau einem Mitglied und einer Freigabe schreibt '1 Mitglied' und '1 Modul-Freigabe' — nicht '1 Mitglieder'."
+ - "Dasselbe gilt auf Englisch: '1 member' und '1 module grant'."
+ - "Ab zwei zeigt derselbe Text weiterhin die Mehrzahl."
+ - "de.json und en.json tragen weiterhin denselben Schluesselsatz."
+ - "Alle Tests, die diese Texte festnageln, laufen gruen — keiner wurde geloescht."
+ artifacts:
+ - apps/web/src/messages/de.json
+ - apps/web/src/messages/en.json
+ key_links:
+ - "Die Mock-Uebersetzer in den Tests spiegeln die Texte aus de.json — jede Textaenderung muss dort mitgezogen werden, sonst pinnen die Tests eine Fassung, die es nicht mehr gibt."
+ - "Der Mock-Uebersetzer in groups-page.test.tsx kann heute KEIN ICU-Plural — ohne Erweiterung bricht der Loeschdialog-Test durch die Plural-Umstellung."
+---
+
+
+Zwei Wortlaut-Maengel in der deutschen Oberflaeche, beide vom Nutzer beauftragt und beide am
+Code verifiziert:
+
+1. **Die Anrede springt.** Die Oberflaeche siezt ueberall — an genau vier Stellen duzt sie.
+2. **Die Einzahl ist falsch.** Der Loeschdialog einer Gruppe schreibt "1 Mitglieder und
+ 1 Modul-Freigaben", weil zwei Zahlen in fest formulierte Mehrzahl-Woerter eingesetzt
+ werden. Live beobachtet an der Gruppe "Vertrieb Innendienst".
+
+Purpose: Ein Produkt, das an externe Kunden verkauft werden soll, redet seine Nutzer nicht
+mal so und mal so an und zaehlt nicht falsch.
+
+Output: Vier korrigierte deutsche Texte, ein Loeschdialog-Text mit echter Ein-/Mehrzahl in
+beiden Sprachen, mitgezogene Tests und ein neuer Test, der genau den Einzahl-Fall festnagelt.
+
+Ausdruecklich NICHT Teil dieses Plans: jede andere Umformulierung. Es werden ausschliesslich
+die fuenf hier namentlich genannten Schluessel angefasst. Englisch ist von Defekt 1 nicht
+betroffen — Englisch kennt den Unterschied nicht und ist bereits richtig formuliert.
+
+
+
+@~/.claude/gsd-core/workflows/execute-plan.md
+
+
+
+@CLAUDE.md
+@apps/web/src/app/(portal)/admin/groups/components/DeleteGroupDialog.tsx
+@apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx
+
+
+
+Alles Folgende wurde beim Planen am Code nachgeprueft und erspart dem Executor die Suche.
+
+**1. Die vier Du-Stellen sind vollstaendig — es gibt keine fuenfte.**
+Ein Wortgrenzen-Grep ueber `de.json` nach `du|dich|dir|dein*` trifft heute exakt vier
+Zeilen (400, 464, 492, 630) und nichts sonst. Die Erhebung im Auftrag ist damit
+bestaetigt und abgeschlossen.
+
+| Schluessel | Zeile | heute |
+|---|---|---|
+| `admin.groups.noGroupsBody` | 400 | "Lege deine erste Gruppe an, um Modulzugriff gezielt zu vergeben." |
+| `adminModules.activationDialog.body` | 464 | "… oder moechtest du die Freigaben separat konfigurieren?" |
+| `modules.accessDenied.body` | 492 | "Du hast fuer dieses Modul keine Freigabe. Wende dich an deinen Administrator." |
+| `marketplace.toastNoAccess` | 630 | "Kein Zugriff auf dieses Modul — wende dich an deinen Administrator." |
+
+`marketplace.toastNoAccess` liegt tatsaechlich unter `marketplace` (Zeile 630, direkt vor
+`certManager`) — nicht unter `domaincheck`, obwohl die Nachbarzeilen im Ausschnitt so
+wirken. Genutzt wird er in `app/(portal)/marketplace/page.tsx:178`.
+
+**2. Derselbe Grep trifft in den drei Testdateien exakt vier Zeilen** — allesamt die oben
+gelisteten Texte. Es gibt keine weitere Stelle im `src`-Baum, die diese Texte kopiert.
+
+**3. ICU-Plural ist im Projekt bereits etabliert.** `admin.ldap.…resultSummary`
+(de.json Zeile 365) nutzt `{errors, plural, =0 {} other {, # Fehler}}`. next-intl 4.13
+kann das produktiv ohne jede Zusatzarbeit — es braucht kein neues Mittel, nur dieselbe
+Schreibweise.
+
+**4. Der Loeschdialog uebergibt beide Zahlen bereits als Parameter.**
+`DeleteGroupDialog.tsx:102` ruft `t('deleteConfirm.body', { memberCount, grantCount })`.
+Es ist also nichts am Aufrufer zu aendern — nur am Text.
+
+**5. Der Mock-Uebersetzer in `groups-page.test.tsx` versteht KEIN ICU.**
+Nachgelesen in `resolve()` (Zeile 66–80): er ersetzt stur `{k}` durch den Wert. Sobald
+`deleteConfirm.body` auf Plural-Schreibweise umgestellt wird, findet er `{memberCount}`
+nicht mehr, gibt den ICU-Rohtext aus, und die bestehenden Zusicherungen `/4 Mitglieder/`
+(Zeile 475) und `/2 Modul-Freigaben/` (Zeile 477) schlagen fehl.
+**Das ist keine Nebenwirkung, die man in Kauf nimmt — Task 2 loest es ausdruecklich.**
+
+**6. Fuer die Loesung gibt es im Projekt bereits ein Vorbild.**
+`grants-matrix.test.tsx` (Zeile 48–53) hat genau dieses Problem fuer `select` geloest: ein
+winziger Regex-Ersatz im Mock, der die eine benutzte ICU-Form aufloest. Task 2 spiegelt
+dieses Vorgehen fuer `plural`. Damit bleibt die Mock-Nachricht wortgleich mit `de.json` —
+was der Sinn dieser Mocks ist.
+
+**7. Der Umlaut-Waechter schlaegt bei keiner der neuen Formulierungen an.**
+`umlaut-guard.spec.ts` meldet jedes neue Token mit `ae`/`oe`/`ue`/`ss`, das nicht auf der
+Positivliste steht. Die geplanten Formulierungen wurden gegen genau diese Regel geprueft:
+kein einziges verdaechtiges Token. Es ist also **kein** Eintrag in `umlaut-dictionary.ts`
+noetig.
+
+**8. de.json und en.json haben heute identische Schluesselsaetze** (maschinell verglichen,
+beide Richtungen leer). Dieser Plan aendert ausschliesslich Werte, niemals Schluessel.
+
+
+
+**Ja, der Einzahl-Test wird angelegt.** Begruendung, weil der Auftrag sie verlangt:
+
+Der Fehler war ueber Monate im Code und kein Test hat ihn bemerkt — weil beide bestehenden
+Loeschdialog-Tests mit `memberCount: 4, grantCount: 2` laufen, also nur im Mehrzahl-Fall.
+Genau die eine Zahl, bei der der Text falsch wird, war nie abgedeckt. Ein Test, der nach
+der Korrektur nur die Mehrzahl prueft, wuerde denselben Rueckfall wieder durchlassen.
+
+Der Aufwand ist zudem praktisch null: `buildRouterFetchMock` nimmt `impact` bereits als
+Parameter entgegen (Zeile 393/412), der neue Fall ist ein Aufruf mit `{ memberCount: 1,
+grantCount: 1 }` und zwei Zusicherungen. Und der Test hat einen zweiten Nutzen: er ist der
+Beweis, dass die Mock-Erweiterung aus Task 2 das ICU-Plural wirklich auswertet — ohne ihn
+koennte der Mock stillschweigend falsch bleiben, solange nur mit 4 und 2 geprueft wird.
+
+
+
+Keine der fuenf bestehenden Test-Fundstellen wird entfernt. Sie werden **mitgezogen**: die
+Mock-Nachrichten und die Zusicherungen tragen danach den neuen Wortlaut. Ein Test, der
+eine Formulierung festnagelt, ist nach einer Formulierungsaenderung nicht wertlos, sondern
+schuetzt die neue Fassung.
+
+
+
+
+
+ Task 1: Anrede auf Siezen vereinheitlichen (Defekt 1)
+ apps/web/src/messages/de.json, apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx, apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx, apps/web/src/components/modules/module-access-gate.test.tsx
+
+Setze in `apps/web/src/messages/de.json` genau vier Werte auf die Sie-Form. Ausschliesslich
+diese vier Schluessel werden angefasst, kein weiterer Text in der Datei wird umformuliert.
+
+- `admin.groups.noGroupsBody` (Zeile 400) wird zu:
+ Legen Sie Ihre erste Gruppe an, um Modulzugriff gezielt zu vergeben.
+- `adminModules.activationDialog.body` (Zeile 464) wird zu:
+ Soll die Standardgruppe sofort Zugriff auf dieses Modul erhalten, oder moechten Sie die
+ Freigaben separat konfigurieren? — mit korrektem Umlaut in "moechten" geschrieben.
+- `modules.accessDenied.body` (Zeile 492) wird zu:
+ Sie haben fuer dieses Modul keine Freigabe. Wenden Sie sich an Ihren Administrator. —
+ mit korrekten Umlauten in "fuer".
+- `marketplace.toastNoAccess` (Zeile 630) wird zu:
+ Kein Zugriff auf dieses Modul — wenden Sie sich an Ihren Administrator. — der
+ Geviertstrich bleibt unveraendert erhalten.
+
+Alle vier Werte werden mit echten Umlauten geschrieben (`ü`, `ö`); die Ersatzschreibweisen
+in dieser Aufgabenbeschreibung sind nur der Klartext dieses Plans.
+
+`en.json` wird in dieser Aufgabe NICHT angefasst — Englisch kennt die Unterscheidung nicht
+und ist bereits richtig formuliert. Es werden keine Schluessel hinzugefuegt, umbenannt oder
+entfernt.
+
+Ziehe anschliessend die drei Testdateien nach, damit ihre Mock-Nachrichten und
+Zusicherungen wieder wortgleich zu `de.json` sind. Kein Test wird geloescht oder
+uebersprungen — nur der Wortlaut wird aktualisiert:
+
+- `grants-matrix.test.tsx` Zeile 27 — Mock-Nachricht `adminModules.activationDialog.body`.
+- `groups-page.test.tsx` Zeile 15 — Mock-Nachricht `admin.groups.noGroupsBody`.
+- `groups-page.test.tsx` Zeile 185 — die Zusicherung, die denselben Text im Leerzustand
+ sucht. Sie muss buchstabengleich zur Mock-Nachricht aus Zeile 15 bleiben.
+- `module-access-gate.test.tsx` Zeile 27 — Eintrag `accessDenied.body` in der
+ handgepflegten Uebersetzungstabelle.
+
+
+ cd /home/vicolab/projects/tessera-ctl && ! grep -nE '\b([Dd]u|dich|dir|dein(e|en|er|em|es)?)\b' apps/web/src/messages/de.json 'apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx' 'apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx' apps/web/src/components/modules/module-access-gate.test.tsx && grep -q 'Wenden Sie sich an Ihren Administrator' apps/web/src/messages/de.json
+ cd /home/vicolab/projects/tessera-ctl && pnpm --dir apps/web test -- groups-page.test.tsx grants-matrix.test.tsx module-access-gate.test.tsx umlaut-guard.spec.ts
+
+
+`de.json` enthaelt in keinem Wert mehr eine Du-Form; dieselbe Pruefung ueber die drei
+Testdateien ist ebenfalls leer. Die vier Texte lauten auf Sie/Ihr. `en.json` ist
+unveraendert. Alle vier genannten Testdateien laufen gruen, keiner der fuenf bisherigen
+Pruefpunkte wurde entfernt.
+
+
+
+
+ Task 2: Ein-/Mehrzahl im Loeschdialog korrigieren (Defekt 2)
+ apps/web/src/messages/de.json, apps/web/src/messages/en.json, apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx
+
+ - Bei einem Mitglied und einer Freigabe lautet der deutsche Text "… hat 1 Mitglied und
+ 1 Modul-Freigabe. …"; die Zeichenfolge "1 Mitglieder" kommt nicht vor.
+ - Bei vier Mitgliedern und zwei Freigaben lautet er weiterhin "… hat 4 Mitglieder und
+ 2 Modul-Freigaben. …" (die beiden bestehenden Tests bleiben gruen).
+ - Auf Englisch gilt dasselbe: "1 member" / "1 module grant" gegen "4 members" /
+ "2 module grants".
+ - Der Rest des Satzes (Folgen des Loeschens, Unwiderruflichkeit) bleibt in beiden
+ Sprachen unveraendert.
+
+
+Schreibe zuerst die Mock-Erweiterung und den neuen Einzahl-Test, dann die Textaenderung —
+der neue Test muss vor der Textaenderung rot sein.
+
+Schritt A — `resolve()` in `groups-page.test.tsx` (Zeile 66–80) um eine ICU-Plural-Aufloesung
+erweitern, nach demselben Muster, mit dem `grants-matrix.test.tsx` (Zeile 48–53) bereits
+`select` aufloest. Vor der bestehenden Schleife, die schlichte `{k}`-Platzhalter ersetzt,
+laeuft ein globaler Ersatz mit dem Muster
+`/\{(\w+),\s*plural,\s*one\s*\{([^}]*)\}\s*other\s*\{([^}]*)\}\}/g`.
+Fuer jeden Treffer wird der Zweig `one` gewaehlt, wenn `Number(params[name]) === 1`, sonst
+der Zweig `other`; im gewaehlten Zweig wird danach jedes `#` durch `String(params[name])`
+ersetzt. Die bestehende schlichte Ersetzungsschleife bleibt unveraendert dahinter stehen —
+sie greift weiterhin fuer alle anderen Schluessel wie `adDnLabel`. Aktualisiere den
+Kopfkommentar der Datei so, dass er die nun ebenfalls aufgeloeste Plural-Form erwaehnt.
+
+Schritt B — neuer Test im Block `DeleteGroupDialog (via AdminGroupsPage)`, direkt nach dem
+bestehenden Test, der beide Zahlen prueft. Er nutzt `buildRouterFetchMock` mit
+`impact: { memberCount: 1, grantCount: 1 }`, oeffnet den Loeschdialog ueber denselben Weg
+wie der Nachbartest (letzte Loeschen-Schaltflaeche) und sichert zu, dass der Dialogtext
+`/hat 1 Mitglied und 1 Modul-Freigabe\./` enthaelt. Zusaetzlich eine Gegenprobe: die
+Mehrzahlform mit vorangestellter Eins darf nicht im Dokument sein
+(`queryByText(/1 Mitglieder/)` ist `null`). Der Test muss an dieser Stelle fehlschlagen —
+das ist der Nachweis, dass er den Defekt wirklich trifft.
+
+Schritt C — `admin.groups.deleteConfirm.body` in beiden Sprachdateien auf ICU-Plural
+umstellen, in derselben Schreibweise wie das bestehende `admin.ldap.…resultSummary`.
+Beide Zahlen bekommen je ein eigenes `plural`-Argument mit den Zweigen `one` und `other`,
+die Zahl selbst wird im Zweig als `#` gesetzt:
+
+- `de.json` Zeile 433: erstes Argument `memberCount` mit den Zweigen Mitglied / Mitglieder,
+ zweites Argument `grantCount` mit den Zweigen Modul-Freigabe / Modul-Freigaben. Der
+ Satzanfang "Diese Gruppe hat" und die beiden Folgesaetze bleiben Wort fuer Wort stehen.
+- `en.json` Zeile 433: dieselbe Struktur mit member / members und module grant /
+ module grants. "This group has" und die beiden Folgesaetze bleiben unveraendert.
+
+Ziehe abschliessend die Mock-Nachricht in `groups-page.test.tsx` Zeile 49–50 nach, sodass
+sie buchstabengleich zum neuen Wert aus `de.json` ist — die Erweiterung aus Schritt A wertet
+sie dann korrekt aus. Es werden keine Schluessel hinzugefuegt oder entfernt; beide
+Sprachdateien behalten denselben Schluesselsatz.
+
+
+ cd /home/vicolab/projects/tessera-ctl && test "$(grep -c 'memberCount, plural' apps/web/src/messages/de.json)" = "1" && test "$(grep -c 'memberCount, plural' apps/web/src/messages/en.json)" = "1" && test "$(grep -c 'grantCount, plural' apps/web/src/messages/de.json)" = "1" && test "$(grep -c 'grantCount, plural' apps/web/src/messages/en.json)" = "1"
+ cd /home/vicolab/projects/tessera-ctl && node -e "const a=require('./apps/web/src/messages/de.json'),b=require('./apps/web/src/messages/en.json');const f=(o,p='')=>Object.entries(o).flatMap(([k,v])=>typeof v==='object'&&v?f(v,p+k+'.'):[p+k]);const A=f(a).sort().join('|'),B=f(b).sort().join('|');if(A!==B){console.error('key sets differ');process.exit(1)}console.log('key parity ok:',f(a).length)"
+ cd /home/vicolab/projects/tessera-ctl && pnpm --dir apps/web test -- groups-page.test.tsx
+
+
+Beide Sprachdateien tragen fuer `memberCount` und `grantCount` je ein `plural`-Argument.
+Der Schluesselsatz beider Dateien ist weiterhin identisch (787). In `groups-page.test.tsx`
+laufen alle Loeschdialog-Tests gruen: die beiden bestehenden mit 4/2 und der neue mit 1/1,
+der ohne die Textkorrektur rot war. Der Mock-Uebersetzer loest das Plural-Argument auf,
+statt den Rohtext auszugeben.
+
+
+
+
+
+
+**Maschinell** (laeuft in beiden Aufgaben, hier nochmal als Gesamtlauf):
+
+- `pnpm --dir apps/web test` — die gesamte Web-Testsuite, nicht nur die vier beruehrten
+ Dateien. Es soll auffallen, falls eine bisher unbekannte Stelle einen der Texte spiegelt.
+- `pnpm --dir apps/web run type-check`
+
+**Sichtpruefung im Browser** — vom Orchestrator auszufuehren, nicht vom Planer.
+Vorher noetig: `docker compose build web && docker compose up -d --force-recreate web`.
+Anschliessend `http://localhost:3000`.
+
+
+Alle Bildschirme in **deutscher** Oberflaeche pruefen. Gesucht wird jeweils die Sie-Form —
+kein "du", "dich", "dein".
+
+1. **Modul-Aktivierungsdialog** — als `admin`/`admin123` anmelden, dann
+ Administration → Module, bei einem noch nicht aktivierten Modul auf Aktivieren klicken.
+ Der Dialogtext muss siezen ("… oder möchten Sie die Freigaben separat konfigurieren?").
+
+2. **Löschdialog einer Gruppe, Einzahl** — als `admin` unter Administration → Gruppen die
+ Gruppe **"Vertrieb Innendienst"** loeschen (Dialog nur oeffnen, NICHT bestaetigen).
+ Der Text muss "1 Mitglied und 1 Modul-Freigabe" lauten — nicht "1 Mitglieder und
+ 1 Modul-Freigaben". Zur Gegenprobe eine Gruppe mit mehreren Mitgliedern oeffnen: dort
+ muss weiterhin die Mehrzahl stehen. Beide Dialoge mit Abbrechen schliessen.
+
+3. **Modul-Sperrseite** — abmelden, als `nutzer2`/`Test1234!` anmelden (dieser Benutzer hat
+ KEINE Freigabe fuer tender-radar) und die Modulseite von tender-radar direkt aufrufen.
+ Die 403-Seite muss "Sie haben für dieses Modul keine Freigabe. Wenden Sie sich an Ihren
+ Administrator." zeigen.
+
+4. **Marktplatz-Hinweis** — als `nutzer2` im Marktplatz auf die Kachel eines aktiven Moduls
+ ohne Freigabe klicken. Die eingeblendete Meldung muss "… wenden Sie sich an Ihren
+ Administrator." lauten.
+
+5. **Englische Fassung des Löschdialogs** — Sprache auf Englisch umstellen und Punkt 2
+ wiederholen: "1 member and 1 module grant".
+
+**Nicht im Browser pruefbar:** der Leerzustand der Gruppenliste
+(`admin.groups.noGroupsBody`) erscheint nur, wenn gar keine Gruppe existiert — im
+Testsystem gibt es Gruppen. Dieser Text ist durch den Test in `groups-page.test.tsx`
+(Zeile 185) abgedeckt und darf im Browser uebersprungen werden.
+
+
+
+
+- Die vier bisher duzenden deutschen Texte siezen; `de.json` enthaelt keine Du-Form mehr.
+- Der Loeschdialog schreibt bei genau einem Mitglied und einer Freigabe die Einzahl, ab
+ zwei weiterhin die Mehrzahl — in beiden Sprachen.
+- `en.json` wurde nur fuer den Loeschdialog angefasst; die Schluesselsaetze beider
+ Sprachdateien sind weiterhin identisch (787).
+- Kein bestehender Test wurde geloescht; alle fuenf bisherigen Fundstellen wurden auf den
+ neuen Wortlaut nachgezogen.
+- Ein neuer Test nagelt den Einzahl-Fall fest und war vor der Korrektur rot.
+- Kein anderer Text der Oberflaeche wurde umformuliert.
+
+
+
\ No newline at end of file