diff --git a/CHANGELOG.md b/CHANGELOG.md
index b62d161..f6a340f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,10 @@ follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Fixed
+- **A divider stops at its box's sides in HTML.** `.ms-divider` reached 1ch past each side of its box
+ whatever the box's padding, so in a box padded 0 (`--ms-pad:0ch`) every divider ran past the border.
+ It now reaches across `--ms-pad`, and not at all in a borderless box. Each box sets `--ms-pad: 1ch`
+ itself, so a box inside one padded 0 is no longer padded 0 too.
- **A box round a picture spans the width in a terminal again.** 2.14.2 fitted a box holding only a
picture to the picture, so in a scene log it stood narrower than every box and rule around it. It is
drawn across the width again, with its padding, and the picture at its left. HTML still fits it
diff --git a/MarkupString.Html/LayoutCss.cs b/MarkupString.Html/LayoutCss.cs
index 9b4ad2e..935988d 100644
--- a/MarkupString.Html/LayoutCss.cs
+++ b/MarkupString.Html/LayoutCss.cs
@@ -11,7 +11,8 @@ namespace MarkupString.Html;
/// second row of a striped table or list (a faint grey when unset). A page
/// sets these to theme every layout on it; a layout's own theme sets them on its block, and a game's
/// default theme sets the -default form of each, under what the page sets.
-/// A box's padding is --ms-pad (1ch unset), set on the box when its own differs.
+/// A box's padding is --ms-pad, 1ch unless the box sets its own; a divider in the box reaches
+/// across that padding to the box's sides.
/// Everything is sized in ch, the width of a cell in the terminal font the layout was laid out
/// in, and every row wraps, so nothing scrolls sideways on a narrow page.
///
@@ -21,7 +22,7 @@ public static class LayoutCss
public static readonly string Fixed =
".ms-layout { display: block; max-width: 100%; white-space: normal; }\n" +
".ms-text { white-space: pre-wrap; overflow-wrap: anywhere; }\n" +
- ".ms-box { margin: 0; padding: 0 var(--ms-pad, 1ch); min-width: 0; border: 1px solid var(--ms-border, var(--ms-border-default, currentColor)); }\n" +
+ ".ms-box { --ms-pad: 1ch; margin: 0; padding: 0 var(--ms-pad); min-width: 0; border: 1px solid var(--ms-border, var(--ms-border-default, currentColor)); }\n" +
".ms-box.ms-border-none { border: none; padding: 0; }\n" +
".ms-box.ms-border-double { border: 3px double var(--ms-border, var(--ms-border-default, currentColor)); }\n" +
".ms-box.ms-border-heavy { border-width: 2px; }\n" +
@@ -34,7 +35,8 @@ public static class LayoutCss
".ms-rule::before, .ms-rule::after, .ms-divider::before, .ms-divider::after { content: \"\"; flex: 1 1 0; border-top: 1px solid var(--ms-border, var(--ms-border-default, currentColor)); }\n" +
".ms-rule[data-align=\"left\"]::before, .ms-divider[data-align=\"left\"]::before { flex: 0 0 1ch; }\n" +
".ms-rule[data-align=\"right\"]::after, .ms-divider[data-align=\"right\"]::after { flex: 0 0 1ch; }\n" +
- ".ms-divider { margin: 0 -1ch; }\n" +
+ ".ms-divider { margin: 0 calc(-1 * var(--ms-pad, 1ch)); }\n" +
+ ".ms-box.ms-border-none > .ms-divider { margin: 0; }\n" +
".ms-border-double.ms-rule::before, .ms-border-double.ms-rule::after { border-top: 3px double var(--ms-border, var(--ms-border-default, currentColor)); }\n" +
".ms-border-none.ms-rule::before, .ms-border-none.ms-rule::after { border-top: none; }\n" +
".ms-flex { display: flex; flex-wrap: wrap; }\n" +
diff --git a/MarkupString.Tests/Layout/BlockLayoutTests.cs b/MarkupString.Tests/Layout/BlockLayoutTests.cs
index 0b3a973..6d440d0 100644
--- a/MarkupString.Tests/Layout/BlockLayoutTests.cs
+++ b/MarkupString.Tests/Layout/BlockLayoutTests.cs
@@ -324,7 +324,21 @@ public async Task Figure_AloneInABox_TheBoxFitsThePicture()
// The picture sits on the top of its line rather than its baseline, so no gap for descenders is left under it.
await Assert.That(LayoutCss.Fixed).Contains(".ms-figure-image { max-width: 100%; height: auto; vertical-align: top; }");
await Assert.That(LayoutCss.Fixed).Contains(".ms-box.ms-box-picture { padding: 0; width: fit-content; max-width: 100%; box-sizing: border-box; }");
- await Assert.That(LayoutCss.Fixed).Contains(".ms-box { margin: 0; padding: 0 var(--ms-pad, 1ch);");
+ await Assert.That(LayoutCss.Fixed).Contains(".ms-box { --ms-pad: 1ch; margin: 0; padding: 0 var(--ms-pad);");
+ }
+
+ [Test]
+ public async Task Divider_InABoxWithoutPadding_ReachesOnlyToItsSides()
+ {
+ var tight = BlockLayout.Build(new Frame(new Stack([T("Above"), new Rule(), T("Below")])) { Padding = 0 }, 30).Render(MarkupFormat.Html, Registry);
+
+ await Assert.That(tight).Contains("