diff --git a/docs/web-grammars-html-css-framing.md b/docs/web-grammars-html-css-framing.md
new file mode 100644
index 0000000..8378e56
--- /dev/null
+++ b/docs/web-grammars-html-css-framing.md
@@ -0,0 +1,295 @@
+# Web grammars (HTML + CSS) + HTML injections — framing
+
+**Revision 3 — pre-implementation. Ground truth: canonical `main` @
+`4daa1b8` (after LaTeX #144 and inline-math-docs #145), 2026-07-23. Status:
+framing only; no implementation.** Rev 2 settled the capture set, injection
+scope, and LSP claim (round 1); rev 3 corrects the `#match?` predicate analysis
+(tree-sitter evaluates it natively) and refreshes the folding footprint to the
+current branch (round 2). See §0.1.
+
+## 0.1 Revision history
+
+### Round 1 (rev 1 → rev 2)
+
+- **F1 (high).** Q#WEB4 deferred the capture mapping. The upstream queries
+ settle it: HTML and CSS need exactly **`tag`** and **`attribute`**; `@tag.error`
+ prefix-walks to `tag`; there is **no** `@tag.delimiter` (rev-1 speculation).
+ Q#WEB4 now names both mappings and their styles, and acceptance 6 paints an
+ HTML **attribute** explicitly (a tag/property-only test could pass with
+ `attribute` unverified since `property` is already recognized).
+- **F2 (medium).** The HTML injections query has **exactly two** patterns —
+ `script_element`→javascript, `style_element`→css. Q#WEB5 no longer claims
+ event-handler / `style=` coverage; inline-attribute injection is now a named
+ deferral.
+- **F3 (medium).** No HTML/CSS language server is bundled: `lsp.lua`'s default
+ config list ends at YAML (`:247`) and `:521` returns `nil` without a
+ configured command. §2 and §5 corrected — the grammars give stable language
+ IDs so a *user-supplied* config attaches automatically, but nothing ships or
+ starts by default.
+
+### Round 2 (rev 2 → rev 3)
+
+- **R2-1 (medium).** The rev-2 claim that pmacs *ignores* `#match?` was wrong.
+ `src/syntax.rs:1701` passes the source to `QueryCursor::captures`, so
+ tree-sitter evaluates the standard `#match?`/`#eq?`/`#any-of?` text predicates
+ natively (pmacs special-cases only `#is? local`, `:1703`). Corrected in §2 and
+ Q#WEB4: an ordinary CSS property has a single `@property` capture; only a
+ `--custom-property` also receives `@variable` (a benign double-capture).
+ Acceptance 6 no longer claims an ordinary property pins custom-property
+ precedence — it just verifies `color` → `@property` paints.
+- **R2-2 (low).** The folding footprint in §0 was stale. At
+ `githubsucks/folding` @ `036a994` the branch touches `src/fold.rs`,
+ `editor.rs`, `editor_core.rs`, `lib.rs`, `lua_bindings/{fold.rs,mod.rs}`,
+ `semantic_render.rs`, `fold.lua`, and tests — not
+ `overlay.rs`/`daemon.rs`/`syntax.rs`. §0 now lists the real set; the
+ zero-overlap conclusion is unchanged (stronger, if anything).
+
+Add tree-sitter **HTML** and **CSS** grammars so `.html`/`.htm` and `.css`
+buffers get lexical highlighting, and — the north-star payoff — light up
+HTML's embedded-language **injections**: `` parses as JavaScript and
+ `` parses as CSS, via HTML's crate-exported injections query
+ riding the #122 injection engine. CSS is registered here so the `` and `` produces a
+ `ParseTreeBundle` whose child layers resolve to `css` and `javascript`, and
+ a grid-paint asserts an injected CSS property and JS keyword paint **inside**
+ the embedded regions (mirroring `grid_paints_injected_child_keyword`).
+6. **Paint (non-vacuous highlighting)**: parse `` and assert **both**
+ the `` tag (`@tag`) **and** the `href` attribute name (`@attribute`) paint
+ the two new non-default styles — the attribute assertion is load-bearing, since
+ a tag-only test could pass with `@attribute` unverified. Also assert a CSS
+ selector (`@tag`) paints the new `tag` style and an ordinary CSS property
+ (`color` → `@property`) paints non-default (an ordinary property has a single
+ unconditional capture, so no precedence subtlety — Q#WEB4).
+7. **Full gate suite** per `CLAUDE.md`.
+
+## 7. Prior art in pmacs
+
+- **LaTeX lane #144** (`docs/latex-grammar-math-substrate-framing.md`) — the
+ grammar-add mechanics, the table-guard/smoke/paint test conventions, and the
+ compile-gate-plus-paint-test discipline. (Its overlay convention is not
+ needed here.)
+- **Multi-language injections #122**
+ (`docs/multi-language-injections-framing.md`) — the `ParseTreeBundle` +
+ `Layer` engine HTML's injections ride.
+- **markdown → rust fenced code** (`src/syntax.rs:848`, tested by
+ `src/highlight.rs`'s `grid_paints_injected_child_keyword`) — the working
+ injection precedent this lane's acceptance 5 mirrors.