{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"scroll-hint","type":"registry:component","title":"Scroll Hint","description":"A hero-section scroll-down indicator in two shapes: a mouse outline with a dropping dot, or a pair of pulsing chevrons. currentColor sizing, optional label, reduced-motion shows the static glyph.","author":"UI Lab","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/scroll-hint.tsx","type":"registry:component","target":"@components/motion/scroll-hint.tsx","content":"// ui-lab-ten.vercel.app/components/motion/scroll-hint\nimport { EASE_OUT_CSS } from \"@/lib/ease\";\nimport { cn } from \"@/lib/utils\";\n\nexport type ScrollHintVariant = \"mouse\" | \"chevron\";\n\nexport interface ScrollHintProps {\n  /** Which glyph to render. */\n  variant?: ScrollHintVariant;\n  className?: string;\n  /** Optional caption under the glyph, e.g. \"Scroll\". */\n  label?: string;\n}\n\n/**\n * A quiet \"there's more below\" cue for the foot of a hero section. Two\n * glyphs, both pure CSS loops: a mouse shell with a dot that falls and fades,\n * or a pair of chevrons pulsing downward out of step with each other.\n * `motion-reduce:` freezes the loop and leaves the glyph sitting still.\n */\nexport function ScrollHint({\n  variant = \"mouse\",\n  className,\n  label,\n}: ScrollHintProps) {\n  return (\n    <>\n      <style>{KEYFRAMES}</style>\n      <div\n        className={cn(\n          \"inline-flex flex-col items-center gap-2 text-foreground\",\n          className,\n        )}\n      >\n        {variant === \"mouse\" && <MouseGlyph />}\n        {variant === \"chevron\" && <ChevronGlyph />}\n        {label && (\n          <span className=\"text-xs text-muted-foreground\">{label}</span>\n        )}\n      </div>\n    </>\n  );\n}\n\nconst KEYFRAMES = `\n@keyframes uilab-scroll-hint-dot {\n  0% { transform: translateY(0); opacity: 0; }\n  20% { opacity: 1; }\n  60% { opacity: 1; }\n  100% { transform: translateY(10px); opacity: 0; }\n}\n@keyframes uilab-scroll-hint-chevron {\n  0%, 100% { opacity: 0.3; transform: translateY(-2px); }\n  50% { opacity: 1; transform: translateY(2px); }\n}\n`;\n\nfunction MouseGlyph() {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      width=\"20\"\n      height=\"30\"\n      viewBox=\"0 0 20 30\"\n      fill=\"none\"\n    >\n      <rect\n        x=\"1.25\"\n        y=\"1.25\"\n        width=\"17.5\"\n        height=\"27.5\"\n        rx=\"8.75\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n      />\n      <circle\n        cx=\"10\"\n        cy=\"8.5\"\n        r=\"2\"\n        fill=\"currentColor\"\n        className=\"animate-[uilab-scroll-hint-dot_1.8s_infinite] motion-reduce:animate-none\"\n        style={{ animationTimingFunction: EASE_OUT_CSS }}\n      />\n    </svg>\n  );\n}\n\nfunction ChevronGlyph() {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      width=\"24\"\n      height=\"28\"\n      viewBox=\"0 0 24 28\"\n      fill=\"none\"\n    >\n      <path\n        d=\"M6 6L12 12L18 6\"\n        stroke=\"currentColor\"\n        strokeWidth=\"2\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        className=\"animate-[uilab-scroll-hint-chevron_1.6s_infinite] motion-reduce:animate-none\"\n        style={{ animationTimingFunction: EASE_OUT_CSS }}\n      />\n      <path\n        d=\"M6 15L12 21L18 15\"\n        stroke=\"currentColor\"\n        strokeWidth=\"2\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        className=\"animate-[uilab-scroll-hint-chevron_1.6s_infinite] motion-reduce:animate-none\"\n        style={{ animationTimingFunction: EASE_OUT_CSS, animationDelay: \"0.35s\" }}\n      />\n    </svg>\n  );\n}\n"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts","content":"// Shared motion tokens. Easing curves mirror the CSS custom properties in\n// globals.css; springs are the canonical physics used across components.\n// Strong custom variants — defaults like `ease-in`/`ease-out` feel weak.\n\nexport const EASE_OUT = [0.16, 1, 0.3, 1] as const;\nexport const EASE_IN_OUT = [0.77, 0, 0.175, 1] as const;\nexport const EASE_DRAWER = [0.32, 0.72, 0, 1] as const;\n\n/** CSS string form of EASE_OUT for inline style transitions. */\nexport const EASE_OUT_CSS = \"cubic-bezier(0.16, 1, 0.3, 1)\";\n\n/** Press feedback on buttons and other tappable surfaces. */\nexport const SPRING_PRESS = {\n  type: \"spring\",\n  stiffness: 500,\n  damping: 30,\n  mass: 0.6,\n} as const;\n\n/** Content swaps — label/icon slots trading places inside a control. */\nexport const SPRING_SWAP = {\n  type: \"spring\",\n  stiffness: 460,\n  damping: 30,\n  mass: 0.55,\n} as const;\n\n/** Overlay panel entrances — modals and sheets summoned by pointer. */\nexport const SPRING_PANEL = {\n  type: \"spring\",\n  stiffness: 420,\n  damping: 40,\n  mass: 0.5,\n} as const;\n\n/** Shared-layout glides — pills, indicators and panels morphing between positions. */\nexport const SPRING_LAYOUT = {\n  type: \"spring\",\n  stiffness: 360,\n  damping: 32,\n  mass: 0.6,\n} as const;\n\n/** Cursor-follow physics for decorative mouse tracking (magnetic, tilt, dock). */\nexport const SPRING_MOUSE = {\n  stiffness: 200,\n  damping: 15,\n  mass: 0.3,\n} as const;\n"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts","content":"import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"}]}