+
+
+
+
+
+
+This page is the interactive companion to the README. Use hover labels to inspect the refusal-pole probe without forcing the README plot to carry every label.
+
+Refusal-Pole Probe
+
+Each point is one template, averaged over two refusal-probe axes and four clean model artifacts. Lower-right is better: more intended-axis movement with less off-axis confounding.
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/index.qmd b/docs/index.qmd
new file mode 100644
index 0000000..6eb46f2
--- /dev/null
+++ b/docs/index.qmd
@@ -0,0 +1,99 @@
+---
+title: Persona Steering Template Library
+format:
+ html:
+ toc: true
+ code-fold: true
+jupyter: python3
+execute:
+ echo: false
+ warning: false
+ message: false
+---
+
+```{python}
+from pathlib import Path
+import json
+import sys
+
+import plotly.graph_objects as go
+
+ROOT = Path.cwd().parent
+sys.path.insert(0, str(ROOT / "scripts"))
+```
+
+This page is the interactive companion to the README. Use hover labels to inspect
+the refusal-pole probe without forcing the README plot to carry every label.
+
+## Refusal-Pole Probe
+
+```{python}
+summary_path = ROOT / "out/model_matrix/refusal_probe_seed24_n1_template_model_summary.jsonl"
+rows = [json.loads(line) for line in summary_path.read_text().splitlines() if line.strip()]
+
+plot_rows = []
+for i, row in enumerate(rows, start=1):
+ plot_rows.append({
+ "rank": i,
+ "template": row["template"],
+ "on_axis": min(1.0, max(0.0, row["axis_delta_mean"] / 8.0)),
+ "off_axis": min(1.0, max(0.0, (row["off_axis_problem_mean"] - 1.0) / 6.0)),
+ "score_p25": row["score_p25"],
+ "score_t": row["score_t"],
+ "score_mean": row["score_mean"],
+ "score_std": row["score_std"],
+ "pass": row["strict_pass_rate_mean"],
+ "echo": row["persona_echo_rate_mean"],
+ "refusal": row["refusal_or_ai_break_rate_mean"],
+ })
+
+hover = [
+ "