+ the whole point is confirming something you cannot otherwise see.
+ The REGION is always mounted and only the bubble is conditional: a polite region has to
+ pre-exist in the accessibility tree for a later insertion to be announced, so one created
+ together with its own first message is unreliably read — and since `osd` clears after 2.2s,
+ conditioning the wrapper re-created it for every switch, not just the first (E10 review
+ round 1). Toaster.tsx carries the same rule and the same shape. */}
+
- )}
+ )}
+
{/* While a panel/menu is open or an auto-skip countdown is running, take over the browser/mouse
Back button (history popstate) so it closes/cancels that instead of dropping to the grid. */}
diff --git a/frontend/src/components/ui/ActionMenu.tsx b/frontend/src/components/ui/ActionMenu.tsx
index dd441fb5..23e4cc68 100644
--- a/frontend/src/components/ui/ActionMenu.tsx
+++ b/frontend/src/components/ui/ActionMenu.tsx
@@ -22,7 +22,15 @@ interface MenuItemBase {
id: string;
label: string;
icon?: ReactNode; // an element (so a caller can swap in a spinning icon while pending)
+ /** Off because it is BUSY (a mutation in flight): a native `disabled` button, skipped by roving
+ * focus. Use it when there is nothing to explain — the row will be back in a moment. */
disabled?: boolean;
+ /** Off because it does not APPLY here (no other list to move to, a built-in list you cannot
+ * rename): reachable but inert — `aria-disabled`, still focusable, still tooltip-capable, and it
+ * no-ops on activation. `disabled` would hide the very explanation the row exists to give, since
+ * a disabled button fires no pointer events (no `title`) and roving focus skips it, so neither a
+ * mouse nor a keyboard could ever read the `title` (E10 review round 1). */
+ inert?: boolean;
title?: string; // optional native tooltip (carries a longer hint the label alone can't)
/** Draw a divider above this item. Ignored on the first item — a menu never opens with a rule. */
separatorBefore?: boolean;
@@ -71,13 +79,16 @@ export function MenuItemList({