diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md
index 2d38038..41a4aa7 100644
--- a/docs/DECISIONS.md
+++ b/docs/DECISIONS.md
@@ -2009,6 +2009,11 @@ gekrümmten Ebene und begegnen den Rahmenkanten nie. Die **Hervorhebung**
(fokussierter Knoten) bleibt durchgezogen in Tinte: Sie ist ausdrücklich
angefordert und soll sich nach vorn drängen. Export identisch nachgezogen.
+Zweiter Schritt derselben Rückmeldung: Auch die **Pfeilspitze** ist jetzt
+**offen** (ein Winkel aus zwei dünnen grauen Strichen) statt eines gefüllten
+Dreiecks — das Dreieck stach als einziger satter Fleck aus der gepunkteten
+Linie heraus. Die Hervorhebung bekommt denselben Winkel in Tinte.
+
## D42 — Closure-Pfad: erschöpfend über die gekoppelten Gruppen, gierig nur benannt
Die letzte Baustelle aus D34: Mit Abhängigkeiten zählt der günstigste Pfad
nicht mehr den gewählten Teilbaum, sondern die **Hülle** — jeder nötige Knoten
diff --git a/docs/SPEC.md b/docs/SPEC.md
index f0be00b..e9a5793 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -643,9 +643,9 @@ Abhängigkeiten werden als **optisch sekundäre** Kanten gezeichnet: dünn,
blassgrau (`#6B7A8C`), **gepunktet** und **geschwungen** — Punktierung und
Krümmung unterscheiden sie doppelt von den Baumlinien (durchgezogen bzw.
gestrichelt, immer orthogonal) und halten sie zurückhaltend — auf einer
-eigenen Zeichenebene **hinter** den Knoten, mit einer kleinen **Pfeilspitze
-auf das Gebrauchte** („braucht“-Richtung). Der Baum trägt weiterhin die
-Hauptaussage.
+eigenen Zeichenebene **hinter** den Knoten, mit einer kleinen **offenen
+Pfeilspitze auf das Gebrauchte** („braucht“-Richtung; ein Winkel aus zwei
+Strichen, kein gefülltes Dreieck). Der Baum trägt weiterhin die Hauptaussage.
- Die Kanten des **ausgewählten** Knotens — Tastaturfokus im Diagramm, sonst
der Knoten der Cursor-Zeile — liegen hervorgehoben in Tinte auf einer
diff --git a/frontend/src/app.js b/frontend/src/app.js
index 968d584..d98d207 100644
--- a/frontend/src/app.js
+++ b/frontend/src/app.js
@@ -336,13 +336,15 @@ function depCurve(a, b){
return {d:`M${p1.x.toFixed(1)},${p1.y.toFixed(1)} Q${ctrl.x.toFixed(1)},${ctrl.y.toFixed(1)} ${p2.x.toFixed(1)},${p2.y.toFixed(1)}`,
end:p2, ctrl};
}
+/* Offene Pfeilspitze (Winkel aus zwei Strichen, D41-Nachtrag): ein gefülltes
+ Dreieck stach als einziger satter Fleck aus der gepunkteten Linie heraus. */
function depArrow(end, from){
const dx = end.x - from.x, dy = end.y - from.y;
const l = Math.hypot(dx, dy) || 1;
const ux = dx/l, uy = dy/l, s = 5;
const a = {x: end.x - ux*s*1.8 - uy*s, y: end.y - uy*s*1.8 + ux*s};
const b = {x: end.x - ux*s*1.8 + uy*s, y: end.y - uy*s*1.8 - ux*s};
- return `M${end.x.toFixed(1)},${end.y.toFixed(1)} L${a.x.toFixed(1)},${a.y.toFixed(1)} L${b.x.toFixed(1)},${b.y.toFixed(1)} Z`;
+ return `M${a.x.toFixed(1)},${a.y.toFixed(1)} L${end.x.toFixed(1)},${end.y.toFixed(1)} L${b.x.toFixed(1)},${b.y.toFixed(1)}`;
}
/* „Ausgewählt" heißt: Tastaturfokus im Diagramm, sonst der Knoten der
Cursor-Zeile (D25) — beide Lesarten von „ich schaue auf diesen Knoten". */
@@ -495,7 +497,7 @@ function diagramToSvg(){
depEdges().forEach(([from, to]) => {
const c = depCurve(R(from), R(to));
parts.push(``);
- parts.push(``);
+ parts.push(``);
});
/* 1b) Günstigster-Pfad: kräftige Linie hinter den Knoten */
diff --git a/frontend/src/style.css b/frontend/src/style.css
index a06be3d..f07f24b 100644
--- a/frontend/src/style.css
+++ b/frontend/src/style.css
@@ -710,9 +710,11 @@
.dep-overlay{position:absolute;left:0;top:0;overflow:visible;pointer-events:none}
.dep-edge{fill:none;stroke:var(--muted);stroke-width:1.5;opacity:.4;
stroke-linecap:round;stroke-dasharray:.1 5}
- .dep-arrow{fill:var(--muted);opacity:.4}
+ /* Offene Pfeilspitze — grau und dünn wie die Linie, kein gefülltes Dreieck. */
+ .dep-arrow{fill:none;stroke:var(--muted);stroke-width:1.5;opacity:.4;
+ stroke-linecap:round;stroke-linejoin:round}
.dep-edge.hl{stroke:var(--line);stroke-width:2;opacity:.9;stroke-dasharray:none}
- .dep-arrow.hl{fill:var(--line);opacity:.9}
+ .dep-arrow.hl{stroke:var(--line);stroke-width:2;opacity:.9}
.cheap-path{fill:none;stroke:var(--or);stroke-width:2.5;stroke-linecap:round;
stroke-dasharray:8 6;opacity:.9}
.cheap-path.faint{opacity:.2}
diff --git a/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json b/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
new file mode 100644
index 0000000..f8f1854
--- /dev/null
+++ b/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
@@ -0,0 +1 @@
+{"version":"4.1.11","results":[[":frontend/tests/render.test.js",{"duration":98.47688799999997,"failed":false}],[":frontend/tests/focusmark.test.js",{"duration":35.79027499999995,"failed":false}],[":frontend/tests/fold.test.js",{"duration":41.154863999999975,"failed":false}],[":frontend/tests/optional.test.js",{"duration":35.42018200000001,"failed":false}],[":frontend/tests/parser.test.js",{"duration":33.422897999999975,"failed":false}],[":frontend/tests/xor.test.js",{"duration":52.44134100000002,"failed":false}],[":frontend/tests/desc.test.js",{"duration":31.248038000000008,"failed":false}],[":frontend/tests/deps.test.js",{"duration":31.736107000000004,"failed":false}],[":frontend/tests/closure.test.js",{"duration":24.077101999999968,"failed":false}],[":frontend/tests/effective.test.js",{"duration":27.93907200000001,"failed":false}],[":frontend/tests/ids.test.js",{"duration":25.51923499999998,"failed":false}],[":frontend/tests/remote.test.js",{"duration":20.999619999999993,"failed":false}],[":frontend/tests/warnings.test.js",{"duration":9.594386999999983,"failed":false}],[":frontend/tests/fresh.test.js",{"duration":22.15039999999999,"failed":false}]]}
\ No newline at end of file