diff --git a/docs/PLAN.md b/docs/PLAN.md
index 03e78f7..5aaa55c 100644
--- a/docs/PLAN.md
+++ b/docs/PLAN.md
@@ -384,11 +384,16 @@ program, click **Run** (or Ctrl+Return); each Run is one undo step.
output log; `OllieView` creates the tool view and owns the apply logic
(`runSamProgram`, `applySamToDocument`, `runSamFileLoop`).
- **Deviations (documented)**: regexes use `QRegularExpression` (PCRE), not
- plan9 `regexp(7)` — practical patterns match identically; `longest-leftmost`
- edge cases differ. Out of scope: multi-file menu (`b B n D`), external file
- I/O (`e r w f`), `"re"` file-addressing, and sam's own `u` (Kate's undo stack
- is the undo mechanism). The live GUI panel (tool view + transaction apply) is
- not exercisable headless and is unverified on a live display.
+ plan9 `regexp(7)`. Everyday patterns match identically; the one semantic
+ divergence is leftmost-greedy vs sam's leftmost-longest (bites only on
+ overlapping alternation like `a|ab`), and `^`/`$` anchor buffer-wide not
+ per-line by default. katesam gains PCRE extras (`\d \w \b`, lookahead,
+ non-greedy, inline flags). Full user-facing treatment with verified examples
+ and workarounds in **`docs/SAM.md`**. Out of scope: multi-file menu
+ (`b B n D`), external file I/O (`e r w f`), `"re"` file-addressing, and sam's
+ own `u` (Kate's undo stack is the undo mechanism). The live GUI panel (tool
+ view + transaction apply) is not exercisable headless and is unverified on a
+ live display.
## Build and test
diff --git a/docs/SAM.md b/docs/SAM.md
new file mode 100644
index 0000000..41d65f6
--- /dev/null
+++ b/docs/SAM.md
@@ -0,0 +1,139 @@
+# katesam — sam structural-regexp editing in Kate
+
+The **Sam** tool view (left sidebar) runs the plan9
+[sam](https://doc.cat-v.org/plan_9/4th_edition/papers/sam/) command language
+against the active document, or project-wide via `X`/`Y`. Type a program, press
+**Ctrl+Return** or click **Run**. Each Run is one undo step (Ctrl+Z reverts the
+whole Run; multi-file `X`/`Y` undoes per file).
+
+This document covers the **regex deviation** — katesam uses PCRE
+(`QRegularExpression`), not plan9 `regexp(7)` — and what that means in practice.
+Every example below is verified against the engine.
+
+## TL;DR
+
+- Everyday patterns behave **identically** to sam.
+- One real semantic difference: **PCRE is leftmost-greedy, sam is
+ leftmost-longest.** It only bites on *overlapping alternation* (`a|ab`).
+- `^` and `$` anchor to the **whole buffer, not each line**, by default. This is
+ the most common surprise. Use a structural loop or the `(?m)` flag.
+- You *gain* PCRE features sam never had: `\d \w \s \b`, lookahead/lookbehind,
+ non-greedy `*?`, backreferences, inline flags `(?i)` `(?m)` `(?s)`.
+
+---
+
+## The one semantic divergence: greedy vs longest
+
+sam's `regexp(7)` matches the **leftmost-longest** substring. PCRE matches
+**leftmost**, then resolves alternation **left-to-right, first wins** (greedy
+within a branch, but alternation order decides between branches).
+
+| program | input | sam (leftmost-longest) | **katesam (PCRE)** |
+|---|---|---|---|
+| `,s/a\|ab/X/` | `ab` | `X` (matches `ab`) | **`Xb`** (matches `a`) |
+| `,s/ab\|a/X/` | `ab` | `X` | `X` |
+
+**Implication:** when alternatives overlap, **order them longest-first**
+(`ab|a`, not `a|ab`). This is the only case where a working sam command can
+silently produce a different edit in katesam. Non-overlapping alternation
+(`cat|dog`) and all non-alternation patterns are unaffected.
+
+Quantifier greediness is the same in both (`a.*c` on `axxcxxc` → whole string in
+both). katesam additionally offers non-greedy `.*?`:
+
+| program | input | result |
+|---|---|---|
+| `,s/a.*c/X/` | `axxcxxc` | `X` (greedy, same as sam) |
+| `,s/a.*?c/X/` | `axxcxxc` | `Xxxc` (non-greedy, PCRE bonus) |
+
+---
+
+## Anchors `^` / `$` are buffer-wide, not per-line
+
+By default PCRE anchors `^`/`$` to the **start/end of the whole buffer**, so a
+global substitution touches only the first/last position — **not** every line:
+
+| program | input | result |
+|---|---|---|
+| `,s/^/> /g` | `a⏎b⏎c` | `> a⏎b⏎c` (only line 1!) |
+| `,s/$/;/g` | `a⏎b⏎c` | `a⏎b⏎c;` (only the end!) |
+
+This differs from how people expect line-oriented edits to work. **Two correct
+ways** to act per line:
+
+**1. Structural (sam-idiomatic, preferred).** Loop over lines with `x`, then act
+on each line's dot. `.+` matches the non-newline run of each line:
+
+```
+,x/.+/ i/> / prefix every (non-empty) line with "> " → > a⏎> b⏎> c
+,x/.+/ a/;/ append ";" to every line → a;⏎b;⏎c;
+```
+
+**2. PCRE multiline flag `(?m)`** (a bonus sam lacks). Makes `^`/`$` match at
+every line boundary:
+
+```
+,s/(?m)^/> /g → > a⏎> b⏎> c
+,s/(?m)$/;/g → a;⏎b;⏎c;
+```
+
+Prefer the structural form — it is the real sam way and composes with other
+loop/guard steps. Reach for `(?m)` for quick one-liners.
+
+---
+
+## What matches identically (no surprises)
+
+| behaviour | katesam (PCRE) | sam | same? |
+|---|---|---|---|
+| `.` crosses newline | no | no | ✅ |
+| `[^z]+` crosses newline | yes | yes | ✅ |
+| empty-match advance `s/x*/-/g` on `axbx` → `-a-b-` | ✅ | ✅ | ✅ |
+| greedy quantifiers `* + ?` | ✅ | ✅ | ✅ |
+| character classes `[a-z]`, groups `( )`, `|` | ✅ | ✅ | ✅ |
+| `&` whole-match, `\1..\9` groups in replacement | ✅ | ✅ | ✅ |
+
+> `.` not crossing newlines matches sam, so line-structured descent
+> (`,x/.*\n/ ...`) behaves as expected. Use `(?s)` if you *want* `.` to span
+> newlines (PCRE bonus).
+
+---
+
+## PCRE features sam never had (free upgrades)
+
+All verified working in katesam:
+
+| feature | example | effect |
+|---|---|---|
+| shorthand classes | `,s/\d+/N/g` on `a12b345` | `aNbN` |
+| `\w+` in a loop | `,x/\w+/ c/W/` on `foo bar` | `W W` |
+| word boundary `\b` | `,s/\bfoo\b/X/g` on `foo foobar foo` | `X foobar X` |
+| backreference | `,s/(\w)\1/D/g` on `aabbc` | `DDc` |
+| lookahead | `,s/foo(?=bar)/X/g` on `foobar fooqux` | `Xbar fooqux` |
+| inline flags | `(?i)` ignore-case, `(?m)` multiline, `(?s)` dotall | per-pattern |
+
+These make many edits *easier* than in real sam. The tradeoff is that a pattern
+written for katesam may not be portable back to sam.
+
+---
+
+## Practical guidance
+
+- **Order overlapping alternatives longest-first** (`\.tar\.gz|\.gz`, not the
+ reverse). The only silent divergence.
+- **For per-line edits, prefer `,x/.+/ …`** over `^`/`$`. If you use anchors
+ globally, remember they are buffer-wide, or add `(?m)`.
+- **Lean on PCRE extras** (`\b`, `\d`, lookahead) freely — they are reliable;
+ just know they are not sam-portable.
+- **Empty-match globals** (`s/x*/…/g`) advance one position per null match, as in
+ sam — safe, but as always with `*`, double-check the result.
+
+## Why PCRE and not regexp(7)
+
+`QRegularExpression` ships with Qt (already a dependency) and gives a richer,
+familiar syntax for free. plan9 `regexp(7)` is a Rune-based NFA woven into sam's
+own buffer types; using it would mean porting that engine. The tradeoff accepted
+here: everyday fidelity plus extra power, at the cost of exact leftmost-longest
+semantics on overlapping alternation. If that matters for your workflow, the
+engine's regex calls are isolated in `SamEngine` and could be swapped for a
+ported `regexp.c` later.
diff --git a/src/plugin/sampanel.cpp b/src/plugin/sampanel.cpp
index 1bb60cc..354875b 100644
--- a/src/plugin/sampanel.cpp
+++ b/src/plugin/sampanel.cpp
@@ -55,7 +55,10 @@ SamPanel::SamPanel(QWidget *parent)
auto *hint = new QLabel(
i18n("sam commands — e.g. ,s/foo/bar/g or "
- "X/\\.cpp$/ ,s/old/new/g (project-wide). Ctrl+Return runs."),
+ "X/\\.cpp$/ ,s/old/new/g (project-wide). Ctrl+Return runs.
"
+ "Regex is PCRE: ^/$ are buffer-wide (use "
+ ",x/.+/ … or (?m) per line); order overlapping "
+ "alternatives longest-first."),
this);
hint->setWordWrap(true);
hint->setTextFormat(Qt::RichText);