{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"internals-motion","type":"registry:lib","title":"Shared internals (motion)","description":"Animation primitives: easing, presence, stagger, in-view and motion tokens.","registryDependencies":["https://fancy-ui.rama.app/r/internals.json","https://fancy-ui.rama.app/r/internals-dom.json","https://fancy-ui.rama.app/r/utils.json"],"files":[{"path":"react/src/internals/motion/anchored.ts","content":"\"use client\";\n\n/**\n * The shared scale+opacity transition for floating surfaces — menus, selects,\n * pickers, popovers, tooltips, hover cards. One rung, one curve, one\n * geometry, so twelve panels stop each inventing their own.\n *\n * The name is a slight misnomer for the modal rung: a centred dialog is\n * anchored to nothing, and spends this same function with its own two\n * durations (`DURATIONS.base` in, `DURATIONS.exit` out) rather than the\n * anchored defaults. One geometry in one place was worth more than a synonym,\n * so the modal surfaces call `anchored` too and spell their rung at the call\n * site where the diff can see it.\n *\n * Two pieces that are deliberately kept separate:\n *\n * 1. `anchored()` — a css-only transition (scale + opacity, nothing else). It\n *    never emits `transform-origin`.\n * 2. `originFor()` — a pure lookup the CALLER writes as a static inline\n *    `style={{ transformOrigin }}`. Keeping the origin out of `css()` means it\n *    is inspectable in DevTools, survives a future exit transition, and is\n *    available to any CSS that wants to key off it — none of which is true\n *    of a value smuggled into a per-frame transition string.\n *\n * Why scale + opacity and nothing else: a panel that also translated would\n * fight `anchor-position.ts`'s `position: fixed; left/top` writes on the same\n * element (that module owns those three properties and touches neither\n * `transform` nor `transform-origin`, so the two never collide as written).\n * The travel is not lost, it moves into the origin: growing from the panel\n * edge nearest the trigger reads as \"this came out of that button\" far more\n * clearly than four pixels of slide ever did.\n *\n * SSR: nothing here touches `window` at module scope. `prefersReducedMotion()`\n * does, but a transition body only ever runs in the browser.\n */\n\nimport type { Side, Align } from \"../anchor-position\";\nimport { DURATIONS, JS_EASINGS } from \"./tokens\";\nimport { PRESETS } from \"./presets\";\nimport { REDUCED_MOTION_QUERY } from \"./media-query\";\nimport type { TransitionSpec, TransitionDirection } from \"./transitions\";\n\n/** Params for `anchored`. Every field is optional; the defaults ARE the\n * anchored rung, so `anchored(node, { side: resolvedSide })` is the whole\n * call site for eleven of the twelve panels. */\nexport interface AnchoredParams {\n\t/** The RESOLVED side the surface was placed on — `useAnchorPosition`'s\n\t * `onPlacement` value, never the requested one. Drives nothing but the\n\t * caller's own `transform-origin`; kept here so one params object feeds\n\t * both. Defaults to `\"bottom\"`, but callers always pass their own\n\t * requested side as the seed. */\n\tside?: Side;\n\t/** Entrance duration (ms). Defaults to `DURATIONS.fast`. */\n\tduration?: number;\n\t/** Exit duration (ms). Defaults to `DURATIONS.fast`. */\n\texitDuration?: number;\n\t/** Delay (ms), entrance only — an exit never waits. Defaults to 0. */\n\tdelay?: number;\n\t/** `false` drops the scale term, leaving an opacity-only fade (Tooltip).\n\t * Defaults to `true`. */\n\tscale?: boolean;\n\t/** Direction override, read fresh on every call, for a caller driving ONE\n\t * bidirectional transition instance (which reports `direction: \"both\"`,\n\t * and cannot distinguish enter from leave). Unset, direction comes from\n\t * `options.direction`. */\n\tentering?: boolean;\n}\n\n/**\n * One-shot read of the user's motion preference.\n *\n * Browser-only, and safe because of where it is called from: a transition body\n * never runs during SSR. NEVER call it at module scope, in a render path, or\n * inside a memo — for a live, reactive answer use `useReducedMotion()` from\n * `media-query.ts` instead. A listener would be pointless here: the value only\n * matters at the instant a one-shot transition starts, and a preference that\n * flips mid-flight should not retro-cancel an animation already on screen.\n *\n * `window.matchMedia` is resolved FRESH on every call, never memoised, for\n * the same reason `media-query.ts` documents at length: a test that overrides\n * `window.matchMedia` wholesale must be visible to the next call.\n */\nexport function prefersReducedMotion(): boolean {\n\tif (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return false;\n\treturn window.matchMedia(REDUCED_MOTION_QUERY).matches;\n}\n\n/**\n * Resolved side + alignment → the `transform-origin` keyword pair that puts\n * the growth origin on the panel edge NEAREST the anchor.\n *\n * The rule behind the twelve entries: the MAIN-axis keyword is the panel edge\n * facing the anchor (`bottom` → `top`, `top` → `bottom`, `left` → `right`,\n * `right` → `left`); the CROSS-axis keyword comes from `align`\n * (`start` → `left`/`top`, `center` → `center`, `end` → `right`/`bottom`).\n * Written out rather than computed so the table can be read against a design\n * doc at a glance. Output is always `\"<x> <y>\"`.\n *\n * Physical, never logical, and that is deliberate: `computePosition` is\n * physical throughout (`align: \"start\"` is `anchor.left`, unconditionally),\n * so a physical origin is the CONSISTENT choice under RTL rather than a bug.\n * `transform-origin` has no interoperable logical form anyway.\n */\nconst ORIGINS: Record<Side, Record<Align, string>> = {\n\tbottom: { start: \"left top\", center: \"center top\", end: \"right top\" },\n\ttop: { start: \"left bottom\", center: \"center bottom\", end: \"right bottom\" },\n\tleft: { start: \"right top\", center: \"right center\", end: \"right bottom\" },\n\tright: { start: \"left top\", center: \"left center\", end: \"left bottom\" },\n};\n\nexport function originFor(side: Side, align: Align = \"center\"): string {\n\treturn ORIGINS[side][align];\n}\n\n/** The entrance floor, read from the shared geometry table rather than\n * retyped, so a change to the creative direction's scale floor lands here\n * too. The `??` is type narrowing for `PresetGeometry.scale`'s optionality —\n * `PRESETS.scale` has always had one, and `anchored.test.tsx` asserts against\n * the table entry rather than the literal so a real removal fails loudly. */\nconst ENTER_FLOOR = PRESETS.scale.scale ?? 0.92;\n\n/** An exit travels HALF the delta of an entrance (0.96 against 0.92):\n * leaving is a smaller gesture than arriving, and a full-depth collapse on\n * dismiss reads as the panel being sucked away rather than simply closing. */\nconst EXIT_FLOOR = 1 - (1 - ENTER_FLOOR) / 2;\n\n/**\n * A css-only, direction-aware transition for an anchored floating surface.\n * Entrance: `JS_EASINGS.out` over `DURATIONS.fast`, growing from the scale\n * floor. Exit: `JS_EASINGS.in`, half the depth, and never delayed — a\n * dismissal that waits before starting reads as an unresponsive UI.\n *\n * Reduced motion collapses `duration` (and `delay`) to 0, which makes\n * `runTransition` skip `element.animate()` entirely rather than running a\n * zero-length animation — the same fast path `animate.test.ts` pins down.\n */\nexport function anchored(\n\t_node: Element,\n\tparams: AnchoredParams = {},\n\toptions?: { direction: TransitionDirection }\n): TransitionSpec {\n\t// `\"both\"` (one bidirectional transition instance) counts as entering:\n\t// it is the only direction the caller cannot resolve for us, and an\n\t// arrival curve is the more common perceived motion of the two when one\n\t// has to stand in for both. `params.entering` wins over\n\t// `options.direction` outright — a caller that passes it has better\n\t// information than the directive does.\n\tconst entering = params.entering ?? options?.direction !== \"out\";\n\tconst reduced = prefersReducedMotion();\n\n\tconst duration = reduced\n\t\t? 0\n\t\t: entering\n\t\t\t? (params.duration ?? DURATIONS.fast)\n\t\t\t: (params.exitDuration ?? DURATIONS.fast);\n\tconst delay = reduced ? 0 : entering ? (params.delay ?? 0) : 0;\n\tconst easing = entering ? JS_EASINGS.out : JS_EASINGS.in;\n\n\tconst floor = params.scale === false ? 1 : entering ? ENTER_FLOOR : EXIT_FLOOR;\n\n\treturn {\n\t\tdelay,\n\t\tduration,\n\t\teasing,\n\t\t// `t` arrives ALREADY eased (the sampler evaluates `easing(i / n)`\n\t\t// before calling `css`), so this interpolates linearly in `t` exactly\n\t\t// like `transitions.ts` does. A floor of 1 emits no `transform` at all\n\t\t// rather than a no-op `scale(1)`, which keeps an opacity-only panel\n\t\t// from acquiring a compositing layer and a stacking context it never\n\t\t// asked for.\n\t\tcss: (t) =>\n\t\t\tfloor === 1 ? `opacity: ${t}` : `opacity: ${t}; transform: scale(${floor + (1 - floor) * t})`,\n\t};\n}\n\n/** The `data-state` values a transitioning surface reports. There is no\n *  \"closed\": a closed surface is not in the DOM. Deliberately two values, not\n *  three — a 150–300 ms entrance has nothing that needs to key off \"opening\",\n *  and two values means one static attribute plus two one-line handlers\n *  instead of four.\n *\n *  The Svelte side pairs this type with a `markSurfaceState(event, state)`\n *  helper that writes `data-state` IMPERATIVELY from a transition event\n *  handler. That helper is NOT ported (divergence D-2): it exists solely\n *  because Svelte marks a closing `{#if}` branch inert and its scheduler skips\n *  inert effects, so a reactive attribute inside a closing block never reaches\n *  the DOM. React re-renders an exiting surface normally, so a port writes\n *  `data-state={presence.surfaceState}` as an ordinary attribute — same DOM,\n *  same CSS hooks, same two values. */\nexport type SurfaceState = \"open\" | \"closing\";\n","type":"registry:lib","target":"@ui/fancy/internals/motion/anchored.ts"},{"path":"react/src/internals/motion/animate.ts","content":"\"use client\";\n\n/**\n * The transition sampler — the one mechanism every `transition:`-driven\n * component in this library runs through, reproduced from the framework's own\n * `animate()` so the React port animates the SAME curve rather than a\n * hand-matched approximation of it.\n *\n * The decisive fact behind the whole design: a css-only transition is already\n * WAAPI. The framework samples `css(t, 1 - t)` at\n * `n = Math.ceil(duration / (1000 / 60))` points, converts each string to a\n * keyframe object and hands the array to `element.animate()`. Running that same\n * algorithm against the same `css()` produces a byte-identical keyframe array\n * and therefore a pixel-identical animation — fidelity is structural here, not\n * aspirational. It also inherits, free: the `duration: 0` fast path reduced\n * motion relies on, reversal from an in-flight position rather than a full\n * restart, and testability through an `Element.prototype.animate` stub.\n *\n * The curve lives entirely in the SAMPLE POSITIONS (`t = t1 + delta *\n * easing(i / n)`), never in a WAAPI `easing` option — which is exactly why\n * every `css(t, u)` in `transitions.ts` and `anchored.ts` interpolates\n * linearly in `t`: by the time `css` is called, `t` has already been eased.\n *\n * Two things the source has and this does not, both deliberate:\n *\n * - `tick`. `TransitionSpec` has no `tick` field (nothing in this library uses\n *   one), so the per-frame JS loop and the `reset()` handle it exists for are\n *   both gone with it.\n * - The `overflow: hidden` inline-style workaround for a Safari < 18 bug. It\n *   triggers only when a sampled keyframe declares `overflow: hidden`, and no\n *   `css()` in this package emits `overflow` at all — porting it would ship a\n *   branch no test can reach.\n *\n * There is deliberately NO \"the host cannot animate\" `setTimeout` fallback:\n * the source has none, and inventing one would put an untested code path into\n * production. jsdom is handled by the `Element.prototype.animate` stub in\n * `test-setup.ts`, which is a hard requirement of this module's suite.\n *\n * SSR: nothing here touches a browser global at module scope. `runTransition`\n * itself is only ever called from a layout effect.\n */\n\nimport type { TransitionSpec } from \"./transitions\";\nimport { linear } from \"./easing\";\n\nfunction noop(): void {}\n\n/** `float` and `offset` are special-cased in compliance with the spec, and a\n * custom property (`--x`) is never renamed — a `--`-prefixed key reaches\n * `element.animate()` exactly as written. Everything else is `kebab-case` →\n * `camelCase`. */\nfunction cssPropertyToCamelcase(style: string): string {\n\t// in compliance with spec\n\tif (style === \"float\") return \"cssFloat\";\n\tif (style === \"offset\") return \"cssOffset\";\n\n\t// do not rename custom @properties\n\tif (style.startsWith(\"--\")) return style;\n\n\tconst parts = style.split(\"-\");\n\tif (parts.length === 1) return parts[0] ?? style;\n\treturn (\n\t\t(parts[0] ?? \"\") +\n\t\tparts\n\t\t\t.slice(1)\n\t\t\t.map((word) => (word[0] ?? \"\").toUpperCase() + word.slice(1))\n\t\t\t.join(\"\")\n\t);\n}\n\n/**\n * One `css(t, u)` declaration string → one WAAPI keyframe object.\n *\n * Split on `;`, split each part at the FIRST `:`, and **break** — not\n * `continue` — on a malformed part: a declaration missing its value ends the\n * parse rather than being skipped, which is the source's behaviour and is what\n * keeps a trailing `;` harmless.\n */\nexport function cssToKeyframe(css: string): Keyframe {\n\tconst keyframe: Keyframe = {};\n\tconst parts = css.split(\";\");\n\tfor (const part of parts) {\n\t\tconst [property, value] = part.split(\":\");\n\t\tif (!property || value === undefined) break;\n\n\t\tconst formattedProperty = cssPropertyToCamelcase(property.trim());\n\t\tkeyframe[formattedProperty] = value.trim();\n\t}\n\treturn keyframe;\n}\n\n/** The handle one transition leg hands back. The counterpart of a running leg\n * is read (`t()`) and then torn down (`abort()`) by the leg that replaces it. */\nexport interface TransitionRun {\n\t/** Current eased position in `css()`'s own `t` space: `1 - to` before the\n\t * delay elapses, the interpolated position while the main animation runs,\n\t * and `to` once it has finished. Read by a reversing counterpart so a\n\t * reopen mid-close starts from where the close actually got to. */\n\tt(): number;\n\t/** Cancel without finishing. Also nulls the animation's `effect` (a\n\t * Chromium leak) and replaces `onfinish` with a no-op, because a cancelled\n\t * animation can still fire `onfinish` in rare cases. */\n\tabort(): void;\n\t/** Silence this run's `onFinish` without stopping it. Called on the\n\t * counterpart the instant a new leg starts, so a superseded leg cannot\n\t * unmount the node the new leg is animating back in. */\n\tdeactivate(): void;\n}\n\n/**\n * Runs one transition leg on `element`, from its current position to `to`\n * (`1` = visible, `0` = hidden), and calls `onFinish` when it lands.\n *\n * Order matters in five places, and every one of them is visible if broken:\n *\n * 1. `counterpart?.deactivate()` runs FIRST, before anything else.\n * 2. A falsy `spec.duration` finishes SYNCHRONOUSLY and never calls\n *    `element.animate()` — the reduced-motion fast path, and the reason a\n *    reduced-motion close completes inside the same layout effect, before\n *    paint.\n * 3. A leading dummy animation is ALWAYS created, even at `delay: 0`. Its\n *    keyframes pin the hidden state (`css(0, 1)`, twice) only for a fresh\n *    intro, and are empty otherwise. Pinning is what stops a DELAYED entrance\n *    painting one frame at rest first; keeping the dummy at `delay: 0` is what\n *    defers the real keyframes until the DOM has updated. The consequence for\n *    every caller: the main animation always starts ASYNCHRONOUSLY, in the\n *    dummy's `onfinish`.\n * 4. Inside that `onfinish`: cancel the dummy, then READ `counterpart.t()`,\n *    then abort the counterpart — the read strictly before the abort, or a\n *    reversal restarts from the far end instead of from the in-flight\n *    position.\n * 5. The sample loop runs `i = 0` to `i <= n` INCLUSIVE (n + 1 keyframes);\n *    `n` must be an integer or the `to` value is missed entirely.\n */\nexport function runTransition(\n\telement: Element,\n\tspec: TransitionSpec,\n\tto: 0 | 1,\n\tcounterpart: TransitionRun | undefined,\n\tonFinish: () => void\n): TransitionRun {\n\tconst isIntro = to === 1;\n\n\tcounterpart?.deactivate();\n\n\tif (!spec.duration) {\n\t\tonFinish();\n\n\t\treturn {\n\t\t\tabort: noop,\n\t\t\tdeactivate: noop,\n\t\t\tt: () => to,\n\t\t};\n\t}\n\n\tconst { delay = 0, css, easing = linear } = spec;\n\n\tconst dummyKeyframes: Keyframe[] = [];\n\n\tif (isIntro && counterpart === undefined) {\n\t\tconst styles = cssToKeyframe(css(0, 1));\n\t\tdummyKeyframes.push(styles, styles);\n\t}\n\n\tlet getT = () => 1 - to;\n\t// Reassigned by `deactivate()`, exactly as the source reassigns its own\n\t// `on_finish` parameter.\n\tlet finish = onFinish;\n\n\t// A dummy animation that lasts as long as the delay. In the common case\n\t// that it is `0` we keep it anyway, so the real CSS keyframes are not\n\t// created until the DOM has updated. `fill: \"forwards\"` prevents the\n\t// element rendering without styles applied.\n\tlet animation = element.animate(dummyKeyframes, { duration: delay, fill: \"forwards\" });\n\n\tanimation.onfinish = () => {\n\t\t// Remove the dummy from the stack so it cannot conflict with the main\n\t\t// animation.\n\t\tanimation.cancel();\n\n\t\t// For bidirectional transitions we start from the CURRENT position\n\t\t// rather than doing a full intro/outro — hence the read before the abort.\n\t\tconst t1 = counterpart?.t() ?? 1 - to;\n\t\tcounterpart?.abort();\n\n\t\tconst delta = to - t1;\n\t\tconst duration = spec.duration * Math.abs(delta);\n\t\tconst keyframes: Keyframe[] = [];\n\n\t\tif (duration > 0) {\n\t\t\t// `n` must be an integer, or we risk missing the `to` value.\n\t\t\tconst n = Math.ceil(duration / (1000 / 60));\n\n\t\t\tfor (let i = 0; i <= n; i += 1) {\n\t\t\t\tconst t = t1 + delta * easing(i / n);\n\t\t\t\tkeyframes.push(cssToKeyframe(css(t, 1 - t)));\n\t\t\t}\n\n\t\t\tgetT = () => {\n\t\t\t\t// `currentTime` is `CSSNumberish | null`, not `number`: it is\n\t\t\t\t// `null` on an idle or cancelled animation, and a\n\t\t\t\t// `CSSNumericValue` on a timeline that reports progress rather\n\t\t\t\t// than milliseconds. Either one divided by `duration` is a\n\t\t\t\t// value `easing()` cannot use — `null` silently coerces to `0`\n\t\t\t\t// and a `CSSNumericValue` gives `NaN`, which `css(t, 1 - t)`\n\t\t\t\t// would then bake into every sampled keyframe as the literal\n\t\t\t\t// string \"NaN\" and the reversing leg would jump. A leg whose\n\t\t\t\t// clock cannot be read has made no progress worth honouring,\n\t\t\t\t// so it reports the position it started from.\n\t\t\t\tconst time = animation.currentTime;\n\t\t\t\tif (typeof time !== \"number\" || !Number.isFinite(time)) return t1;\n\t\t\t\treturn t1 + delta * easing(time / duration);\n\t\t\t};\n\t\t}\n\n\t\t// No `easing` option and no `delay` option: the curve is already baked\n\t\t// into the sample positions above, and the delay was the dummy's job.\n\t\tanimation = element.animate(keyframes, { duration, fill: \"forwards\" });\n\n\t\tanimation.onfinish = () => {\n\t\t\tgetT = () => to;\n\t\t\tfinish();\n\t\t};\n\t};\n\n\treturn {\n\t\tabort: () => {\n\t\t\tanimation.cancel();\n\t\t\t// Prevents a memory leak in Chromium.\n\t\t\t(animation as unknown as { effect: AnimationEffect | null }).effect = null;\n\t\t\t// Prevents `onfinish` firing after `cancel()`, which happens in\n\t\t\t// some rare cases.\n\t\t\tanimation.onfinish = noop;\n\t\t},\n\t\tdeactivate: () => {\n\t\t\tfinish = noop;\n\t\t},\n\t\tt: () => getT(),\n\t};\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/animate.ts"},{"path":"react/src/internals/motion/easing.ts","content":"\"use client\";\n\n/**\n * The two curves `JS_EASINGS` names, inlined because the framework's easing\n * module cannot become a runtime dependency of this package (divergence D-7 in\n * the internals contract). Closed forms, copied byte-for-byte — do NOT \"clean\n * up\" the endpoint guards; they are the shape of the curve at exactly the\n * points `css()` is sampled at.\n *\n * Pure data-shaped functions: no side effects, no browser globals, safe to\n * import at module scope during SSR.\n */\n\n/** Departure curve. `t === 0` is guarded so the curve starts at exactly 0\n * rather than at `2 ** -10`. */\nexport function expoIn(t: number): number {\n\treturn t === 0.0 ? t : Math.pow(2.0, 10.0 * (t - 1.0));\n}\n\n/** Arrival curve. `t === 1` is guarded so the curve ends at exactly 1 rather\n * than at `1 - 2 ** -10`. */\nexport function expoOut(t: number): number {\n\treturn t === 1.0 ? t : 1.0 - Math.pow(2.0, -10.0 * t);\n}\n\n/** The identity curve. Not referenced by `JS_EASINGS`; exported for a caller\n * that wants to opt a single transition out of easing entirely without\n * hand-rolling `(t) => t` at the call site. */\nexport const linear: (t: number) => number = (t) => t;\n","type":"registry:lib","target":"@ui/fancy/internals/motion/easing.ts"},{"path":"react/src/internals/motion/haptics.ts","content":"\"use client\";\n\n/**\n * Optional tactile feedback via `navigator.vibrate` — a touch-only, opt-in\n * add-on (Pressable's `haptic` prop, StatusMorph's `haptic` prop). Nothing\n * here ever throws: the Vibration API is unsupported on most desktop\n * browsers and can be silently refused by a browser policy even where it\n * exists (Safari on iOS has never implemented it, for instance), and a\n * missing rumble is a non-event a user never notices — a thrown error\n * breaking the surrounding click handler very much would be.\n */\n\nexport const HAPTIC_PATTERNS = {\n\tlight: 10,\n\tmedium: 25,\n\theavy: 50,\n\tsuccess: [15, 60, 15],\n\terror: [60, 60, 60, 60, 60],\n} as const;\nexport type HapticPattern = keyof typeof HAPTIC_PATTERNS;\n\n/** Whether this environment can even attempt a vibration. Checked before\n * `vibrate()` calls into the API, and exported separately so a component\n * can skip attaching pointer-tracking work entirely when it never could\n * fire (e.g. gating a touch-only listener without needing to wait for a\n * failed call to find that out). */\nexport function canVibrate(): boolean {\n\treturn typeof navigator !== \"undefined\" && typeof navigator.vibrate === \"function\";\n}\n\n/** A named pattern resolves to its numeric/array value; a raw number or\n * array passes straight through. Array patterns are copied out of the\n * frozen `HAPTIC_PATTERNS` tuple so the caller (and `navigator.vibrate`,\n * which mutates nothing but shouldn't be handed a `readonly` array either)\n * never holds a reference into shared, `as const` state. */\nfunction resolvePattern(pattern: HapticPattern | number | number[]): number | number[] {\n\tif (typeof pattern !== \"string\") return pattern;\n\tconst value = HAPTIC_PATTERNS[pattern];\n\treturn typeof value === \"number\" ? value : [...value];\n}\n\n/**\n * Fire a vibration pattern. Returns whether the browser accepted the\n * request (`navigator.vibrate`'s own return value) — `false` for\n * \"unsupported\", \"not called from a user gesture\", \"the tab is\n * backgrounded\", or any other reason the platform declines. Never throws:\n * some browsers throw rather than return `false` for a policy refusal, so\n * the whole thing is wrapped rather than trusting the spec's \"just returns\n * a boolean\" description. The `!== false` coerces `navigator.vibrate`'s\n * result rather than returning it verbatim: lib.dom types the method as\n * always returning `boolean`, but that is a type-level promise the DOM spec\n * does not actually keep — a user agent is free to return `undefined`, and\n * without the coercion this function's own `boolean` return type would be a\n * runtime lie for a caller that checks `=== false`.\n */\nexport function vibrate(pattern: HapticPattern | number | number[]): boolean {\n\ttry {\n\t\tif (!canVibrate()) return false;\n\t\treturn navigator.vibrate(resolvePattern(pattern)) !== false;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/haptics.ts"},{"path":"react/src/internals/motion/in-view.ts","content":"\"use client\";\n\n/**\n * The primitive every \"reveal on scroll\" / \"notice when visible\" component in\n * this family uses instead of hand-rolling its own IntersectionObserver.\n * Four components elsewhere in this library (blur-reveal, box-reveal,\n * line-reveal, number-ticker) each carry a near-identical\n * `new IntersectionObserver(...)` block, differing only in whether they\n * honour `once: false` and whether `root`/`rootMargin` are exposed at all —\n * this is the single, parameterized version those four independently\n * reinvented, written once for the ten components that need it here.\n *\n * Two layers, as everywhere in `internals/`: `observeInView(node, options)` is\n * the framework-free core (its own suite drives it directly against a\n * hand-built node, no React), and `useInView(node, options)` is the hook the\n * components use.\n */\n\nimport { useRef, useState } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../dom/ssr\";\nimport { useEventCallback } from \"../dom/use-event-callback\";\nimport { useLiveRef } from \"../dom/use-live-ref\";\n\nexport interface InViewOptions {\n\t/** Disconnect after the first time the node becomes visible. Default true. */\n\tonce?: boolean;\n\t/** IntersectionObserver threshold(s). Default 0.1. */\n\tthreshold?: number | number[];\n\t/** IntersectionObserver rootMargin. Default \"0px\". */\n\trootMargin?: string;\n\t/** IntersectionObserver root. Default null (the viewport). */\n\troot?: Element | Document | null;\n\t/** Called with the current intersecting state on every observer callback. */\n\tonChange: (inView: boolean, entry?: IntersectionObserverEntry) => void;\n}\n\n/** What `observeInView` hands back. `update()` swaps the live options in\n * place; `destroy()` disconnects for good. */\nexport interface InViewHandle {\n\tupdate(options: InViewOptions): void;\n\tdestroy(): void;\n}\n\nconst DEFAULT_THRESHOLD = 0.1;\nconst DEFAULT_ROOT_MARGIN = \"0px\";\n\nfunction observerInit(options: InViewOptions): IntersectionObserverInit {\n\treturn {\n\t\tthreshold: options.threshold ?? DEFAULT_THRESHOLD,\n\t\trootMargin: options.rootMargin ?? DEFAULT_ROOT_MARGIN,\n\t\troot: options.root ?? null,\n\t};\n}\n\n/** Whether two option sets would produce the SAME IntersectionObserver\n * constructor arguments — the only thing that actually requires tearing\n * down and rebuilding the observer (see `update()` below). */\nfunction sameObserverInit(a: InViewOptions, b: InViewOptions): boolean {\n\tconst ia = observerInit(a);\n\tconst ib = observerInit(b);\n\treturn (\n\t\tJSON.stringify(ia.threshold) === JSON.stringify(ib.threshold) &&\n\t\tia.rootMargin === ib.rootMargin &&\n\t\tia.root === ib.root\n\t);\n}\n\nfunction noop(): void {}\n\nexport function observeInView(node: Element, opts: InViewOptions): InViewHandle {\n\tif (typeof window === \"undefined\" || typeof IntersectionObserver === \"undefined\") {\n\t\t// SSR, or a browser old enough to lack IntersectionObserver entirely:\n\t\t// report visible immediately rather than leaving the caller's content\n\t\t// stuck in whatever \"not yet revealed\" state it renders by default —\n\t\t// failing visible beats failing invisible for a real reader on a real\n\t\t// page. There is nothing to tear down, so `update`/`destroy` are inert.\n\t\topts.onChange(true);\n\t\treturn { update: noop, destroy: noop };\n\t}\n\n\t// The observer callback reads through `current`, not the `opts` closed\n\t// over above — so swapping in a new `onChange` (or flipping `once`) via\n\t// update() takes effect on the observer's NEXT fire without needing to\n\t// disconnect and recreate it.\n\tlet current = opts;\n\tlet observer: IntersectionObserver | undefined;\n\t// A real IntersectionObserver keeps no \"have I already fired\" state a\n\t// caller can inspect — disconnecting it just stops callbacks. So when\n\t// `once: true` has already delivered its one `onChange(true)` and\n\t// disconnected, that fact lives ONLY in this flag, not in the (now gone)\n\t// observer instance. Without it, a later `update()` that changes\n\t// threshold/rootMargin/root — a reactive prop on the consuming\n\t// component, not a static one — rebuilds a brand-new observer that knows\n\t// nothing about the earlier fire, re-observes the (already revealed)\n\t// node, and delivers a SECOND `onChange(true)` the moment it next\n\t// intersects. `firedOnce` survives every rebuild and makes `build()` a\n\t// no-op once set, so \"once\" really does mean once for the lifetime of\n\t// this handle.\n\tlet firedOnce = false;\n\n\tfunction build(o: InViewOptions) {\n\t\tobserver?.disconnect();\n\t\tobserver = undefined;\n\t\tif (firedOnce) return;\n\t\tobserver = new IntersectionObserver((entries) => {\n\t\t\tfor (const entry of entries) {\n\t\t\t\tcurrent.onChange(entry.isIntersecting, entry);\n\t\t\t\tif (entry.isIntersecting && (current.once ?? true)) {\n\t\t\t\t\tfiredOnce = true;\n\t\t\t\t\tobserver?.disconnect();\n\t\t\t\t\tobserver = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}, observerInit(o));\n\t\tobserver.observe(node);\n\t}\n\n\tbuild(current);\n\n\treturn {\n\t\tupdate(newOpts: InViewOptions) {\n\t\t\t// threshold/rootMargin/root are the IntersectionObserver's own\n\t\t\t// constructor arguments — only changing one of those needs a real\n\t\t\t// new observer. `once`/`onChange` are read fresh off `current`\n\t\t\t// every fire, so they never trigger a rebuild: a component that\n\t\t\t// passes an inline `onChange` closure creates a new function\n\t\t\t// identity on every render, and re-observing for that alone would\n\t\t\t// throw away more than intended. When a rebuild IS needed after\n\t\t\t// `once: true` has already fired, `build()`'s own `firedOnce`\n\t\t\t// check (above) keeps it a no-op rather than re-observing and\n\t\t\t// risking a second `onChange(true)`.\n\t\t\tconst needsRebuild = !sameObserverInit(current, newOpts);\n\t\t\tcurrent = newOpts;\n\t\t\tif (needsRebuild) build(newOpts);\n\t\t},\n\t\tdestroy() {\n\t\t\tobserver?.disconnect();\n\t\t},\n\t};\n}\n\nexport interface UseInViewOptions extends Omit<InViewOptions, \"onChange\"> {\n\t/** Optional here (it is required on the core): the hook already returns the\n\t * state, so a consumer only supplies this when it needs the entry itself. */\n\tonChange?: InViewOptions[\"onChange\"];\n\t/** `false` observes nothing at all. Default true. */\n\tenabled?: boolean;\n}\n\n/**\n * Returns whether `node` is currently intersecting, and forwards every\n * observer fire to `onChange`.\n *\n * Takes the NODE, not a ref (convention C-1): a node created by a conditional\n * or by a presence-gated subtree is still `null` when a `[]`-deps effect\n * fires, and that effect never re-runs.\n *\n * A LAYOUT effect, not a passive one: the no-IntersectionObserver branch calls\n * `onChange(true)` synchronously, and a passive effect would paint one frame\n * of hidden content before the fail-visible answer landed. Constructing the\n * observer costs nothing measurable.\n *\n * The dependency array is exactly the core's own rebuild rule: `threshold`,\n * `rootMargin` and `root` are the observer's constructor arguments and force a\n * new one; `once` and `onChange` are read live on every fire and must NOT —\n * an inline `onChange` closure changes identity every render, and re-observing\n * for that alone would throw away more than intended. `firedOnce` lives in a\n * ref so it survives a rebuild but not an unmount, matching the core handle's\n * per-instance lifetime.\n *\n * SSR: `false`, matching an un-run observer. A consumer that needs the\n * revealed state in the server HTML uses the `initial=\"visible\"` pattern\n * `Reveal` already has.\n */\nexport function useInView(node: Element | null, options: UseInViewOptions = {}): boolean {\n\tconst { once, threshold, rootMargin, root, enabled = true, onChange } = options;\n\n\tconst [inView, setInView] = useState(false);\n\tconst onceRef = useLiveRef(once);\n\tconst handleChange = useEventCallback(onChange);\n\tconst firedOnce = useRef(false);\n\n\t// `threshold` is legitimately an array, so its identity is not a usable\n\t// dependency; the core compares the same way, through JSON.\n\tconst thresholdKey = JSON.stringify(threshold ?? DEFAULT_THRESHOLD);\n\n\tuseIsomorphicLayoutEffect(() => {\n\t\tif (!node || !enabled) return;\n\t\t// The core's own post-fire rule, hoisted to hook scope so a rebuild\n\t\t// (a changed threshold/rootMargin/root) cannot re-observe an element\n\t\t// whose single `once` fire has already been delivered.\n\t\tif (firedOnce.current) return;\n\n\t\tconst handle = observeInView(node, {\n\t\t\t// A getter, not a captured value: `once` is read off this object on\n\t\t\t// every fire, so flipping it takes effect without a rebuild.\n\t\t\tget once() {\n\t\t\t\treturn onceRef.current;\n\t\t\t},\n\t\t\tthreshold,\n\t\t\trootMargin,\n\t\t\troot,\n\t\t\t// Rest args, forwarded as received: the fail-visible branch calls\n\t\t\t// `onChange(true)` with ONE argument, and re-expanding it to\n\t\t\t// `(visible, entry)` would hand the consumer an explicit\n\t\t\t// `undefined` second argument the core never sent.\n\t\t\tonChange: (...args) => {\n\t\t\t\tconst [visible] = args;\n\t\t\t\tsetInView(visible);\n\t\t\t\tif (visible && (onceRef.current ?? true)) firedOnce.current = true;\n\t\t\t\thandleChange(...args);\n\t\t\t},\n\t\t});\n\n\t\treturn () => {\n\t\t\thandle.destroy();\n\t\t};\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps -- `threshold` is\n\t\t// covered by `thresholdKey`; `once`/`onChange` are live by design.\n\t}, [node, enabled, thresholdKey, rootMargin, root]);\n\n\treturn inView;\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/in-view.ts"},{"path":"react/src/internals/motion/media-query.ts","content":"\"use client\";\n\n/**\n * Live `matchMedia` state as a hook.\n *\n * `useSyncExternalStore`, and this file is the archetype for choosing it: a\n * store React does not own, mutated outside React's knowledge, that must not\n * tear across a concurrent render — and whose `getServerSnapshot` returns\n * `fallback` for the server render AND the hydration render, which eliminates\n * the whole mismatch class rather than papering over it. A\n * `useState` + `useEffect` version reaches the same end state with one extra\n * committed frame and a real hydration mismatch the moment someone\n * \"optimises\" the initializer into a lazy `window.matchMedia` read.\n *\n * The Svelte source's `start()`/`stop()` pair has no counterpart here (D-4): it\n * exists only because that framework has no lifecycle-bound reactive\n * primitive, so the factory had to be constructible during SSR before any\n * effect ran. `useSyncExternalStore` IS that lifecycle, with the hydration\n * guarantee on top. The consuming pattern collapses from three lines to\n * `const reduced = useReducedMotion();`.\n *\n * `getSnapshot` is called by React on every render and again after every store\n * change, so it must not allocate: `window.matchMedia(query)` mints a NEW\n * `MediaQueryList` on each call, and one `useReducedMotion()` in a\n * scroll-driven tree turns that into garbage on every frame. The list is\n * therefore memoised per query, and dropped again on the two events that can\n * make a memoised one wrong:\n *\n * - `window.matchMedia` itself being replaced. The cache is keyed on the\n *   current function as well as the query, which is what keeps the source's\n *   own rationale intact: a test that overrides `matchMedia` wholesale\n *   installs a different function, the key stops matching, and the very next\n *   call resolves against the new implementation.\n * - A `change` event actually firing. A real `MediaQueryList.matches` is live\n *   — the browser updates the object you already hold — but a stub that\n *   captures `matches` at construction is not, and re-resolving once per real\n *   preference change (a rare, user-driven event) costs nothing and makes the\n *   hook correct against both. The subscription itself stays on the list it\n *   was added to, so no listener is stranded.\n *\n * Between those, every render reuses one list. `matches` is a boolean, so\n * snapshot identity is a non-issue either way.\n */\n\nimport { useCallback, useSyncExternalStore } from \"react\";\n\nexport const REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\";\n\nfunction canMatchMedia(): boolean {\n\treturn typeof window !== \"undefined\" && typeof window.matchMedia === \"function\";\n}\n\n/** The `window.matchMedia` the cache below was built against. A different\n *  function means a different implementation, so the cache is dropped. */\nlet cachedImpl: typeof window.matchMedia | undefined;\nlet cache = new Map<string, MediaQueryList>();\n\n/**\n * The `MediaQueryList` for `query`, allocated once per (implementation, query)\n * pair. Called through `window.` rather than a saved reference, because\n * `matchMedia` is a `Window` method and an unbound call throws in a real\n * browser; the saved reference is only ever compared, never invoked.\n */\nfunction mediaQueryList(query: string): MediaQueryList {\n\tif (cachedImpl !== window.matchMedia) {\n\t\tcachedImpl = window.matchMedia;\n\t\tcache = new Map();\n\t}\n\n\tconst existing = cache.get(query);\n\tif (existing) return existing;\n\n\tconst mql = window.matchMedia(query);\n\tcache.set(query, mql);\n\treturn mql;\n}\n\n/** Drop one query's memoised list, so the next snapshot resolves a fresh one. */\nfunction invalidate(query: string): void {\n\tcache.delete(query);\n}\n\n/**\n * `true` while `query` matches. `fallback` is the answer on the server, during\n * hydration, and on a host without `matchMedia` at all.\n */\nexport function useMediaQuery(query: string, fallback = false): boolean {\n\tconst subscribe = useCallback(\n\t\t(onStoreChange: () => void) => {\n\t\t\tif (!canMatchMedia()) return () => {};\n\n\t\t\tconst mql = mediaQueryList(query);\n\t\t\t// Invalidate BEFORE waking React, so the snapshot it reads next is\n\t\t\t// resolved against the media state that just changed.\n\t\t\tconst handleChange = () => {\n\t\t\t\tinvalidate(query);\n\t\t\t\tonStoreChange();\n\t\t\t};\n\n\t\t\tmql.addEventListener(\"change\", handleChange);\n\t\t\treturn () => {\n\t\t\t\tmql.removeEventListener(\"change\", handleChange);\n\t\t\t};\n\t\t},\n\t\t[query]\n\t);\n\n\tconst getSnapshot = useCallback(() => {\n\t\tif (!canMatchMedia()) return fallback;\n\t\treturn mediaQueryList(query).matches;\n\t}, [query, fallback]);\n\n\tconst getServerSnapshot = useCallback(() => fallback, [fallback]);\n\n\treturn useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n/** `useMediaQuery(REDUCED_MOTION_QUERY, false)` — reduced motion is never\n * assumed before the browser has actually been asked. */\nexport function useReducedMotion(): boolean {\n\treturn useMediaQuery(REDUCED_MOTION_QUERY, false);\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/media-query.ts"},{"path":"react/src/internals/motion/presence-core.ts","content":"\"use client\";\n\n/**\n * Shared core: the framework-free state machine behind `usePresence`\n * (`./presence.ts`), split into its own module so a Vue package can drive\n * the identical mount/unmount clock. Tracked in `shared-cores.json` at the\n * repo root as a byte-identical file across every framework package this\n * library ships — see that manifest and `scripts/check-shared-cores.mjs`\n * for the identity/purity gate this file must keep passing.\n *\n * Framework-free means exactly that: no `import ... from \"react\"` (or\n * \"svelte\"/\"vue\"), no bundler env reads, no `window`/`document`/`navigator`\n * read at module scope. DOM types (`HTMLElement`, `Element`) are fine — they\n * come from `lib.dom.d.ts`, not from a framework — and so is calling into a\n * DOM API from inside a function, which only ever happens once a real node\n * is handed in through `register`.\n *\n * `presence.ts` carries the design rationale for the clock itself (why one\n * hook owns both directions, why grouping matters, the SSR story); this file\n * is the mechanism, with the framework-specific ref type swapped for\n * `PresenceRefCallback` so the same bytes work in every package.\n */\n\nimport { runTransition, type TransitionRun } from \"./animate\";\nimport type { TransitionFn, TransitionSpec } from \"./transitions\";\n\n/** The three values `Presence` renders into `data-state`, because its source\n *  renders three. An anchored surface renders `surfaceState`'s two instead —\n *  the two vocabularies are not interchangeable (convention C-5). */\nexport type PresenceState = \"opening\" | \"open\" | \"closing\";\n\n/**\n * A framework-free callback ref: called with the live node on attach, `null`\n * on detach, returning nothing. This is the shape every framework's own ref\n * callback type is compatible with — React's `RefCallback<HTMLElement>`\n * resolves to `(instance: HTMLElement | null) => void | (() => void)`, and a\n * function that always returns `void` satisfies that trivially — so each\n * package's wrapper can hand a slot's `ref` straight through with no cast.\n */\nexport type PresenceRefCallback = (node: HTMLElement | null) => void;\n\n/** Internal spelling of `register`'s second/third argument: a params value, or a\n *  factory called with the direction at the instant the leg starts. */\nexport type ParamsOrFactory<P> = P | ((entering: boolean) => P);\n\nexport interface PresenceCoreDeps {\n\tsetMounted: (mounted: boolean) => void;\n\tsetState: (state: PresenceState) => void;\n\tonEnterStart: () => void;\n\tonEnterEnd: () => void;\n\tonExitStart: () => void;\n\tonExitEnd: () => void;\n\t/** Read live at the moment an exit starts, never captured per render. */\n\tinertRef: { readonly current: boolean };\n}\n\n/** The key a single-element `register(transition, params)` call lands on. */\nconst DEFAULT_KEY = \"default\";\n\n/** One animated element's seat on the shared clock. */\ninterface PresenceSlot {\n\t/** The live node, or `null` while detached. */\n\tnode: HTMLElement | null;\n\t/** Rewritten on EVERY render by `register`; called at the instant a leg\n\t *  starts, which is the only moment `params` is ever read. */\n\tbuildSpec: ((node: Element, entering: boolean) => TransitionSpec) | null;\n\t/** The leg attached to this slot, kept as the counterpart a reversing leg\n\t *  reads its start position from. Cleared when an ENTER finishes, kept when\n\t *  an exit does — see `startSlot`. */\n\trun: TransitionRun | undefined;\n\t/** Direction of the leg this slot last started; `null` before its first. */\n\tlegTo: 0 | 1 | null;\n\t/** Whether that leg has settled. */\n\tfinished: boolean;\n\t/**\n\t * A leg this slot was carrying when its node was detached, owed back to it\n\t * the moment a node reattaches. `null` whenever nothing is owed.\n\t *\n\t * React 19's StrictMode detaches and reattaches every ref on a host node's\n\t * mount (`attach` → `detach` → `attach`), and the detach lands AFTER the\n\t * layout effect that started the leg. Without this the aborted leg is lost\n\t * for good: the driver effect is keyed on `[open, mounted]`, neither of\n\t * which moves across a double-invoke, so nothing would ever start it again\n\t * and the entrance would stall at `\"opening\"` forever.\n\t */\n\tpendingLeg: 0 | 1 | null;\n\t/** Cached for the life of the slot, so a caller never detaches and\n\t *  reattaches the node just because its owning component re-rendered. */\n\tref: PresenceRefCallback;\n}\n\n/**\n * The framework-free state machine behind `usePresence`. Allocation-only, so\n * it is safe to build eagerly on first use: it installs no listener and\n * starts no timer until `sync()` is called from a layout effect.\n */\nexport function createPresenceCore(deps: PresenceCoreDeps) {\n\tconst slots = new Map<string, PresenceSlot>();\n\n\t/** Direction of the leg currently in flight across the whole group, or\n\t *  `null` once it has settled. One clock, shared by every slot. */\n\tlet leg: 0 | 1 | null = null;\n\t/** Suppresses `settle()` while `startLegs` is still handing legs out, so a\n\t *  synchronous (duration 0) finish on the first slot cannot settle the group\n\t *  before the rest have started. */\n\tlet starting = false;\n\t/** Whether the closing `inert` is currently applied, so a reversal clears\n\t *  exactly what was set and a caller that opted out is never touched. */\n\tlet inertApplied = false;\n\t/** Reset by `teardown()`, so a remount is a fresh mount again — which is\n\t *  what keeps the `appear` rule honest under StrictMode's double-invoke. */\n\tlet firstRun = true;\n\n\tfunction ensureSlot(key: string): PresenceSlot {\n\t\tconst existing = slots.get(key);\n\t\tif (existing) return existing;\n\n\t\tconst slot: PresenceSlot = {\n\t\t\tnode: null,\n\t\t\tbuildSpec: null,\n\t\t\trun: undefined,\n\t\t\tlegTo: null,\n\t\t\tfinished: false,\n\t\t\tpendingLeg: null,\n\t\t\t// Block body, never a concise arrow: a returned value would be read\n\t\t\t// as a cleanup function by a framework's ref-callback contract\n\t\t\t// (React 19's included — convention C-3), and this ref never means to\n\t\t\t// return one.\n\t\t\tref: (node) => {\n\t\t\t\tif (node) {\n\t\t\t\t\tslot.node = node;\n\t\t\t\t\t// A leg was in flight when the previous node detached, so hand\n\t\t\t\t\t// it back — this is the only thing that restarts it. The common\n\t\t\t\t\t// case is StrictMode's mount-time detach/reattach, where the\n\t\t\t\t\t// detach lands after the layout effect that started the leg;\n\t\t\t\t\t// the resume is a no-op on a first attach, which is what keeps\n\t\t\t\t\t// the ordinary path (legs started by the driver effect) intact.\n\t\t\t\t\tconst pending = slot.pendingLeg;\n\t\t\t\t\tslot.pendingLeg = null;\n\t\t\t\t\tif (pending !== null && leg === pending) startSlot(slot, pending);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// The node is leaving the DOM in this very commit; the animation\n\t\t\t\t// goes with it, so drop it rather than leaving a Chromium effect\n\t\t\t\t// leak behind, and forget the leg so a remount starts clean —\n\t\t\t\t// but remember an UNSETTLED leg, which a reattach owes back.\n\t\t\t\tslot.pendingLeg = leg !== null && !slot.finished ? leg : null;\n\t\t\t\tslot.run?.abort();\n\t\t\t\tslot.run = undefined;\n\t\t\t\tslot.node = null;\n\t\t\t\tslot.legTo = null;\n\t\t\t\tslot.finished = false;\n\t\t\t},\n\t\t};\n\t\tslots.set(key, slot);\n\t\treturn slot;\n\t}\n\n\tfunction register<P>(\n\t\ta: string | TransitionFn<P>,\n\t\tb?: TransitionFn<P> | ParamsOrFactory<P>,\n\t\tc?: ParamsOrFactory<P>\n\t): PresenceRefCallback {\n\t\tconst keyed = typeof a === \"string\";\n\t\tconst slot = ensureSlot(keyed ? a : DEFAULT_KEY);\n\t\tconst transition = (keyed ? b : a) as TransitionFn<P>;\n\t\tconst params = (keyed ? c : b) as ParamsOrFactory<P> | undefined;\n\n\t\t// Stored, not called: `params` is read at leg start, never at render\n\t\t// time. A `params` that IS a function is therefore always the factory\n\t\t// form — no transition in this package takes a callable params value.\n\t\tslot.buildSpec = (node, entering) => {\n\t\t\tconst resolved =\n\t\t\t\ttypeof params === \"function\" ? (params as (entering: boolean) => P)(entering) : params;\n\t\t\t// A real \"in\"/\"out\", never the \"both\" a single bidirectional\n\t\t\t// directive reports, so a preset's own direction-dependent easing\n\t\t\t// default resolves correctly.\n\t\t\treturn transition(node, resolved, { direction: entering ? \"in\" : \"out\" });\n\t\t};\n\n\t\treturn slot.ref;\n\t}\n\n\tfunction startSlot(slot: PresenceSlot, to: 0 | 1): void {\n\t\tconst node = slot.node;\n\t\tconst buildSpec = slot.buildSpec;\n\t\tif (!node || !buildSpec) return;\n\n\t\t// Read BEFORE the bookkeeping below overwrites it: the in-flight leg is\n\t\t// this one's counterpart, and `runTransition` reads its position before\n\t\t// aborting it.\n\t\tconst counterpart = slot.run;\n\t\tslot.legTo = to;\n\t\tslot.finished = false;\n\t\tslot.run = undefined;\n\n\t\tconst handle: { current: TransitionRun | undefined } = { current: undefined };\n\n\t\tconst run = runTransition(node, buildSpec(node, to === 1), to, counterpart, () => {\n\t\t\tslot.finished = true;\n\t\t\tif (to === 1) {\n\t\t\t\t// On ENTER finish, abort: that removes `fill: forwards` so the\n\t\t\t\t// element falls back to its resting style, which is the visible\n\t\t\t\t// end state by construction. On EXIT finish, deliberately do NOT\n\t\t\t\t// — the node stays in the DOM until the owner processes\n\t\t\t\t// `mounted = false` one render later, and dropping fill-forwards\n\t\t\t\t// would flash it back to visible for a frame.\n\t\t\t\thandle.current?.abort();\n\t\t\t\tslot.run = undefined;\n\t\t\t}\n\t\t\tsettle(to);\n\t\t});\n\n\t\thandle.current = run;\n\t\t// A duration-0 leg already finished synchronously inside the call above;\n\t\t// keeping its handle would hand a settled leg to the next one as a\n\t\t// counterpart.\n\t\tif (!slot.finished) slot.run = run;\n\t}\n\n\tfunction startLegs(to: 0 | 1): void {\n\t\tstarting = true;\n\t\ttry {\n\t\t\tfor (const slot of slots.values()) {\n\t\t\t\tif (!slot.node) continue;\n\t\t\t\tif (slot.legTo === to) continue;\n\t\t\t\tstartSlot(slot, to);\n\t\t\t}\n\t\t} finally {\n\t\t\tstarting = false;\n\t\t}\n\t\tsettle(to);\n\t}\n\n\t/**\n\t * The group settles only once EVERY attached node's leg has landed — the\n\t * source's own rule that a block is destroyed when its LAST transition\n\t * finishes, which is what makes a scrim/panel pair leave together instead of\n\t * one straggling.\n\t */\n\tfunction settle(to: 0 | 1): void {\n\t\tif (starting) return;\n\t\t// A leg that has been superseded by a reversal must not settle the group.\n\t\tif (leg !== to) return;\n\n\t\tfor (const slot of slots.values()) {\n\t\t\tif (!slot.node) continue;\n\t\t\tif (slot.legTo !== to || !slot.finished) return;\n\t\t}\n\n\t\tleg = null;\n\n\t\tif (to === 1) {\n\t\t\tdeps.setState(\"open\");\n\t\t\tdeps.onEnterEnd();\n\t\t\treturn;\n\t\t}\n\n\t\tdeps.setMounted(false);\n\t\t// Reset to the same honest default `state` starts at: without this a\n\t\t// completed close would leave `state` sitting at \"closing\", so the next\n\t\t// open would create the node already carrying a stale value for one\n\t\t// render before the entrance corrected it.\n\t\tdeps.setState(\"open\");\n\t\tdeps.onExitEnd();\n\t}\n\n\tfunction beginEnter(): void {\n\t\tleg = 1;\n\t\tif (inertApplied) {\n\t\t\tinertApplied = false;\n\t\t\tfor (const slot of slots.values()) {\n\t\t\t\tslot.node?.toggleAttribute(\"inert\", false);\n\t\t\t}\n\t\t}\n\t\tdeps.setState(\"opening\");\n\t\tdeps.onEnterStart();\n\t}\n\n\tfunction beginExit(): void {\n\t\tleg = 0;\n\t\t// Fires at the dismiss instant, before the exit paints: a keyboard user\n\t\t// does not wait out the fade with focus stranded on `<body>`.\n\t\tdeps.onExitStart();\n\t\tdeps.setState(\"closing\");\n\t\tif (deps.inertRef.current) {\n\t\t\tinertApplied = true;\n\t\t\tfor (const slot of slots.values()) {\n\t\t\t\tslot.node?.toggleAttribute(\"inert\", true);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * The whole state machine, driven from one layout effect keyed on\n\t * `[open, mounted]`. Every registered node has already attached by the time\n\t * this runs: a child host node's ref attaches before an ancestor's layout\n\t * effect runs, children first.\n\t */\n\tfunction sync(open: boolean, mounted: boolean, appear: boolean): void {\n\t\tconst first = firstRun;\n\t\tfirstRun = false;\n\n\t\tif (open) {\n\t\t\tif (!mounted) {\n\t\t\t\t// Fully closed. Announce the entrance and mount; the legs start on\n\t\t\t\t// the next pass, once the subtree has rendered and every\n\t\t\t\t// registered node has attached. Both passes land before paint.\n\t\t\t\tbeginEnter();\n\t\t\t\tdeps.setMounted(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (first && !appear) {\n\t\t\t\t// A local transition never plays on the initial render of the\n\t\t\t\t// block that owns it: an already-open mount paints at rest, with\n\t\t\t\t// `state` left at its \"open\" default and no callback fired.\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (leg !== 1) beginEnter();\n\t\t\tstartLegs(1);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!mounted) return;\n\t\tif (leg !== 0) beginExit();\n\t\tstartLegs(0);\n\t}\n\n\t/** Unmount. Aborts every in-flight leg and rewinds the machine, so a\n\t *  StrictMode remount is indistinguishable from a fresh mount. */\n\tfunction teardown(): void {\n\t\tfor (const slot of slots.values()) {\n\t\t\tslot.run?.abort();\n\t\t\tslot.run = undefined;\n\t\t\tslot.legTo = null;\n\t\t\tslot.finished = false;\n\t\t\tslot.pendingLeg = null;\n\t\t}\n\t\tleg = null;\n\t\tinertApplied = false;\n\t\tfirstRun = true;\n\t}\n\n\treturn { register, sync, teardown };\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/presence-core.ts"},{"path":"react/src/internals/motion/presence.ts","content":"\"use client\";\n\n/**\n * `usePresence` — the mount/unmount clock every transition-driven surface in\n * this package runs on.\n *\n * The framework this library ports from hands a conditional block's exit\n * animation to its own scheduler: the block stays alive until its LAST\n * transition finishes, and only then is it destroyed. React has no such thing\n * — a component that stops rendering a subtree removes it in the same commit —\n * so the clock has to be owned explicitly. This hook is that owner, and owning\n * it in one place rather than per component buys three things:\n *\n * - **Cleanup timing, for free.** Every effect cleanup inside the subtree — a\n *   scroll lock's release, a dismissable layer's splice, a focus trap's destroy\n *   path — lands at the instant the exit settles, exactly where the source's\n *   outro-delayed `destroy()` put it. No other module needs a \"delay my\n *   teardown\" mechanism.\n * - **Reversal.** ONE bidirectional leg per node, never a split in/out pair, so\n *   a reopen mid-close resumes from the position the close actually reached\n *   instead of snapping back to the far end. Reversal smoothing only exists for\n *   a unified leg; having one hook own both directions makes that structural\n *   rather than a convention a port could break. The node is never unmounted\n *   during such a reversal — which is why `onEnterStart` is the hook a focus\n *   trap re-arms from.\n * - **Grouping.** Several nodes can share one clock (a dialog's scrim and its\n *   panel), so they leave together and the unmount is a tie rather than a\n *   straggler.\n *\n * `register()` is called during render and returns a CACHED, identity-stable\n * ref callback, so React never detaches and reattaches an animated node. The\n * transition and its params are rewritten on every render into a per-key slot\n * and read at the instant a leg starts, never at render time. That is what lets\n * `params` take a `(entering: boolean) => P` factory, and the factory form is\n * the documented default: a single bidirectional transition cannot tell\n * entering from leaving on its own (its `direction` reports `\"both\"` on both\n * invocations, computed once for the life of the node), but `open` can — so the\n * factory is called with the answer, and `usePresence` passes a real\n * `\"in\"`/`\"out\"` down so a preset's own direction-dependent easing default\n * resolves correctly.\n *\n * Phase: layout effect throughout (contract §4). The source starts an intro\n * before paint; a passive effect would paint one frame at rest first, and a\n * reduced-motion close would cost an extra frame instead of settling inside the\n * same effect that started it.\n *\n * SSR: `mounted` starts at `open`, no leg ever runs, and nothing here reads a\n * browser global in a render path. A surface that is open on the server paints\n * at rest — which is also the client's behaviour on a first render, because\n * `appear` defaults to false.\n */\n\nimport { useState } from \"react\";\nimport type { RefCallback } from \"react\";\n\nimport { useConstant, useIsomorphicLayoutEffect } from \"../dom/ssr\";\nimport { useEventCallback } from \"../dom/use-event-callback\";\nimport { useLiveRef } from \"../dom/use-live-ref\";\nimport { createPresenceCore, type PresenceState } from \"./presence-core\";\nimport type { SurfaceState } from \"./anchored\";\nimport type { TransitionFn } from \"./transitions\";\n\nexport type { PresenceState };\n\nexport interface UsePresenceOptions {\n\t/**\n\t * Animate an entrance when `open` is ALREADY true on the very first render.\n\t * Default FALSE, reproducing two source rules that happen to agree: a LOCAL\n\t * transition never plays on the initial render of the block that owns it,\n\t * and hydration defaults intros off. A surface mounting for the first time\n\t * already open therefore paints visible with no intro.\n\t */\n\tappear?: boolean;\n\t/**\n\t * Set `inert` on every attached node while closing, clear it on enter.\n\t * Default true — the source sets it itself, synchronously, immediately\n\t * before the exit starts, which is what keeps a closing panel from\n\t * answering a click. `false` is the explicit opt-out, and it means this\n\t * hook never touches `inert` at all.\n\t *\n\t * Written as the ATTRIBUTE, through `toggleAttribute`, never as the\n\t * `inert` IDL property — the one mechanism this package uses everywhere\n\t * (`useInertAttribute` is the same write behind a ref). The attribute is\n\t * what `:not([inert])` selectors and assistive technology key on, a real\n\t * browser reflects it to the IDL property for anything reading that\n\t * instead, and it is the only one of the two that exists under jsdom, so\n\t * the behaviour a test pins is the behaviour that ships.\n\t */\n\tinert?: boolean;\n\tonEnterStart?: () => void;\n\tonEnterEnd?: () => void;\n\t/** Fires at the dismiss instant, before the exit paints. */\n\tonExitStart?: () => void;\n\tonExitEnd?: () => void;\n}\n\nexport interface PresenceHandle {\n\t/** Render the subtree while true. Stays true through the WHOLE exit. */\n\treadonly mounted: boolean;\n\t/** Three values. For anything whose source renders three. */\n\treadonly state: PresenceState;\n\t/** Two values — `state === \"closing\" ? \"closing\" : \"open\"`. THE value every\n\t *  anchored surface renders into `data-state` (convention C-5). */\n\treadonly surfaceState: SurfaceState;\n\t/** `=== open`. Pass to `active:` options and to params factories. */\n\treadonly entering: boolean;\n\n\t/** Attach the single animated element. */\n\tregister<P>(\n\t\ttransition: TransitionFn<P>,\n\t\tparams?: P | ((entering: boolean) => P)\n\t): RefCallback<HTMLElement>;\n\t/** Attach one of several elements sharing this clock (a dialog's scrim +\n\t *  panel). The subtree unmounts only when EVERY keyed exit has finished. */\n\tregister<P>(\n\t\tkey: string,\n\t\ttransition: TransitionFn<P>,\n\t\tparams?: P | ((entering: boolean) => P)\n\t): RefCallback<HTMLElement>;\n}\n\n/**\n * Owns one surface's mount/unmount clock. `mounted` stays true through the\n * whole exit and goes false only once every registered node's leg has settled.\n */\nexport function usePresence(open: boolean, options: UsePresenceOptions = {}): PresenceHandle {\n\tconst { appear = false, inert = true } = options;\n\n\tconst [mounted, setMounted] = useState(open);\n\tconst [state, setState] = useState<PresenceState>(\"open\");\n\n\tconst onEnterStart = useEventCallback(options.onEnterStart);\n\tconst onEnterEnd = useEventCallback(options.onEnterEnd);\n\tconst onExitStart = useEventCallback(options.onExitStart);\n\tconst onExitEnd = useEventCallback(options.onExitEnd);\n\n\tconst inertRef = useLiveRef(inert);\n\tconst appearRef = useLiveRef(appear);\n\n\t// Every dependency below is identity-stable for the life of the component:\n\t// React's own setters, and the permanently-stable wrappers `useEventCallback`\n\t// and `useLiveRef` hand back. The factory allocates and nothing else.\n\tconst core = useConstant(() =>\n\t\tcreatePresenceCore({\n\t\t\tsetMounted,\n\t\t\tsetState,\n\t\t\tonEnterStart,\n\t\t\tonEnterEnd,\n\t\t\tonExitStart,\n\t\t\tonExitEnd,\n\t\t\tinertRef,\n\t\t})\n\t);\n\n\t// `appear` is deliberately not a dependency: it only ever decides whether the\n\t// FIRST run animates, and it is read live so it cannot go stale either.\n\tuseIsomorphicLayoutEffect(() => {\n\t\tcore.sync(open, mounted, appearRef.current);\n\t}, [core, appearRef, open, mounted]);\n\n\t// Separate from the driver above precisely BECAUSE it has a cleanup: giving\n\t// the driver one would abort every leg on each reversal, which is the one\n\t// thing this hook exists to avoid.\n\tuseIsomorphicLayoutEffect(() => core.teardown, [core]);\n\n\treturn {\n\t\tmounted,\n\t\tstate,\n\t\tsurfaceState: state === \"closing\" ? \"closing\" : \"open\",\n\t\tentering: open,\n\t\tregister: core.register,\n\t};\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/presence.ts"},{"path":"react/src/internals/motion/presets.ts","content":"\"use client\";\n\n/**\n * The single source of preset GEOMETRY for the motion family — what moves,\n * and how far, for each named look. Two consumers read this table for two\n * different purposes: Reveal's CSS (hand-written rules keyed on\n * `data-preset`, built to match this table by hand) and `transitions.ts`'s\n * `preset()` (which reads this table directly at runtime to build a\n * `TransitionSpec`). Keeping the numbers in exactly one place is the\n * whole point of this file existing separately from `transitions.ts` — a\n * geometry change here is the one edit that keeps both in sync in theory,\n * even though Reveal's CSS side still has to be updated by hand (this\n * package ships no shared runtime stylesheet for motion; see `tokens.ts`'s\n * header comment for the same caveat applied to durations/easings).\n */\n\nexport type PresetName =\n\t| \"fade\"\n\t| \"fade-up\"\n\t| \"fade-down\"\n\t| \"fade-left\"\n\t| \"fade-right\"\n\t| \"scale\"\n\t| \"blur\"\n\t| \"zoom\";\n\n/** Reveal never offers `blur`/`zoom` — a per-child `filter` is expensive\n * enough (forces paint, not just compositing) that the contract keeps it\n * opt-in to a single root (Presence), not something an arbitrary-length\n * list of children can each carry. */\nexport type RevealPresetName = Exclude<PresetName, \"blur\" | \"zoom\">;\n\nexport const PRESET_NAMES: readonly PresetName[] = [\n\t\"fade\",\n\t\"fade-up\",\n\t\"fade-down\",\n\t\"fade-left\",\n\t\"fade-right\",\n\t\"scale\",\n\t\"blur\",\n\t\"zoom\",\n];\n\n/**\n * `x`/`y`: the SIGN of the travel axis, multiplied by a caller-supplied\n * `distance` (px) at the consuming site — `1` and `-1`, never a raw pixel\n * value, so one `distance` prop scales every directional preset the same\n * way. `scale`/`blur`: the value at the HIDDEN end of the transition (t=0);\n * the visible end (t=1) is always the resting `scale(1)` / `blur(0)`.\n */\nexport interface PresetGeometry {\n\tx?: -1 | 1;\n\ty?: -1 | 1;\n\tscale?: number;\n\tblur?: number;\n}\n\nexport const PRESETS: Record<PresetName, PresetGeometry> = {\n\tfade: {},\n\t\"fade-up\": { y: 1 }, // hidden state sits BELOW rest (+y), travels up into place\n\t\"fade-down\": { y: -1 }, // hidden state sits ABOVE rest (-y), travels down into place\n\t\"fade-left\": { x: 1 },\n\t\"fade-right\": { x: -1 },\n\tscale: { scale: 0.92 }, // floor per the creative direction — never smaller\n\tblur: { blur: 8 }, // px; the one Reveal-excluded preset that is opacity+filter only\n\tzoom: { scale: 0.85, blur: 12 }, // Presence's deepest preset — scale floor + filter combined\n};\n","type":"registry:lib","target":"@ui/fancy/internals/motion/presets.ts"},{"path":"react/src/internals/motion/raf.ts","content":"\"use client\";\n\n/**\n * Trailing-edge rAF throttle, generalized from the hand-rolled\n * `if (id) cancelAnimationFrame(id); id = requestAnimationFrame(...)` pattern\n * already duplicated in this repo (GlowingEffect's pointer sampling). Magnetic\n * (pointermove) and ScrollProgress (scroll/resize) both need \"run at most\n * once per frame, with the freshest data\" — this is that primitive, pulled\n * out once instead of re-hand-rolled a third and fourth time.\n */\n\nexport interface RafThrottled<A extends unknown[]> {\n\t(...args: A): void;\n\t/** Cancel a pending (not-yet-run) frame. A safe no-op if none is pending. */\n\tcancel(): void;\n}\n\n/**\n * Wraps `fn` so a burst of synchronous calls collapses into ONE\n * `requestAnimationFrame` callback, called with the LATEST call's\n * arguments — never the first.\n *\n * Deliberately does NOT cancel-and-reschedule on every call (the\n * `cancelAnimationFrame(frame); frame = requestAnimationFrame(...)` shape\n * some throttles use). That pattern resets the wait on every single call,\n * so a continuous stream faster than one call per frame — the ENTIRE\n * reason to throttle in the first place — would push the scheduled frame\n * back forever and `fn` would never run until the stream paused. Storing\n * the latest args and letting the frame ALREADY in flight pick them up\n * guarantees `fn` runs at most once per frame no matter the input rate,\n * and the one frame that does run always reflects where things ended up.\n *\n * Falls back to a synchronous call when `requestAnimationFrame` itself is\n * unavailable (checked per call, not once at creation, since every real\n * call site is browser-only by construction and this only matters for a\n * pathological/non-browser caller) — failing to run at all would be worse\n * than running un-throttled.\n */\nexport function rafThrottle<A extends unknown[]>(fn: (...args: A) => void): RafThrottled<A> {\n\tlet frame: number | undefined;\n\tlet lastArgs: A;\n\n\tfunction throttled(...args: A): void {\n\t\tlastArgs = args;\n\n\t\tif (typeof requestAnimationFrame !== \"function\") {\n\t\t\tfn(...args);\n\t\t\treturn;\n\t\t}\n\n\t\tif (frame !== undefined) return;\n\t\tframe = requestAnimationFrame(() => {\n\t\t\tframe = undefined;\n\t\t\tfn(...lastArgs);\n\t\t});\n\t}\n\n\tthrottled.cancel = () => {\n\t\tif (frame !== undefined) {\n\t\t\tif (typeof cancelAnimationFrame === \"function\") cancelAnimationFrame(frame);\n\t\t\tframe = undefined;\n\t\t}\n\t};\n\n\treturn throttled as RafThrottled<A>;\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/raf.ts"},{"path":"react/src/internals/motion/stagger.ts","content":"\"use client\";\n\n/**\n * Per-item delay arithmetic for a staggered entrance — the one formula\n * Reveal's per-child stagger and TextRoll's per-grapheme roll both build on,\n * so \"stagger from the center\" or \"stagger from the last item\" means the\n * same thing everywhere in this family.\n *\n * Deliberately pure: no DOM, no timers, no side effects. A caller is expected\n * to call this once per item and write the result into a CSS custom property\n * or a transition `delay` param — this file never schedules anything itself.\n */\n\nexport type StaggerFrom = \"first\" | \"last\" | \"center\" | number;\n\n/** How far item `index` sits from the stagger's origin, in item-steps\n * (not ms yet — `staggerDelay` multiplies this by the step). A numeric\n * `from` is honoured exactly as given, even outside `[0, count - 1]`:\n * clamping it would silently change what a caller asked for, and an\n * origin outside the list is a legitimate way to make one edge stagger in\n * later than the other. */\nfunction distanceFromOrigin(index: number, count: number, from: StaggerFrom): number {\n\tif (from === \"first\") return index;\n\tif (from === \"last\") return count - 1 - index;\n\tif (from === \"center\") return Math.abs(index - (count - 1) / 2);\n\treturn Math.abs(index - from);\n}\n\n/**\n * The delay (ms) item `index` of `count` should wait before starting its\n * own entrance, staggered by `step` ms per item away from `from`.\n *\n * `cap`, when given, is a TOTAL-delay ceiling reached by COMPRESSING the\n * step, never by clipping individual delays: `effStep = min(step, cap /\n * max(1, count - 1))`. A 40-item list at the usual 50ms/item step would\n * otherwise finish its LAST item's entrance 1950ms after the first one —\n * long enough to read as \"the list is still loading\", not \"staggered\".\n * Compressing the step instead keeps every item's delay proportional to\n * its distance from the origin (the ordering and relative spacing survive\n * intact), just packed into a shorter total window. The cap is computed\n * against `count - 1` regardless of `from` — exact for `\"first\"`/`\"last\"`\n * (whose maximum distance IS `count - 1`), and a safe (non-tight) bound\n * for `\"center\"` or a numeric origin inside the list, whose maximum\n * distance is smaller. An origin far outside the list can still exceed\n * the cap in absolute ms — a deliberate consequence of not clamping\n * `from` above, not a bug in the cap.\n */\nexport function staggerDelay(\n\tindex: number,\n\tcount: number,\n\tstep: number,\n\tfrom: StaggerFrom = \"first\",\n\tcap?: number\n): number {\n\tif (count <= 1) return 0;\n\tconst effStep = cap === undefined ? step : Math.min(step, cap / Math.max(1, count - 1));\n\treturn distanceFromOrigin(index, count, from) * effStep;\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/stagger.ts"},{"path":"react/src/internals/motion/tokens.ts","content":"\"use client\";\n\n/**\n * The shared motion vocabulary for the micro-interaction family: durations,\n * curves, and stagger steps every one of the ten components draws from\n * instead of re-typing its own numbers.\n *\n * This file has zero side effects and touches nothing browser-specific — it\n * is pure data, safe to import at module scope from anywhere, including\n * during SSR. It is also the ONLY place these numbers live as JS values:\n * every component that wants `--ft-ease-out` (or any other `--ft-*` var)\n * still has to type the identical literal into its own colocated `.css` as\n * that CSS custom property's fallback (this package ships no shared runtime\n * stylesheet for motion — see `tailwind.css`, which carries only the semantic\n * colour vocabulary). Nothing enforces that a component's CSS fallback\n * matches the value here; this file is the source of truth a component author\n * copies FROM, not a value CSS reads at runtime. Keep the two in sync by hand.\n *\n * The four curves intentionally read as this collection's own voice, not as\n * \"the house curve\" borrowed from anywhere else — `out`/`in` are chosen to\n * have exact JS equivalents in `./easing.js` (`expoOut`/`expoIn`, see\n * `JS_EASINGS` below) so a component that needs the SAME curve as both a CSS\n * string (`transition-timing-function`) and a JS easing function (a\n * transition spec's `easing` field) never has two numbers to keep in sync.\n *\n * One rule about WHERE a consumer may spend these numbers, written down here\n * rather than left as tribal knowledge held by whichever component happened\n * to get it right first: colour-only transitions (`color`,\n * `background-color`, `border-color`) are exempt from\n * `prefers-reduced-motion` gating — a colour change is not motion, and gating\n * it only makes a theme flip look broken for the users who asked for less\n * movement. `transform` / `opacity` / `filter` are NOT exempt and are always\n * declared inside `@media (prefers-reduced-motion: no-preference)`, with the\n * resting state as the ungated fallback.\n */\n\nimport { expoIn, expoOut } from \"./easing\";\n\n/** Milliseconds. `micro` = a glyph-scale beat that only reads as a beat (a\n * stroke draw's lead-in, an icon cross-fade). `fast` = a reversible state\n * flip (press, dim). `base` = the default entrance. `exit` = the default\n * exit (shorter than an entrance — leaving reads faster than arriving).\n * `entrance` = a slower, deliberate reveal (Reveal's default).\n *\n * `micro` is deliberately the one rung with no JS consumer: its consumer is\n * the CSS mirror `--ft-duration-micro: 80ms`, spent by StatusMorph's check\n * and cross-stroke delays. It is listed here anyway because this file is the\n * source of truth a component author copies FROM — a rung that exists only\n * in CSS fallbacks would be exactly the drift this table is meant to stop.\n * There is one rung at this scale, not two: StatusMorph's paired `160ms` is\n * written as a literal on purpose rather than tokenised as a second rung. */\nexport const DURATIONS = {\n\tmicro: 80,\n\tfast: 150,\n\tbase: 300,\n\texit: 200,\n\tentrance: 600,\n} as const;\nexport type DurationToken = keyof typeof DURATIONS;\n\n/** CSS `cubic-bezier()` strings — for a component's own `.css` file,\n * never injected by this file. */\nexport const EASINGS = {\n\tout: \"cubic-bezier(0.16, 1, 0.3, 1)\", // arrivals   ↔ expoOut\n\tin: \"cubic-bezier(0.7, 0, 0.84, 0)\", // departures ↔ expoIn\n\tinout: \"cubic-bezier(0.4, 0, 0.2, 1)\", // reversible state ↔ cubicInOut (approx)\n\tovershoot: \"cubic-bezier(0.34, 1.56, 0.64, 1)\", // StatusMorph check only ↔ backOut\n} as const;\nexport type EasingToken = keyof typeof EASINGS;\n\n/** The same two arrival/departure curves as real JS functions, for\n * `transitions.ts`'s `preset()` — a `TransitionSpec.easing` field wants a\n * `(t: number) => number`, not a CSS string. Only `out`/`in` exist here:\n * `inout` never drives a JS-timed transition (it is CSS-only, for\n * hover/press states that have no discrete start/end), and `overshoot` has\n * exactly one consumer (StatusMorph's success check, which computes its own\n * `scale` pop rather than going through `preset()`). */\nexport const JS_EASINGS = { out: expoOut, in: expoIn } as const;\n\n/** Milliseconds per stagger step, before any compression cap is applied.\n * `char`/`charBlur` are TextRoll's per-grapheme step (plain vs. its\n * heavier blur variant); `word`/`line` are illustrative rungs for\n * pretext-driven text layout elsewhere in the library, kept here so a\n * future consumer doesn't invent a fifth number; `item` is Reveal's\n * per-child step. */\nexport const STAGGERS = { char: 15, charBlur: 20, word: 40, line: 150, item: 50 } as const;\n\n/** Total-delay compression caps (ms) — see `stagger.ts`'s `staggerDelay`.\n * `text` bounds TextRoll's per-grapheme roll; `item` bounds Reveal's\n * per-child stagger. A list long enough to blow past its step × count\n * without one of these would stop reading as \"staggered\" and start reading\n * as \"the last item is stuck\". */\nexport const STAGGER_CAPS = { text: 200, item: 600 } as const;\n","type":"registry:lib","target":"@ui/fancy/internals/motion/tokens.ts"},{"path":"react/src/internals/motion/transitions.ts","content":"\"use client\";\n\n/**\n * CSS-only transition factories built from `presets.ts`'s geometry table.\n * Presence's whole \"one transition handles both open and close\" trick, and\n * StickyScroll's panel crossfade, are both built on this file.\n *\n * How the `easing` param actually reaches the screen is worth spelling out,\n * because it is easy to assume `css(t, u)` needs to apply the curve itself:\n * it does not. Whichever runtime plays the transition samples every keyframe\n * as `t = t1 + delta * easing(i / n)` BEFORE `css(t, 1 - t)` is ever called —\n * so the `t`/`u` a `css()` function receives are ALREADY eased, and the curve\n * lives entirely in the sample POSITIONS rather than in a WAAPI `easing`\n * option. `preset()` below does the same as the framework built-ins it\n * mirrors: `easing` is resolved once and returned as-is on the spec; `cssFor`\n * never touches it.\n */\n\nimport { DURATIONS, JS_EASINGS } from \"./tokens\";\nimport { PRESETS, type PresetName } from \"./presets\";\n\n/**\n * One transition's timing plus its per-frame CSS. Structurally identical to\n * the `TransitionConfig` a framework transition runtime consumes, declared\n * locally so this shared core takes no dependency on any one framework's\n * types. The `tick` field is omitted: nothing in this library uses it.\n */\nexport interface TransitionSpec {\n\tdelay: number;\n\tduration: number;\n\teasing: (t: number) => number;\n\t/** `t` runs 0 (hidden) → 1 (visible); `u = 1 - t`. Receives ALREADY-EASED\n\t *  `t` — the sampler applies `easing` before calling this, which is why\n\t *  `cssFor` interpolates linearly in `t`. See the file header. */\n\tcss: (t: number, u: number) => string;\n}\n\nexport type TransitionDirection = \"in\" | \"out\" | \"both\";\n\n/** A transition function's shape, preserved exactly — including the unused\n *  first parameter, so the colocated test files transpose 1:1. */\nexport type TransitionFn<P = unknown> = (\n\tnode: Element,\n\tparams?: P,\n\toptions?: { direction: TransitionDirection }\n) => TransitionSpec;\n\nexport interface PresetParams {\n\tduration?: number;\n\tdelay?: number;\n\tdistance?: number;\n\t/** A JS easing function — `(t: number) => number` — not a CSS string.\n\t * Handed straight to `TransitionSpec.easing`; see the file header for\n\t * why `cssFor` never has to apply it itself. */\n\teasing?: (t: number) => number;\n}\n\nconst DEFAULT_DISTANCE = 16;\n\n/**\n * Builds the `opacity`/`transform`(/`filter`) CSS string for one sampled\n * frame of `name`'s transition. `t` runs 0 (hidden) → 1 (visible) on intro\n * and the reverse on outro; `u = 1 - t` is \"how much of the hidden offset\n * is left\". Every preset animates opacity linearly in `t` (visible state IS\n * opacity 1, by construction, so there is nothing preset-specific to\n * compute there). `transform` is only emitted when this preset's geometry\n * actually has a translate or scale term — \"fade\" has neither, so its CSS\n * string is opacity-only at every frame (never a no-op `transform: none`)\n * — and `filter` is only ever emitted for `\"blur\"`/`\"zoom\"`, the two\n * presets whose `PresetGeometry.blur` is set, matching the frozen rule that\n * `filter` is opt-in, never a default.\n */\nfunction cssFor(name: PresetName, t: number, u: number, distance: number): string {\n\tconst geometry = PRESETS[name];\n\tconst declarations = [`opacity: ${t}`];\n\n\tconst transformTerms: string[] = [];\n\tif (geometry.x !== undefined || geometry.y !== undefined) {\n\t\tconst dx = (geometry.x ?? 0) * distance * u;\n\t\tconst dy = (geometry.y ?? 0) * distance * u;\n\t\ttransformTerms.push(`translate(${dx}px, ${dy}px)`);\n\t}\n\tif (geometry.scale !== undefined) {\n\t\t// Interpolates from the hidden-state scale (< 1) up to the resting\n\t\t// scale(1) as t goes 0 → 1 — never the other way, so a \"scale\" preset\n\t\t// always grows into place regardless of intro/outro direction.\n\t\ttransformTerms.push(`scale(${geometry.scale + (1 - geometry.scale) * t})`);\n\t}\n\tif (transformTerms.length > 0) {\n\t\tdeclarations.push(`transform: ${transformTerms.join(\" \")}`);\n\t}\n\n\tif (geometry.blur !== undefined) {\n\t\tdeclarations.push(`filter: blur(${geometry.blur * u}px)`);\n\t}\n\n\treturn declarations.join(\"; \");\n}\n\n/**\n * Returns a transition function for the named preset, ready to hand to\n * whichever presence/transition mechanism the host framework provides.\n * Resolution order for every param is caller-supplied `params` → this\n * function's own defaults; `duration` defaults to `DURATIONS.base` and\n * `distance` to 16px regardless of direction (a component that wants a\n * shorter/half-distance EXIT, like Presence, passes those explicitly in its\n * own `out`-side params — this factory does not guess at that asymmetry).\n *\n * `easing` is the one default that DOES read `options.direction`: with no\n * explicit `params.easing`, an `\"out\"`-direction instance (an exit-only\n * instance used on its own — StickyScroll's panel crossfade builds one\n * `preset(\"fade\")` and uses it as both the enter and the exit spec, two\n * separate instances) defaults to `JS_EASINGS.in` (a departure curve);\n * anything else (`\"in\"`, or the unified `\"both\"` a single two-way\n * transition reports — Presence's case) defaults to `JS_EASINGS.out` (an\n * arrival curve, the more common perceived motion of the two when only one\n * curve is in play).\n */\nexport function preset(name: PresetName): TransitionFn<PresetParams> {\n\treturn (_node, params = {}, options) => {\n\t\tconst duration = params.duration ?? DURATIONS.base;\n\t\tconst delay = params.delay ?? 0;\n\t\tconst distance = params.distance ?? DEFAULT_DISTANCE;\n\t\tconst easing = params.easing ?? (options?.direction === \"out\" ? JS_EASINGS.in : JS_EASINGS.out);\n\n\t\treturn {\n\t\t\tdelay,\n\t\t\tduration,\n\t\t\teasing,\n\t\t\tcss: (t, u) => cssFor(name, t, u, distance),\n\t\t};\n\t};\n}\n","type":"registry:lib","target":"@ui/fancy/internals/motion/transitions.ts"},{"path":"react/src/internals/motion/types.ts","content":"\"use client\";\n\n/**\n * Type-only aggregate of the shared unions component prop interfaces need to\n * reference. Re-exported from the package barrel with a wholesale\n * `export type *` (the barrel's edit, not this file's) so a component's own\n * `<ComponentName>Props` can `import type` these from one place instead of\n * reaching into individual foundation files — and, per the common contract,\n * so a component's `index.ts` barrel never has to re-export a type itself\n * (which would risk a name collision across the ten components' barrels).\n *\n * `verbatimModuleSyntax` (this package's tsconfig) means every line here is\n * `export type` and compiles to nothing at runtime — this module has zero\n * JS output, by design.\n */\n\nexport type { PresetName, RevealPresetName } from \"./presets\";\nexport type { StaggerFrom } from \"./stagger\";\nexport type { HapticPattern } from \"./haptics\";\n","type":"registry:lib","target":"@ui/fancy/internals/motion/types.ts"}]}