\n {/* Height is a CSS transition rather than an animated value: it is the\n one property here expressed in `em`, and CSS interpolates units\n natively where an animation library has to re-read them in px. */}\n
\n
\n {rows.map(({ item, depth, isTrail }, index) => (\n \n isTrail\n ? setTrail((current) => current.slice(0, depth))\n : handleItem(item)\n }\n reduceMotion={!!reduceMotion}\n />\n ))}\n
\n
\n
\n );\n}\n\ninterface RowProps {\n item: DrilldownMenuItem;\n depth: number;\n index: number;\n isTrail: boolean;\n onActivate: () => void;\n reduceMotion: boolean;\n}\n\n/**\n * Position and presence are deliberately on two different elements.\n *\n * The outer one owns x/y as a plain `animate` object, because that is the only\n * form Motion re-resolves when the values change under an unchanged animation.\n * Express the same thing as a variant target and a row that stays `visible`\n * while its index changes never moves — it silently keeps the slot it had, and\n * lands on top of whatever is there now.\n *\n * The inner one owns presence, so it can orchestrate the characters in and out\n * on a stagger. It drives its own removal through `usePresence`, which is what\n * lets the exit run per character instead of collapsing the row in one step.\n */\nfunction Row({\n item,\n depth,\n index,\n isTrail,\n onActivate,\n reduceMotion,\n}: RowProps) {\n const [isPresent, safeToRemove] = usePresence();\n\n // Backstop: if the exit animation never reports completion, the row would\n // stay mounted forever — invisible, and still in the tab order.\n React.useEffect(() => {\n if (isPresent) return;\n const timer = window.setTimeout(() => safeToRemove?.(), 900);\n return () => window.clearTimeout(timer);\n }, [isPresent, safeToRemove]);\n\n return (\n