diff --git a/app/embeddings.py b/app/embeddings.py
index ea798c7..04baa93 100644
--- a/app/embeddings.py
+++ b/app/embeddings.py
@@ -714,7 +714,7 @@ def _chunk_pdf_url(chunk: dict) -> Optional[str]:
# die URL bleibt bounded (sonst würden 500-Zeichen-Snippets in jeder
# Zitat-URL stehen und das HTML-Report-JSON aufblähen).
q = urllib.parse.quote_plus(text[:200])
- return f"/api/wahlprogramm-cite?pid={prog_id}&seite={seite}&q={q}"
+ return f"/api/wahlprogramm-cite?pid={prog_id}&seite={seite}&q={q}#page={seite}"
if seite:
return f"/static/referenzen/{pdf}#page={seite}"
diff --git a/app/redline_utils.py b/app/redline_utils.py
index f3f5951..118335e 100644
--- a/app/redline_utils.py
+++ b/app/redline_utils.py
@@ -85,4 +85,6 @@ def build_pdf_href(zitat: dict, bundesland: str = "") -> str:
text = zitat.get("text", "")
q = " ".join(text.split()[:5])
- return f"/api/wahlprogramm-cite?pid={pid}&seite={seite}&q={quote_plus(q)}"
+ # #page=N als URL-Hash, damit der Browser-PDF-Viewer direkt zur Seite
+ # springt — OpenAction im PDF wird von Chrome/Firefox ignoriert.
+ return f"/api/wahlprogramm-cite?pid={pid}&seite={seite}&q={quote_plus(q)}#page={seite}"
diff --git a/app/templates/v2/components/quote_card.html b/app/templates/v2/components/quote_card.html
index 1adc1e5..84cd5ab 100644
--- a/app/templates/v2/components/quote_card.html
+++ b/app/templates/v2/components/quote_card.html
@@ -27,7 +27,9 @@
{% endif %}
{{ source }}
{% if pdf_href %}
- · PDF öffnen
+ {# Falls pdf_href noch keinen #page=…-Anker hat, aus seite= im Query-String einen anhaengen — Browser-PDF-Viewer ignorieren PDF-OpenAction, der Hash-Anker funktioniert zuverlaessig. #}
+ {% set _href = pdf_href if '#page=' in pdf_href else (pdf_href ~ ('#page=' ~ (pdf_href.split('seite=')[1].split('&')[0] if 'seite=' in pdf_href else '1'))) %}
+ · PDF öffnen
{% endif %}