diff --git a/docs/extensions/index.md b/docs/extensions/index.md
index 5c76e0c3..40214460 100644
--- a/docs/extensions/index.md
+++ b/docs/extensions/index.md
@@ -16,6 +16,7 @@ Extensions provide a clean way to bundle related customizations together. Each e
| [HeadingReferenceExtension](#headingreferenceextension) | Resolves `[[Heading Text]]` links to headings in the current document |
| [HeadingPermalinksExtension](#headingpermalinksextension) | Adds clickable anchor links to headings |
| [InlineFootnotesExtension](#inlinefootnotesextension) | Converts `[content]{.fn}` spans to inline footnotes |
+| [LineBlockDivExtension](#lineblockdivextension) | Adds a fenced `::: |` line block (verse/addresses) without prefixing every line |
| [MentionsExtension](#mentionsextension) | Converts `@username` patterns to profile links |
| [MermaidExtension](#mermaidextension) | Transforms mermaid code blocks into diagrams |
| [SemanticSpanExtension](#semanticspanextension) | Converts span attributes to semantic HTML elements (``, ``, ``) |
@@ -604,6 +605,47 @@ fallback such as parenthetical inline content.
This follows the approach discussed in [djot issue #286](https://github.com/jgm/djot/issues/286). The `^[...]` syntax used by Pandoc conflicts with djot's superscript syntax (`^text^`), so the span-with-class approach provides inline footnotes without parser changes.
+## LineBlockDivExtension
+
+Adds a fenced line block written as a `:::` div whose only class token is a pipe: `::: |`. It produces the same `line-block` div as the [`|`-prefixed form](/guide/syntax#line-blocks), but without prefixing every line - convenient for verse, addresses, lyrics, and signature blocks where each line would otherwise need a leading `|`.
+
+Inside the fence, each soft line break becomes a hard break (` The limerick packs laughs anatomical But the good ones I've seen
`), leading whitespace is preserved, and a blank line separates stanzas (each becomes its own paragraph). Inline djot (emphasis, links, ...) still parses normally.
+
+```php
+use Djot\Extension\LineBlockDivExtension;
+
+$converter->addExtension(new LineBlockDivExtension());
+```
+
+Leading whitespace on each line is preserved as a non-breaking space, so the indentation survives without any CSS: ` ` in HTML, a real non-breaking space (`U+00A0`) in Markdown - which keeps it through a round-trip re-render and never trips Markdown's indented-code-block rule - and an ordinary space in the plain-text and ANSI renderers. Tabs expand to four-column stops.
+
+**Input:**
+
+```djot
+::: |
+The limerick packs laughs anatomical
+ Into space that is quite economical.
+
+But the good ones I've seen
+ So seldom are clean
+:::
+```
+
+```html
+
+ Into space that is quite economical.
+ So seldom are clean
'));
+ $this->assertStringContainsString("Stanza one a
\nStanza one b", $html);
+ $this->assertStringContainsString("Stanza two a
\nStanza two b", $html);
+ }
+
+ public function testInlineMarkupStillParses(): void
+ {
+ $djot = "::: |\nA _em_ and a [link](https://example.com)\nplain\n:::";
+
+ $html = $this->converter()->convert($djot);
+
+ $this->assertStringContainsString('em', $html);
+ $this->assertStringContainsString('link', $html);
+ }
+
+ public function testPendingAttributesAttachToTheDiv(): void
+ {
+ $djot = "{#poem .verse}\n::: |\nLine one\nLine two\n:::";
+
+ $html = $this->converter()->convert($djot);
+
+ $this->assertStringContainsString('id="poem"', $html);
+ $this->assertStringContainsString('verse', $html);
+ $this->assertStringContainsString('line-block', $html);
+ }
+
+ public function testFencedCodeInsideIsNotTreatedAsClosingFence(): void
+ {
+ $djot = "::: |\nbefore\n```\n:::\nstill code\n```\nafter\n:::";
+
+ $html = $this->converter()->convert($djot);
+
+ $this->assertStringContainsString('
', $html); + $this->assertStringContainsString('', $html); + // Indentation relative to the line block survives the blockquote dedent. + $this->assertStringContainsString("Roses are red
\n Violets are blue", $html); + } + + public function testWorksInsideListItem(): void + { + $djot = "- item\n\n ::: |\n Line one\n Indented two\n :::"; + + $html = $this->converter()->convert($djot); + + $this->assertStringContainsString('', $html); + $this->assertStringContainsString(' ', $html); + $this->assertStringContainsString("Line one
\n Indented two", $html); + } + + public function testWorksInsideBlockquotedList(): void + { + $djot = "> - x\n>\n> ::: |\n> alpha\n> beta\n> :::"; + + $html = $this->converter()->convert($djot); + + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + $this->assertStringContainsString(' ', $html); + $this->assertStringContainsString("alpha
\n beta", $html); + } + + public function testPlainDivWithRealClassIsUntouched(): void + { + $djot = "::: warning\nHello\n:::"; + + $html = $this->converter()->convert($djot); + + $this->assertStringContainsString('', $html); + $this->assertStringNotContainsString('line-block', $html); + } +}