diff --git a/web/sections/BlockSettings.tsx b/web/sections/BlockSettings.tsx
index aa327d5..a919a08 100644
--- a/web/sections/BlockSettings.tsx
+++ b/web/sections/BlockSettings.tsx
@@ -134,7 +134,20 @@ export const BlockSettings = ({
- {steps.map((step) => (
+ {/*
+ Horizontal offers NEGATIVES, vertical does not.
+
+ A negative horizontal is how a block escapes the gutter its band applied — a video
+ that runs to the edge of the window inside an inset band. It becomes `-mx-*`, which
+ cancels the band's `px-*` exactly, so the value to pick is the band's own number.
+
+ Vertically there is nothing to cancel: a band's vertical padding is the room around
+ the whole band, and a block pulling itself into the one above is a bug.
+ */}
+ {(axis === 'x'
+ ? [...steps.filter((step) => step > 0).map((step) => -step).reverse(), ...steps]
+ : steps
+ ).map((step) => (
diff --git a/web/sections/PageBuilderPanel.tsx b/web/sections/PageBuilderPanel.tsx
index 89759f0..9bff87f 100644
--- a/web/sections/PageBuilderPanel.tsx
+++ b/web/sections/PageBuilderPanel.tsx
@@ -7,7 +7,7 @@ import { useClient } from 'hooks/useClient';
import { readableError } from '../errors';
import { SitePreview, useDevPort } from '../SitePreview';
import { usePreviewable, useViewMode } from '../ViewMode';
-import { paintSelection, type CanvasComponent, type CanvasSection } from '../canvas';
+import { paintMeasure, paintSelection, type CanvasComponent, type CanvasSection } from '../canvas';
/*
What the Spacing dialog calls a block, by kind.
@@ -226,7 +226,16 @@ export const PageBuilderPanel = () => {
const onDocument = useCallback(
(frameDocument: Document) => {
frameDoc.current = frameDocument;
- return paintSelection(frameDocument, {
+
+ /*
+ The measure, here as well as on Layout.
+
+ Where the content column ends is invisible until something is placed against it, and this is
+ the screen where things are placed. It also answers the question a negative margin asks: a
+ block escaping the gutter has to be seen escaping it, against the line it crossed.
+ */
+ const measure = paintMeasure(frameDocument);
+ const selection = paintSelection(frameDocument, {
sections,
onAddSection: () => handlersRef.current.add(),
onDeleteSection: (section) => handlersRef.current.remove(section.id),
@@ -245,6 +254,11 @@ export const PageBuilderPanel = () => {
onSpaceSection: (section) => handlersRef.current.spaceSection(section),
onSpaceComponent: (component) => handlersRef.current.spaceComponent(component),
});
+
+ return () => {
+ selection();
+ measure();
+ };
},
// `mode` is in here so switching to View gives SitePreview a NEW function, which runs the previous
// paint's cleanup and then paints nothing. That is what removes the chrome without a reload.