ArrowRight on a collapsed parent expands it. On an already-open
parent it moves the active-descendant to the first child. On a leaf
it is a no-op.
ArrowLeft on an open node collapses it. On a closed node (or
leaf) with a parent it moves the active-descendant to the parent.
On a root leaf it is a no-op.
Move actions internally verify that ad.highlightByValue actually
changed activeId (e.g. disabled skip rejection), and downgrade to
'noop' when it didn't - so consumers bound to (movedToChild) /
(movedToParent) only see state-change-truthful emissions.
onArrowRight / onArrowLeft are the inline-forward / inline-back
intents, not physical keys. CngxHierarchicalNav resolves the physical
arrow to its logical intent by writing direction (resolveInlineArrowKey)
before calling the strategy, so under rtl physical ArrowLeft routes to
onArrowRight. Custom strategies stay direction-naive and inherit correct
RTL behaviour for free.