diff --git a/packages/nodes/src/ceiling/panel.tsx b/packages/nodes/src/ceiling/panel.tsx
index 210976f8c3..c3cb2cb809 100644
--- a/packages/nodes/src/ceiling/panel.tsx
+++ b/packages/nodes/src/ceiling/panel.tsx
@@ -55,7 +55,7 @@ export function CeilingPanel() {
const parent = node?.parentId ? s.nodes[node.parentId as AnyNode['id']] : undefined
return parent?.type === 'level'
? getCeilingClampBound(parent.id, s.nodes, node?.polygon ?? [])
- : 6
+ : Number.POSITIVE_INFINITY
})
// Effective height: the stored custom height, or — for follows-mode
@@ -271,16 +271,34 @@ export function CeilingPanel() {
)}
{/* Presets write an explicit height (clamped to the bound), so
- clicking one on a follows-mode ceiling switches it to custom. */}
+ clicking one on a follows-mode ceiling switches it to custom.
+ A preset taller than the storey would clamp silently and look
+ like a dead button — disable it and name the real gate instead. */}
- {heightPresets.map((preset) => (
-
handleHeightChange(preset.height)}
- />
- ))}
+ {heightPresets.map((preset) => {
+ const fits = preset.height <= maxHeight
+ return (
+ handleHeightChange(preset.height)}
+ title={
+ fits
+ ? undefined
+ : `Taller than this level (${formatLinearMeasurement(maxHeight, unit, metricNotation)} available). Raise the level height first.`
+ }
+ />
+ )
+ })}
+ {Number.isFinite(maxHeight) && (
+
+ Limited by the level to {formatLinearMeasurement(maxHeight, unit, metricNotation)} —
+ raise the level height for a taller ceiling.
+
+ )}
diff --git a/packages/nodes/src/slab/panel.tsx b/packages/nodes/src/slab/panel.tsx
index 07e88ade22..b582b3ca97 100644
--- a/packages/nodes/src/slab/panel.tsx
+++ b/packages/nodes/src/slab/panel.tsx
@@ -279,9 +279,11 @@ export function SlabPanel() {
width={320}
>
+ {/* Range mirrors the 20 m storey cap; `clampSlabElevation` in the
+ write path stays the real bound against the level. */}