diff --git a/ui/src/components/DepthButtons.jsx b/ui/src/components/DepthButtons.jsx
index ab3e2f3..d36e1d6 100644
--- a/ui/src/components/DepthButtons.jsx
+++ b/ui/src/components/DepthButtons.jsx
@@ -35,6 +35,9 @@ export default function DepthButtons({ label, levels, depth, onPick, totalLabel
const known = depth != null
const current = !known ? '—' : (depth === 0 ? totalLabel : levels[depth - 1])
const step = (d) => onPick(Math.min(max, Math.max(0, d)))
+ // Capped so one long field name cannot push the rest of the toolbar off;
+ // past the cap the label truncates, with the full name in the title.
+ const widest = Math.min(18, Math.max(...[totalLabel, ...levels].map(l => String(l).length)))
// One joined control, in the style of the operation panel's
// Scale | Recode | Clone. The arrows used to be separate outlined boxes a
@@ -53,9 +56,13 @@ export default function DepthButtons({ label, levels, depth, onPick, totalLabel
title={`Collapse ${label.toLowerCase()} by one level`}
onClick={() => step((depth ?? 0) - 1)}>◂
+ {/* Sized to the axis's longest level, not the current one. Sized to
+ its content it resized on every step, so the ▸ moved out from
+ under the cursor between clicks. */}
diff --git a/ui/src/components/LayoutMenu.jsx b/ui/src/components/LayoutMenu.jsx
index 7db8102..f79d85a 100644
--- a/ui/src/components/LayoutMenu.jsx
+++ b/ui/src/components/LayoutMenu.jsx
@@ -123,15 +123,22 @@ export default function LayoutMenu({
${open ? 'border-blue-300 text-blue-700 bg-blue-50'
: 'border-gray-200 text-gray-600 hover:border-gray-400'}`}>
{active ? active.name : 'Unsaved'}
- {dirty && •}
+ {/* Always rendered, hidden when clean, so the button does not widen
+ by a dot the moment the pivot changes. */}
+ •▾
{/* Save is the common action and stays outside the menu, but only when
- there is something to save it to and it is yours to overwrite. */}
- {active?.can_edit && dirty && (
-