diff --git a/CHANGELOG.md b/CHANGELOG.md
index c981406..bc0c85f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,19 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
---
+## [1.12.92] — 2026-07-04
+
+### Fixed
+- **Deduction rules never reached the LLM when a custom prompt was saved** — the saved casting prompt turned out to be the 2nd-Quality-Run *verification* prompt (pasted into the Prompt panel at some point), which tells the model it's reviewing an existing segmentation instead of doing first-pass attribution, and contains none of the speaker-deduction rules; the client-side prompt auto-upgrade found no anchor text in it and silently skipped. Two fixes: the server now appends the deduction rules to any prompt that lacks them (attribution quality no longer depends on prompt history), and the misapplied saved prompt was reset to the shipped default (backed up to `config/audiobook_prompt.backup.txt`).
+- **Every "die"/"der" underlined as a character name** — a descriptive alias like "Die, die den Vampir verließ" gets comma-split into tokens, and the bare article "Die" became an alias that matched every article in the book. Bare articles/pronouns are now filtered out of alias token lists.
+- **Raw `style="…"` HTML leaking into casting text** — name-highlighting replaced name-by-name over HTML that already contained the injected highlight spans, so a shorter alias could match *inside* a longer name's `data-name` attribute and corrupt the markup. All names are now matched in a single combined pass that never re-scans its own output.
+
+### Added
+- **Chapter headings render as headings in the casting feed** — short standalone narration that reads like a title ("Prolog", "1. Kapitel", an OCR'd headline band) is now bold, larger, and centered on the paper page.
+- **Font-size controls in the casting toolbar** — A−/A+ buttons scale the paper pages' text; the size is shared with the Script Rehearser Stage (same persisted scale), so one reading size applies across both screens.
+
+---
+
## [1.12.91] — 2026-07-04
### Fixed
diff --git a/VERSION b/VERSION
index 5771ee1..ebc03cf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.12.91
+1.12.92
diff --git a/config/audiobook_prompt.backup.txt b/config/audiobook_prompt.backup.txt
new file mode 100644
index 0000000..c9011c4
--- /dev/null
+++ b/config/audiobook_prompt.backup.txt
@@ -0,0 +1,28 @@
+Du bist ein Qualitätsprüfer für die Analyse eines deutschen Hörbuchs. Eine erste KI hat den Textauszug bereits in Segmente unterteilt und jedem Segment einen Sprecher zugewiesen. Deine Aufgabe ist es JEDEN Sprecher-Zuweisung kritisch zu überprüfen und zu korrigieren.
+
+AUFGABE (2. Qualitätslauf — Verifizierung):
+1. ÜBERPRÜFE jede Sprecher-Zuweisung: Ist der angegebene Sprecher wirklich derjenige, der spricht? Nutze den Kontext (Inquit-Formeln wie "sagte X", "fragte sie", "rief er", Handlungsbeschreibungen, das Ping-Pong-Prinzip zwischen Sprechern, und den Gesamtkontext der Szene).
+2. KORRIGIERE falsch zugewiesene Sprecher mit dem korrekten Namen direkt aus dem Text.
+3. LÖSE alle 'Unknown'-Segmente auf: Nutze umgebenden Kontext, die Reihenfolge der Sprecher und textliche Hinweise. 'Unknown' ist NUR dann erlaubt, wenn der Sprecher absolut nicht bestimmbar ist.
+4. BEHALTE alle korrekten Zuweisungen exakt unverändert bei.
+
+DEFINITION VON DIALOG (KRITISCH):
+Text ist NUR 'dialogue', wenn er in Anführungszeichen steht (»...«, „...", "...", «...», <<...>>) oder mit einem Gedankenstrich (—) beginnt. ALLES ANDERE ist 'narration' (speaker: 'Narrator').
+
+GRAMMATIK-CHECK FÜR NARRATION:
+- Inquit-Formeln / Sprecher-Tags sind narration, niemals dialogue: finite Sprechverben wie sagte, fragte, rief, entgegnete, murmelte, flüsterte, schrie, antwortete + Subjekt/Pronomen/Name.
+- Beispiele: "murmelte er mit erstickter Stimme.", ", entgegnete Marcian kalt.", "fragte Uriens leise." sind Narrator/narration.
+- Action Beats sind narration: blickte, ging, schwieg, lachte, hob die Hand, wandte sich ab, usw.
+- Nur die tatsächlich gesprochenen Wörter innerhalb der Anführungszeichen bleiben dialogue; alle grammatischen Rahmen- und Berichtssätze sind narration.
+
+FÜR JEDES SEGMENT AUSGABE:
+- speaker: 'Narrator' für Narration, oder EXAKT der Name des Charakters.
+- type: 'narration' oder 'dialogue'
+- text: EXAKT der WORTWÖRTLICHE Originaltext — KEINE Änderungen, KEINE Auslassungen, KEINE Ergänzungen.
+- emotion: Bei Dialogen 1-2 deutsche Wörter für den Tonfall. Bei Narration leer ('').
+
+ABSOLUTE REGELN:
+- Alle Segmente zusammen MÜSSEN den Originaltext exakt, lückenlos und wortgetreu rekonstruieren.
+- PDF-/OCR-SCHUTZ: Ein fehlendes » oder « darf niemals bewirken, dass die restliche Passage als Dialog markiert wird. Bei einem offenen »-Zitat vor einer Inquit-Formel oder Erzählerhandlung endet der Dialog am ersten plausiblen Satzende (? ! .). Bei einem einzelnen schließenden « nach einem kurzen Satz ist dieser Satz davor der Dialog.
+- Erfinde NIEMALS Text. Lasse NIEMALS Wörter weg. Füge NIEMALS etwas hinzu.
+- Mische NIEMALS Narration und Dialog in einem Segment.
\ No newline at end of file
diff --git a/routes/conversation.py b/routes/conversation.py
index dff3627..a130425 100644
--- a/routes/conversation.py
+++ b/routes/conversation.py
@@ -961,6 +961,25 @@ async def attribute_dialogue(request: Request):
if not isinstance(prompt_override, str):
prompt_override = data.get("prompt")
base_prompt = (prompt_override if isinstance(prompt_override, str) else "") or _settings.get("audiobook_prompt") or ""
+ # A user-saved custom prompt can predate (or have replaced) the deduction
+ # rules that cut down false Unknown/Narrator attributions — the client-side
+ # prompt migration only upgrades prompts still containing the original
+ # anchor text. Guarantee the rules ride along regardless of what prompt is
+ # in play, so attribution quality never silently depends on prompt history.
+ if base_prompt.strip() and "Doppelpunkt-Regel" not in base_prompt:
+ base_prompt += (
+ "\n\nANALYSE-REGELN FÜR DIE ZUORDNUNG DES SPRECHERS (zusätzlich, immer anwenden):\n"
+ "1. Pronomen (er/sie) IMMER zum zuletzt genannten Charakter passenden Geschlechts auflösen.\n"
+ "2. Doppelpunkt-Regel: Endet ein Erzählersatz mit \":\", spricht dessen Subjekt das folgende Zitat "
+ "(z.B. \"Dann richtete er sich auf und rief in die Runde:\" → der zuvor genannte Charakter spricht).\n"
+ "3. Nachgestellte Zuordnung: Der Erzählersatz NACH einem Zitat verrät oft den Sprecher — auch bei "
+ "unpersönlicher Formel (\"»Was machst du da?« ertönte es über ihm. Karyla war herübergekommen.\" → Karyla sprach).\n"
+ "4. Adressaten-Regel: \"X wandte sich an Y\" / \"X sah Y an\" → X spricht das nächste Zitat, Y antwortet.\n"
+ "5. Ping-Pong: Zwei Personen im Gespräch wechseln sich strikt ab, auch über viele Zitate ohne Tags. "
+ "In einer Zwei-Personen-Szene ist 'Unknown' fast immer falsch.\n"
+ "6. Rollenbezeichnungen sind gültige Sprecher ('Ork', 'Der Fremde', 'Nachbar', 'Wächter') — nutze sie statt 'Unknown'.\n"
+ "7. 'Unknown' NUR, wenn eine Zuordnung trotz aller Regeln absolut unmöglich ist."
+ )
if not base_prompt.strip():
base_prompt = (
"You attribute dialogue in prose fiction for a multi-voice audiobook. "
diff --git a/static/index.html b/static/index.html
index 91af6c8..f218b7f 100644
--- a/static/index.html
+++ b/static/index.html
@@ -10,7 +10,7 @@
-
+
@@ -27,7 +27,7 @@
-
+
@@ -365,7 +365,7 @@ window.toggleNavTree = function(treeId, chevronId) {
-
+