{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tooltip",
  "title": "Tooltip",
  "author": "AlgorizeTech",
  "description": "A portal-rendered tooltip with fade and zoom entrance, positioned on hover or focus. Use it for short labels that clarify an unlabeled control.",
  "dependencies": [],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/custom/blocks/TooltipBlock.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport React, {\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\n/**\n * Positioning has to happen before the browser paints, or the tooltip shows up\n * at its placeholder coordinates for one frame and then jumps into place.\n * useLayoutEffect warns when it runs on the server, and TooltipContent renders\n * nothing until the trigger has committed, so falling back to useEffect there\n * costs nothing.\n */\nconst useIsomorphicLayoutEffect =\n  typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n// ==========================================\n// 1. CONTEXT\n// ==========================================\ninterface TooltipContextType {\n  open: boolean;\n  setOpen: React.Dispatch<React.SetStateAction<boolean>>;\n  delayDuration: number;\n  /**\n   * The trigger node itself, held in state rather than a ref. A ref shared\n   * through context makes every read of this context a ref read during render;\n   * a plain node lets the content position and portal itself without one.\n   */\n  trigger: HTMLDivElement | null;\n  setTrigger: React.Dispatch<React.SetStateAction<HTMLDivElement | null>>;\n}\nconst TooltipContext = createContext<TooltipContextType | undefined>(undefined);\n\n// ==========================================\n// 2. PROVIDER (Optional: For wrapping the app)\n// ==========================================\nexport function TooltipProvider({ children }: { children: React.ReactNode }) {\n  return <>{children}</>;\n}\n\n// ==========================================\n// 3. TOOLTIP ROOT (State Manager)\n// ==========================================\nexport interface TooltipProps {\n  children: React.ReactNode;\n  delayDuration?: number;\n}\n\nexport function Tooltip({ children, delayDuration = 200 }: TooltipProps) {\n  const [open, setOpen] = useState(false);\n  const [trigger, setTrigger] = useState<HTMLDivElement | null>(null);\n\n  // The open/close delay is owned by the trigger, not shared through context:\n  // a timeout ref captured by a context handler would make every read of this\n  // context look like a ref read during render.\n  const value = useMemo(\n    () => ({ open, setOpen, delayDuration, trigger, setTrigger }),\n    [open, delayDuration, trigger],\n  );\n\n  return (\n    <TooltipContext.Provider value={value}>\n      {children}\n    </TooltipContext.Provider>\n  );\n}\n\n// ==========================================\n// 4. TRIGGER\n// ==========================================\nexport interface TooltipTriggerProps\n  extends React.HTMLAttributes<HTMLDivElement> {\n  asChild?: boolean;\n}\n\nexport function TooltipTrigger({\n  children,\n  className,\n  ...props\n}: TooltipTriggerProps) {\n  const context = useContext(TooltipContext);\n  if (!context) throw new Error(\"TooltipTrigger must be used within a Tooltip\");\n\n  const { open, setOpen, delayDuration, setTrigger } = context;\n  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  const handleOpen = useCallback(() => {\n    if (timeoutRef.current) clearTimeout(timeoutRef.current);\n    timeoutRef.current = setTimeout(() => setOpen(true), delayDuration);\n  }, [setOpen, delayDuration]);\n\n  const handleClose = useCallback(() => {\n    if (timeoutRef.current) clearTimeout(timeoutRef.current);\n    setOpen(false);\n  }, [setOpen]);\n\n  useEffect(\n    () => () => {\n      if (timeoutRef.current) clearTimeout(timeoutRef.current);\n    },\n    [],\n  );\n\n  return (\n    <div\n      ref={setTrigger}\n      className={cn(\"inline-block\", className)}\n      tabIndex={0}\n      aria-haspopup=\"dialog\"\n      aria-expanded={open}\n      {...props}\n      onMouseEnter={(e) => {\n        handleOpen();\n        props.onMouseEnter?.(e);\n      }}\n      onMouseLeave={(e) => {\n        handleClose();\n        props.onMouseLeave?.(e);\n      }}\n      onFocus={(e) => {\n        handleOpen();\n        props.onFocus?.(e);\n      }}\n      onBlur={(e) => {\n        handleClose();\n        props.onBlur?.(e);\n      }}\n    >\n      {children}\n    </div>\n  );\n}\n\n// ==========================================\n// 5. CONTENT (Portal Renderer)\n// ==========================================\nexport interface TooltipContentProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n  side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n  align?: \"start\" | \"center\" | \"end\";\n  sideOffset?: number;\n  /** Whether to render a pointer arrow anchoring to the trigger. Defaults to false. */\n  showArrow?: boolean;\n  /** Custom classes for the pointer arrow element. */\n  arrowClassName?: string;\n  /** Whether to enable a smooth pull-out animation sliding gently out from the trigger. Can also be set to \"staggered\" for sequential icon, title, and description entrance, \"expand\" for dynamic height/width expansion, or \"pop\" for a snappy spring popup emergence. */\n  animated?: boolean | \"pull-out\" | \"staggered\" | \"expand\" | \"pop\";\n  /** Whether to animate child elements (icon -> title -> description) in a staggered cascade. Defaults to false. */\n  staggered?: boolean;\n  /** Whether to start with only title space and smoothly expand height (for top/bottom) or width (for left/right) to unveil the description. Defaults to false. */\n  expand?: boolean;\n  /** Optional title header displayed in bold. */\n  title?: React.ReactNode;\n  /** Optional description text. */\n  description?: React.ReactNode;\n  /** Shorthand alias for description. */\n  des?: React.ReactNode;\n  /** Optional leading icon displayed next to the content. */\n  icon?: React.ReactNode;\n  /** Optional custom classes for the icon background container. */\n  iconClassName?: string;\n}\n\ninterface TooltipContentContextType {\n  side: NonNullable<TooltipContentProps[\"side\"]>;\n  align: NonNullable<TooltipContentProps[\"align\"]>;\n}\n\nconst TooltipContentContext = createContext<TooltipContentContextType | null>(null);\n\nconst ARROW_POSITIONS: Record<\n  NonNullable<TooltipContentProps[\"side\"]>,\n  Record<NonNullable<TooltipContentProps[\"align\"]>, string>\n> = {\n  top: {\n    center: \"-bottom-1 left-1/2 -translate-x-1/2\",\n    start: \"-bottom-1 left-3\",\n    end: \"-bottom-1 right-3\",\n  },\n  bottom: {\n    center: \"-top-1 left-1/2 -translate-x-1/2\",\n    start: \"-top-1 left-3\",\n    end: \"-top-1 right-3\",\n  },\n  left: {\n    center: \"-right-1 top-1/2 -translate-y-1/2\",\n    start: \"-right-1 top-3\",\n    end: \"-right-1 bottom-3\",\n  },\n  right: {\n    center: \"-left-1 top-1/2 -translate-y-1/2\",\n    start: \"-left-1 top-3\",\n    end: \"-left-1 bottom-3\",\n  },\n};\n\nconst ARROW_BORDERS: Record<NonNullable<TooltipContentProps[\"side\"]>, string> = {\n  top: \"border-b border-r\",\n  bottom: \"border-t border-l\",\n  left: \"border-t border-r\",\n  right: \"border-b border-l\",\n};\n\nexport interface TooltipArrowProps\n  extends React.HTMLAttributes<HTMLSpanElement> {\n  className?: string;\n}\n\nexport function TooltipArrow({ className, ...props }: TooltipArrowProps) {\n  const context = useContext(TooltipContentContext);\n  const side = context?.side ?? \"top\";\n  const align = context?.align ?? \"center\";\n\n  return (\n    <span\n      aria-hidden=\"true\"\n      className={cn(\n        \"absolute w-2 h-2 rotate-45 bg-inherit border-inherit pointer-events-none\",\n        ARROW_POSITIONS[side][align],\n        ARROW_BORDERS[side],\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n// Precise transform origin mapped to the pointer arrow anchor position.\nconst ARROW_ORIGINS: Record<\n  NonNullable<TooltipContentProps[\"side\"]>,\n  Record<NonNullable<TooltipContentProps[\"align\"]>, string>\n> = {\n  top: {\n    center: \"origin-bottom\",\n    start: \"origin-bottom-left\",\n    end: \"origin-bottom-right\",\n  },\n  bottom: {\n    center: \"origin-top\",\n    start: \"origin-top-left\",\n    end: \"origin-top-right\",\n  },\n  left: {\n    center: \"origin-right\",\n    start: \"origin-top-right\",\n    end: \"origin-bottom-right\",\n  },\n  right: {\n    center: \"origin-left\",\n    start: \"origin-top-left\",\n    end: \"origin-bottom-left\",\n  },\n};\n\n// The entrance grows out of the trigger's edge: a tooltip above the trigger\n// scales from its own bottom edge and slides up a touch, and so on.\nconst ENTER_FROM_SIDE: Record<NonNullable<TooltipContentProps[\"side\"]>, string> = {\n  top: \"origin-bottom slide-in-from-bottom-1\",\n  bottom: \"origin-top slide-in-from-top-1\",\n  left: \"origin-right slide-in-from-right-1\",\n  right: \"origin-left slide-in-from-left-1\",\n};\n\n// Slower, smoother emergence for the staggered cascade variant.\nconst ENTER_STAGGERED: Record<NonNullable<TooltipContentProps[\"side\"]>, string> = {\n  top: \"origin-bottom slide-in-from-bottom-3 zoom-in-90 duration-500 ease-[cubic-bezier(0.16,1,0.3,1)]\",\n  bottom: \"origin-top slide-in-from-top-3 zoom-in-90 duration-500 ease-[cubic-bezier(0.16,1,0.3,1)]\",\n  left: \"origin-right slide-in-from-right-3 zoom-in-90 duration-500 ease-[cubic-bezier(0.16,1,0.3,1)]\",\n  right: \"origin-left slide-in-from-left-3 zoom-in-90 duration-500 ease-[cubic-bezier(0.16,1,0.3,1)]\",\n};\n\nexport function TooltipContent({\n  side = \"top\",\n  align = \"center\",\n  sideOffset = 6,\n  showArrow = false,\n  arrowClassName,\n  animated = false,\n  staggered = false,\n  expand = false,\n  title,\n  description,\n  des,\n  icon,\n  iconClassName,\n  className,\n  children,\n  ...props\n}: TooltipContentProps) {\n  const context = useContext(TooltipContext);\n  if (!context) throw new Error(\"TooltipContent must be used within a Tooltip\");\n\n  const effectiveOffset = sideOffset === 6 && showArrow ? 8 : sideOffset;\n  const { open, trigger: triggerEl } = context;\n  const [coords, setCoords] = useState({ top: -9999, left: -9999 });\n  // Stale coordinates from the previous open would otherwise paint first.\n  const [positioned, setPositioned] = useState(false);\n  const [expanded, setExpanded] = useState(false);\n  const contentRef = useRef<HTMLDivElement>(null);\n\n  const resolvedDes = description ?? des;\n  const isExpand = Boolean(expand || animated === \"expand\");\n  const isStaggered = Boolean(!isExpand && (staggered || animated === \"staggered\"));\n  const isPop = Boolean(!isExpand && !isStaggered && (animated === \"pop\" || (animated === true && showArrow)));\n  const isArrowEmerge = Boolean(!isExpand && !isStaggered && !isPop && (animated === true || animated === \"pull-out\"));\n\n  // Forget the measurement on close, so the next open cannot flash at last\n  // time’s coordinates before the layout effect re-measures.\n  const [wasOpen, setWasOpen] = useState(open);\n  if (wasOpen !== open) {\n    setWasOpen(open);\n    if (!open) {\n      setPositioned(false);\n      setExpanded(false);\n    }\n  }\n\n  // Expand variant: first emerges out of the arrow (animated=true style), then smoothly expands width & height\n  useEffect(() => {\n    if (!open || !isExpand) {\n      setExpanded(false);\n      return;\n    }\n    const timer = setTimeout(() => {\n      setExpanded(true);\n    }, 350);\n    return () => clearTimeout(timer);\n  }, [open, isExpand]);\n\n  useIsomorphicLayoutEffect(() => {\n    if (!open || !triggerEl || !contentRef.current) return;\n\n    // Resolve the document/window the trigger actually lives in, so the\n    // tooltip works when rendered inside an iframe (e.g. docs device preview).\n    const doc = triggerEl.ownerDocument;\n    const win = doc.defaultView ?? window;\n\n    const updatePosition = () => {\n      const trigger = triggerEl.getBoundingClientRect();\n      // offsetWidth/Height are the untransformed layout box; a bounding rect\n      // would report the shrunken size while the zoom-in entrance runs.\n      const content = {\n        width: contentRef.current!.offsetWidth,\n        height: contentRef.current!.offsetHeight,\n      };\n      let top = 0;\n      let left = 0;\n\n      // 1. Calculate Side\n      if (side === \"top\") top = trigger.top - content.height - effectiveOffset;\n      if (side === \"bottom\") top = trigger.bottom + effectiveOffset;\n      if (side === \"left\") left = trigger.left - content.width - effectiveOffset;\n      if (side === \"right\") left = trigger.right + effectiveOffset;\n\n      // 2. Calculate Align\n      if (side === \"top\" || side === \"bottom\") {\n        if (align === \"center\")\n          left = trigger.left + trigger.width / 2 - content.width / 2;\n        if (align === \"start\") left = trigger.left;\n        if (align === \"end\") left = trigger.right - content.width;\n      } else {\n        if (align === \"center\")\n          top = trigger.top + trigger.height / 2 - content.height / 2;\n        if (align === \"start\") top = trigger.top;\n        if (align === \"end\") top = trigger.bottom - content.height;\n      }\n\n      // Clamp within viewport padding to prevent overflowing screen edges\n      const viewportPadding = 8;\n      const docWidth = doc.documentElement.clientWidth || win.innerWidth;\n      const extraLeftShift =\n        isExpand && side === \"left\"\n          ? 146\n          : isExpand && (side === \"top\" || side === \"bottom\")\n            ? 73\n            : 0;\n      const minLeft = viewportPadding + extraLeftShift;\n      const maxLeft = Math.max(\n        minLeft,\n        docWidth - content.width - (isExpand && (side === \"top\" || side === \"bottom\") ? 73 : 0) - viewportPadding,\n      );\n      left = Math.max(minLeft, Math.min(left, maxLeft));\n\n      setCoords({ top: top + win.scrollY, left: left + win.scrollX });\n      setPositioned(true);\n    };\n\n    updatePosition();\n    // Smoothly track moving triggers (e.g. avatar spring/lift transitions) while open\n    let rafId: number;\n    const startTime = typeof performance !== \"undefined\" ? performance.now() : 0;\n    const trackLoop = () => {\n      updatePosition();\n      if (typeof performance !== \"undefined\" && performance.now() - startTime < 600) {\n        rafId = requestAnimationFrame(trackLoop);\n      }\n    };\n    rafId = requestAnimationFrame(trackLoop);\n\n    // Use capture phase for scroll to ensure it updates during nested scroll containers\n    win.addEventListener(\"scroll\", updatePosition, true);\n    win.addEventListener(\"resize\", updatePosition);\n    return () => {\n      cancelAnimationFrame(rafId);\n      win.removeEventListener(\"scroll\", updatePosition, true);\n      win.removeEventListener(\"resize\", updatePosition);\n    };\n  }, [open, side, align, effectiveOffset, triggerEl, isExpand]);\n\n  // triggerEl is null until the trigger commits, which also covers SSR: there\n  // is no document to portal into on the server.\n  if (!triggerEl || !open) return null;\n\n  // Portal into the trigger's own document (supports iframe rendering).\n  const portalTarget = triggerEl.ownerDocument.body;\n\n  const hasStructured = Boolean(title || resolvedDes || icon);\n\n  // Slim, snug dimensions for the expand variant:\n  // Starts as a compact badge (150px wide, 42px high) showing icon and title.\n  // Expands in width to 296px (+146px) across ALL directions (top, bottom, left, right),\n  // with a gentle, slim height increase (42px -> 56px, only +14px) to fit the description underneath.\n  const expandWidth = expanded ? \"296px\" : \"150px\";\n  const expandHeight = expanded ? \"56px\" : \"42px\";\n\n  let expandTransform = \"none\";\n  if (isExpand) {\n    const alignCenter = align === \"center\";\n    if (side === \"top\") {\n      // Offset width expansion symmetrically by 73px to keep centered on trigger\n      // Offset 14px height expansion to keep bottom edge and arrow pinned\n      const tx = alignCenter ? (expanded ? \"-73px\" : \"0px\") : \"0px\";\n      const ty = expanded ? \"-14px\" : \"0px\";\n      expandTransform = `translateX(${tx}) translateY(${ty})`;\n    } else if (side === \"bottom\") {\n      // Offset width expansion symmetrically by 73px to keep centered on trigger\n      // Natural downward height expansion keeps top edge and arrow pinned\n      const tx = alignCenter ? (expanded ? \"-73px\" : \"0px\") : \"0px\";\n      expandTransform = `translateX(${tx}) translateY(0px)`;\n    } else if (side === \"left\") {\n      // Offset full 146px width expansion to keep right edge and arrow pinned\n      // Offset 7px vertical shift to keep arrow centered\n      const tx = expanded ? \"-146px\" : \"0px\";\n      const ty = alignCenter ? (expanded ? \"-7px\" : \"0px\") : \"0px\";\n      expandTransform = `translateX(${tx}) translateY(${ty})`;\n    } else if (side === \"right\") {\n      // Natural rightward width expansion keeps left edge pinned\n      // Offset 7px vertical shift to keep arrow centered\n      const ty = alignCenter ? (expanded ? \"-7px\" : \"0px\") : \"0px\";\n      expandTransform = `translateX(0px) translateY(${ty})`;\n    }\n  }\n\n  if (isExpand) {\n    return createPortal(\n      <TooltipContentContext.Provider value={{ side, align }}>\n        <div\n          style={{\n            position: \"absolute\",\n            top: coords.top,\n            left: coords.left,\n            visibility: positioned ? \"visible\" : \"hidden\",\n            zIndex: 9999,\n            pointerEvents: \"none\",\n            transformOrigin: ARROW_ORIGINS[side]?.[align] ?? \"center\",\n            animation: positioned\n              ? `tooltip-emerge-${side} 480ms cubic-bezier(0.16, 1, 0.3, 1) both`\n              : undefined,\n            willChange: \"transform, opacity\",\n          }}\n        >\n          <div\n            ref={contentRef}\n            role=\"tooltip\"\n            className={cn(\n              \"relative text-xs text-primary-foreground bg-primary shadow-md pointer-events-none box-border px-3.5 rounded-lg flex items-center overflow-visible\",\n              className,\n            )}\n            style={{\n              width: expandWidth,\n              height: expandHeight,\n              transform: expandTransform,\n              transition:\n                \"width 1200ms cubic-bezier(0.16, 1, 0.3, 1), height 1200ms cubic-bezier(0.16, 1, 0.3, 1), transform 1200ms cubic-bezier(0.16, 1, 0.3, 1)\",\n              willChange: \"width, height, transform\",\n            }}\n            {...props}\n          >\n            <style>{`\n              @keyframes tooltip-emerge-top {\n                0% {\n                  opacity: 0;\n                  transform: translateY(12px) scale(0.08);\n                }\n                25% {\n                  opacity: 1;\n                }\n                100% {\n                  opacity: 1;\n                  transform: translateY(0) scale(1);\n                }\n              }\n              @keyframes tooltip-emerge-bottom {\n                0% {\n                  opacity: 0;\n                  transform: translateY(-12px) scale(0.08);\n                }\n                25% {\n                  opacity: 1;\n                }\n                100% {\n                  opacity: 1;\n                  transform: translateY(0) scale(1);\n                }\n              }\n              @keyframes tooltip-emerge-left {\n                0% {\n                  opacity: 0;\n                  transform: translateX(12px) scale(0.08);\n                }\n                25% {\n                  opacity: 1;\n                }\n                100% {\n                  opacity: 1;\n                  transform: translateX(0) scale(1);\n                }\n              }\n              @keyframes tooltip-emerge-right {\n                0% {\n                  opacity: 0;\n                  transform: translateX(-12px) scale(0.08);\n                }\n                25% {\n                  opacity: 1;\n                }\n                100% {\n                  opacity: 1;\n                  transform: translateX(0) scale(1);\n                }\n              }\n              @media (prefers-reduced-motion: reduce) {\n                * {\n                  animation: none !important;\n                }\n              }\n            `}</style>\n            <div className=\"flex items-center gap-2.5 w-full h-full min-w-0\">\n              {icon && (\n                <div\n                  className={cn(\n                    \"size-7 rounded-lg bg-primary-foreground/15 flex items-center justify-center shrink-0 text-current [&_svg]:size-3.5 shadow-2xs\",\n                    iconClassName,\n                  )}\n                >\n                  {icon}\n                </div>\n              )}\n              <div className=\"flex flex-col flex-1 min-w-0 justify-center\">\n                {title && (\n                  <div className=\"font-semibold text-xs leading-none tracking-tight text-primary-foreground truncate whitespace-nowrap\">\n                    {title}\n                  </div>\n                )}\n                {resolvedDes && (\n                  <div\n                    className=\"overflow-hidden min-h-0\"\n                    style={{\n                      maxHeight: expanded ? \"18px\" : \"0px\",\n                      opacity: expanded ? 0.85 : 0,\n                      transform: expanded ? \"translateY(0)\" : \"translateY(-3px)\",\n                      transition:\n                        \"max-height 1200ms cubic-bezier(0.16, 1, 0.3, 1), opacity 800ms cubic-bezier(0.16, 1, 0.3, 1) 220ms, transform 1200ms cubic-bezier(0.16, 1, 0.3, 1)\",\n                      willChange: \"max-height, opacity, transform\",\n                    }}\n                  >\n                    <div className=\"text-[11px] leading-tight text-primary-foreground/85 pt-1 truncate whitespace-nowrap\">\n                      {resolvedDes}\n                    </div>\n                  </div>\n                )}\n                {children}\n              </div>\n            </div>\n            {showArrow && <TooltipArrow className={arrowClassName} />}\n          </div>\n        </div>\n      </TooltipContentContext.Provider>,\n      portalTarget,\n    );\n  }\n\n  // Classic and Staggered variants\n  const entranceClass = isArrowEmerge\n    ? \"\"\n    : isStaggered\n      ? ENTER_STAGGERED[side]\n      : cn(\"zoom-in-95 duration-150\", ENTER_FROM_SIDE[side]);\n\n  return createPortal(\n    <TooltipContentContext.Provider value={{ side, align }}>\n      <div\n        ref={contentRef}\n        role=\"tooltip\"\n        className={cn(\n          \"absolute z-[9999] text-xs text-primary-foreground bg-primary shadow-md pointer-events-none box-border\",\n          (isArrowEmerge || isPop) && (ARROW_ORIGINS[side]?.[align] ?? \"origin-center\"),\n          title\n            ? \"px-3.5 py-2.5 rounded-lg max-w-xs\"\n            : \"px-3 py-1.5 rounded-md font-medium\",\n          !isArrowEmerge && !isPop && \"transition-none\",\n          isArrowEmerge || isPop ? \"\" : entranceClass,\n          className,\n        )}\n        style={\n          {\n            top: coords.top,\n            left: coords.left,\n            visibility: positioned ? \"visible\" : \"hidden\",\n            ...(isPop\n              ? {\n                  animation: positioned\n                    ? `tooltip-pop-${side} 320ms cubic-bezier(0.16, 1, 0.3, 1) both`\n                    : undefined,\n                  willChange: \"transform, opacity\",\n                }\n              : isArrowEmerge\n                ? {\n                    animation: positioned\n                      ? `tooltip-emerge-${side} 600ms cubic-bezier(0.16, 1, 0.3, 1) both`\n                      : undefined,\n                    willChange: \"transform, opacity\",\n                  }\n                : undefined),\n          } as React.CSSProperties\n        }\n        {...props}\n      >\n        {isPop && (\n          <style>{`\n            @keyframes tooltip-pop-top {\n              0% {\n                opacity: 0;\n                transform: translateY(8px) scale(0.65);\n              }\n              60% {\n                opacity: 1;\n                transform: translateY(-2px) scale(1.04);\n              }\n              100% {\n                opacity: 1;\n                transform: translateY(0) scale(1);\n              }\n            }\n            @keyframes tooltip-pop-bottom {\n              0% {\n                opacity: 0;\n                transform: translateY(-8px) scale(0.65);\n              }\n              60% {\n                opacity: 1;\n                transform: translateY(2px) scale(1.04);\n              }\n              100% {\n                opacity: 1;\n                transform: translateY(0) scale(1);\n              }\n            }\n            @keyframes tooltip-pop-left {\n              0% {\n                opacity: 0;\n                transform: translateX(8px) scale(0.65);\n              }\n              60% {\n                opacity: 1;\n                transform: translateX(-2px) scale(1.04);\n              }\n              100% {\n                opacity: 1;\n                transform: translateX(0) scale(1);\n              }\n            }\n            @keyframes tooltip-pop-right {\n              0% {\n                opacity: 0;\n                transform: translateX(-8px) scale(0.65);\n              }\n              60% {\n                opacity: 1;\n                transform: translateX(2px) scale(1.04);\n              }\n              100% {\n                opacity: 1;\n                transform: translateX(0) scale(1);\n              }\n            }\n            @media (prefers-reduced-motion: reduce) {\n              * {\n                animation: none !important;\n              }\n            }\n          `}</style>\n        )}\n        {isArrowEmerge && (\n          <style>{`\n            @keyframes tooltip-emerge-top {\n              0% {\n                opacity: 0;\n                transform: translateY(12px) scale(0.08);\n              }\n              25% {\n                opacity: 1;\n              }\n              100% {\n                opacity: 1;\n                transform: translateY(0) scale(1);\n              }\n            }\n            @keyframes tooltip-emerge-bottom {\n              0% {\n                opacity: 0;\n                transform: translateY(-12px) scale(0.08);\n              }\n              25% {\n                opacity: 1;\n              }\n              100% {\n                opacity: 1;\n                transform: translateY(0) scale(1);\n              }\n            }\n            @keyframes tooltip-emerge-left {\n              0% {\n                opacity: 0;\n                transform: translateX(12px) scale(0.08);\n              }\n              25% {\n                opacity: 1;\n              }\n              100% {\n                opacity: 1;\n                transform: translateX(0) scale(1);\n              }\n            }\n            @keyframes tooltip-emerge-right {\n              0% {\n                opacity: 0;\n                transform: translateX(-12px) scale(0.08);\n              }\n              25% {\n                opacity: 1;\n              }\n              100% {\n                opacity: 1;\n                transform: translateX(0) scale(1);\n              }\n            }\n            @media (prefers-reduced-motion: reduce) {\n              * {\n                animation: none !important;\n              }\n            }\n          `}</style>\n        )}\n        {isStaggered && (\n          <style>{`\n            @keyframes tooltip-icon-pop {\n              0% {\n                opacity: 0;\n                transform: scale(0.3) translateY(4px);\n              }\n              65% {\n                opacity: 1;\n                transform: scale(1.08) translateY(-1px);\n              }\n              100% {\n                opacity: 1;\n                transform: scale(1) translateY(0);\n              }\n            }\n            @keyframes tooltip-title-in {\n              0% {\n                opacity: 0;\n                transform: translateY(6px);\n              }\n              100% {\n                opacity: 1;\n                transform: translateY(0);\n              }\n            }\n            @keyframes tooltip-des-in {\n              0% {\n                opacity: 0;\n                transform: translateY(6px);\n              }\n              100% {\n                opacity: 1;\n                transform: translateY(0);\n              }\n            }\n            @media (prefers-reduced-motion: reduce) {\n              .tooltip-anim-icon,\n              .tooltip-anim-title,\n              .tooltip-anim-des {\n                animation: none !important;\n                opacity: 1 !important;\n                transform: none !important;\n              }\n            }\n          `}</style>\n        )}\n        {hasStructured ? (\n          isExpand ? (\n            <div className=\"flex items-center gap-2.5 w-full h-full min-w-0\">\n              {icon && (\n                <div\n                  className={cn(\n                    \"size-7 rounded-lg bg-primary-foreground/15 flex items-center justify-center shrink-0 text-current [&_svg]:size-3.5 shadow-2xs\",\n                    iconClassName,\n                  )}\n                >\n                  {icon}\n                </div>\n              )}\n              <div className=\"flex flex-col flex-1 min-w-0 justify-center\">\n                {title && (\n                  <div className=\"font-semibold text-xs leading-none tracking-tight text-primary-foreground truncate whitespace-nowrap\">\n                    {title}\n                  </div>\n                )}\n                {resolvedDes && (\n                  <div\n                    className=\"overflow-hidden min-h-0\"\n                    style={{\n                      maxHeight: expanded ? \"18px\" : \"0px\",\n                      opacity: expanded ? 0.85 : 0,\n                      transform: expanded ? \"translateY(0)\" : \"translateY(-3px)\",\n                      transition:\n                        \"max-height 1200ms cubic-bezier(0.16, 1, 0.3, 1), opacity 800ms cubic-bezier(0.16, 1, 0.3, 1) 220ms, transform 1200ms cubic-bezier(0.16, 1, 0.3, 1)\",\n                      willChange: \"max-height, opacity, transform\",\n                    }}\n                  >\n                    <div className=\"text-[11px] leading-tight text-primary-foreground/85 pt-1 truncate whitespace-nowrap\">\n                      {resolvedDes}\n                    </div>\n                  </div>\n                )}\n                {children}\n              </div>\n            </div>\n          ) : (\n            <div className=\"flex items-center gap-2.5 max-w-xs\">\n              {icon && (\n                <div\n                  className={cn(\n                    \"size-8 rounded-lg bg-primary-foreground/15 flex items-center justify-center shrink-0 text-current [&_svg]:size-4 shadow-2xs\",\n                    isStaggered && \"tooltip-anim-icon\",\n                    iconClassName,\n                  )}\n                  style={\n                    isStaggered\n                      ? {\n                          animation: \"tooltip-icon-pop 380ms cubic-bezier(0.16, 1, 0.3, 1) 80ms both\",\n                          willChange: \"transform, opacity\",\n                        }\n                      : undefined\n                  }\n                >\n                  {icon}\n                </div>\n              )}\n              <div className=\"flex flex-col min-w-0 justify-center\">\n                {title && (\n                  <div\n                    className={cn(\n                      \"font-semibold text-xs leading-tight tracking-tight text-primary-foreground truncate whitespace-nowrap\",\n                      isStaggered && \"tooltip-anim-title\",\n                    )}\n                    style={\n                      isStaggered\n                        ? {\n                            animation: \"tooltip-title-in 360ms cubic-bezier(0.16, 1, 0.3, 1) 200ms both\",\n                            willChange: \"transform, opacity\",\n                          }\n                        : undefined\n                    }\n                  >\n                    {title}\n                  </div>\n                )}\n                {resolvedDes && (\n                  <div\n                    className={cn(\n                      \"text-xs leading-tight truncate whitespace-nowrap\",\n                      title\n                        ? \"opacity-85 text-[11px] mt-0.5\"\n                        : \"font-medium opacity-95\",\n                      isStaggered && \"tooltip-anim-des\",\n                    )}\n                    style={\n                      isStaggered\n                        ? {\n                            animation: `tooltip-des-in 380ms cubic-bezier(0.16, 1, 0.3, 1) ${title ? 320 : 200}ms both`,\n                            willChange: \"transform, opacity\",\n                          }\n                        : undefined\n                    }\n                  >\n                    {resolvedDes}\n                  </div>\n                )}\n                {children}\n              </div>\n            </div>\n          )\n        ) : (\n          children\n        )}\n        {showArrow && <TooltipArrow className={arrowClassName} />}\n      </div>\n    </TooltipContentContext.Provider>,\n    portalTarget,\n  );\n}\n",
      "type": "registry:component",
      "target": "components/custom/blocks/TooltipBlock.tsx"
    }
  ],
  "docs": "Named exports: TooltipProvider, Tooltip, TooltipTrigger, TooltipContent. Requires the `animate-in`, `fade-in` and `zoom-in-95` utilities — on Tailwind v4 add `@import \"tw-animate-css\";` to your global stylesheet, on v3 use the `tailwindcss-animate` plugin. Content renders through a React portal at z-index 9999.",
  "categories": [
    "overlays"
  ],
  "type": "registry:component"
}
