{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"social-card","type":"registry:component","title":"Social Card","description":"A realistic post card in three looks: photo feed, short-text timeline and news feed.","author":"DiMaac (HankTheTank) <https://dimaac.com>","dependencies":["gsap","@gsap/react"],"files":[{"path":"registry/dimaac/social-card/social-card.tsx","type":"registry:component","target":"components/dimaac/social-card.tsx","content":"\"use client\";\n\nimport { useEffect, useRef, useState, type CSSProperties, type ReactNode } from \"react\";\nimport { gsap } from \"gsap\";\nimport { useGSAP } from \"@gsap/react\";\n\n// One post card in three looks: a photo feed, a short-text timeline and a news feed. Same data in,\n// different skin out. Like, repost and save really toggle (the counts follow), and they answer with\n// a small pop and a ring of dots. Double-click the photo of the photo-feed card to like it.\n// Every icon is drawn here as a plain stroke shape, so there are no logos and no icon package.\n\nexport type SocialCardVariant = \"instagram\" | \"x\" | \"facebook\";\n\nexport type SocialCardAuthor = {\n  /** The display name, e.g. \"Mira\". */\n  name: string;\n  /** The handle without the @, e.g. \"mira.photo\". */\n  handle: string;\n  /** Address of the profile picture. When it is missing, the initials are shown. */\n  avatar?: string;\n  /** Shows the verified badge. */\n  verified?: boolean;\n};\n\nexport type SocialCardProps = {\n  /** Which look to draw: a photo feed, a short-text timeline or a news feed. */\n  variant?: SocialCardVariant;\n  /** Who posted it: name, handle, profile picture and the verified badge. */\n  author?: SocialCardAuthor;\n  /** The post text. #hashtags and @mentions are coloured. */\n  content?: string;\n  /** Address of the photo under the text. Leave it out for a text-only post. */\n  image?: string;\n  /** Alt text of the photo. */\n  imageAlt?: string;\n  /** Shape of the photo as width divided by height, e.g. 1.6. 0 uses the look's own shape. */\n  imageRatio?: number;\n  /** Number of likes before the visitor touches the heart. */\n  likes?: number;\n  /** Number of comments. */\n  comments?: number;\n  /** Number of shares (reposts on the timeline look). */\n  shares?: number;\n  /** The age of the post, as text: \"2h\", \"Mar 4\". */\n  timestamp?: string;\n  /** Starts the post as already liked by the visitor. */\n  liked?: boolean;\n  /** Colours of the card: follow the page's dark mode, or force light or dark. */\n  scheme?: \"auto\" | \"light\" | \"dark\";\n  /** Called whenever the visitor likes or unlikes the post. */\n  onLikedChange?: (liked: boolean) => void;\n  /** Extra classes for the card. It is as wide as its parent, up to a limit per look. */\n  className?: string;\n};\n\n// The colours of the card as CSS variables, so the three looks share one set of rules.\n// \"auto\" uses the dark: variant of the project's Tailwind setup.\nconst lightScheme = \"[--sc-bg:#ffffff] [--sc-fg:#0f1419] [--sc-muted:#5d6872] [--sc-line:rgba(15,20,25,0.11)] [--sc-hover:rgba(15,20,25,0.06)] [--sc-shadow:rgba(30,40,60,0.28)]\";\nconst darkScheme = \"[--sc-bg:#16181b] [--sc-fg:#eef0f2] [--sc-muted:#8d97a1] [--sc-line:rgba(255,255,255,0.11)] [--sc-hover:rgba(255,255,255,0.07)] [--sc-shadow:rgba(0,0,0,0.7)]\";\nconst autoScheme = `${lightScheme} dark:[--sc-bg:#16181b] dark:[--sc-fg:#eef0f2] dark:[--sc-muted:#8d97a1] dark:[--sc-line:rgba(255,255,255,0.11)] dark:[--sc-hover:rgba(255,255,255,0.07)] dark:[--sc-shadow:rgba(0,0,0,0.7)]`;\nconst schemes = { auto: autoScheme, light: lightScheme, dark: darkScheme };\n\n// Per look: the widest the card gets, the colour of the verified badge and of the like pop,\n// and the shape of the photo\nconst looks = {\n  instagram: { width: \"max-w-[380px]\", badge: \"#0095f6\", pop: \"#ff3b5c\", ratio: 4 / 5 },\n  x: { width: \"max-w-[560px]\", badge: \"#1d9bf0\", pop: \"#f91880\", ratio: 16 / 10 },\n  facebook: { width: \"max-w-[560px]\", badge: \"#1877f2\", pop: \"#1877f2\", ratio: 16 / 10 },\n} as const;\n\nconst defaultAuthor: SocialCardAuthor = { name: \"Alex\", handle: \"alex\", verified: false };\n\n// \"12,480\" for exact counts, \"12.5K\" for the compact ones on the timeline and news feed\nconst exact = new Intl.NumberFormat(\"en-US\");\nconst compact = new Intl.NumberFormat(\"en-US\", { notation: \"compact\", maximumFractionDigits: 1 });\n\nexport function SocialCard({\n  variant = \"instagram\",\n  author = defaultAuthor,\n  content = \"\",\n  image,\n  imageAlt = \"\",\n  imageRatio = 0,\n  likes = 0,\n  comments = 0,\n  shares = 0,\n  timestamp = \"now\",\n  liked: startsLiked = false,\n  scheme = \"auto\",\n  onLikedChange,\n  className = \"\",\n}: SocialCardProps) {\n  const rootRef = useRef<HTMLElement>(null);\n  const [likedState, setLikedState] = useStateWithReset(startsLiked);\n  const [repostedState, setRepostedState] = useStateWithReset(false);\n  const [savedState, setSavedState] = useStateWithReset(false);\n  const look = looks[variant];\n\n  // The entrance: the parts of the card settle in one after another. It plays again when the look\n  // changes, because the markup underneath is new.\n  const { contextSafe } = useGSAP(\n    () => {\n      const root = rootRef.current;\n      if (!root || window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n      gsap.from(root.querySelectorAll(\"[data-in]\"), { opacity: 0, y: 14, duration: 0.7, ease: \"power3.out\", stagger: 0.07, clearProps: \"opacity,transform\" });\n    },\n    { scope: rootRef, dependencies: [variant], revertOnUpdate: true },\n  );\n\n  // The small pop of an icon, and for a like also a ring of dots flying out\n  const pop = contextSafe((name: \"like\" | \"repost\" | \"save\") => {\n    const root = rootRef.current;\n    if (!root || window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n    const icon = root.querySelector(`[data-pop=\"${name}\"]`);\n    if (icon) gsap.fromTo(icon, { scale: 0.5, rotate: variant === \"facebook\" && name === \"like\" ? -24 : 0 }, { scale: 1, rotate: 0, duration: 0.75, ease: \"elastic.out(1.1, 0.4)\", overwrite: true });\n    if (name !== \"like\") return;\n    const dots = root.querySelectorAll(\"[data-dot]\");\n    gsap.fromTo(\n      dots,\n      { x: 0, y: 0, scale: 0.9, opacity: 1 },\n      { x: (i) => Math.cos((i / dots.length) * Math.PI * 2) * 17, y: (i) => Math.sin((i / dots.length) * Math.PI * 2) * 17, scale: 0, opacity: 0, duration: 0.6, ease: \"power2.out\", overwrite: true },\n    );\n  });\n\n  const setLiked = (next: boolean) => {\n    setLikedState(next);\n    onLikedChange?.(next);\n    if (next) pop(\"like\");\n  };\n  const toggleLike = () => setLiked(!likedState);\n  const toggleRepost = () => {\n    setRepostedState(!repostedState);\n    if (!repostedState) pop(\"repost\");\n  };\n  const toggleSave = () => {\n    setSavedState(!savedState);\n    if (!savedState) pop(\"save\");\n  };\n\n  // Double-click on the photo: like it (never un-like) and show a big heart over it\n  const likeFromPhoto = contextSafe(() => {\n    const root = rootRef.current;\n    const heart = root?.querySelector(\"[data-bigheart]\");\n    if (heart && !window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) {\n      gsap\n        .timeline({ overwrite: true })\n        .fromTo(heart, { scale: 0.3, opacity: 0, y: 0 }, { scale: 1.1, opacity: 1, duration: 0.35, ease: \"back.out(2.2)\" })\n        .to(heart, { scale: 1, duration: 0.12 })\n        .to(heart, { opacity: 0, y: -26, scale: 1.2, duration: 0.45, ease: \"power2.in\" }, \"+=0.18\");\n    }\n    if (!likedState) setLiked(true);\n  });\n\n  // The visitor's own like and repost add one to the numbers they were given\n  const view: View = {\n    author,\n    content,\n    image,\n    imageAlt,\n    ratio: imageRatio > 0 ? imageRatio : look.ratio,\n    likes: likes + (likedState ? 1 : 0) - (startsLiked ? 1 : 0),\n    comments,\n    shares: shares + (repostedState ? 1 : 0),\n    timestamp,\n    liked: likedState,\n    reposted: repostedState,\n    saved: savedState,\n    badge: look.badge,\n    toggleLike,\n    toggleRepost,\n    toggleSave,\n    likeFromPhoto,\n  };\n\n  const rootStyle = { \"--sc-pop\": look.pop, fontFamily: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\" } as CSSProperties;\n\n  return (\n    <article\n      ref={rootRef}\n      data-variant={variant}\n      className={`${schemes[scheme]} ${look.width} w-full overflow-hidden rounded-[20px] border border-[var(--sc-line)] bg-[var(--sc-bg)] text-[var(--sc-fg)] shadow-[0_40px_70px_-40px_var(--sc-shadow)] ${className}`}\n      style={rootStyle}\n    >\n      {variant === \"instagram\" && <PhotoFeed {...view} />}\n      {variant === \"x\" && <Timeline {...view} />}\n      {variant === \"facebook\" && <NewsFeed {...view} />}\n    </article>\n  );\n}\n\n// ---------------------------------------------------------------------------------------------\n// State that starts from a prop and follows it when the prop changes (the playground flips it)\n// ---------------------------------------------------------------------------------------------\n\nfunction useStateWithReset(value: boolean) {\n  const [state, setState] = useState(value);\n  useEffect(() => setState(value), [value]);\n  return [state, setState] as const;\n}\n\n// ---------------------------------------------------------------------------------------------\n// The three looks\n// ---------------------------------------------------------------------------------------------\n\ntype View = {\n  author: SocialCardAuthor;\n  content: string;\n  image?: string;\n  imageAlt: string;\n  ratio: number;\n  likes: number;\n  comments: number;\n  shares: number;\n  timestamp: string;\n  liked: boolean;\n  reposted: boolean;\n  saved: boolean;\n  badge: string;\n  toggleLike: () => void;\n  toggleRepost: () => void;\n  toggleSave: () => void;\n  likeFromPhoto: () => void;\n};\n\nfunction PhotoFeed(v: View) {\n  return (\n    <>\n      <header data-in className=\"flex items-center gap-3 px-3.5 py-3\">\n        <span className=\"rounded-full p-[2px]\" style={{ background: \"linear-gradient(135deg, #ffc53d, #ff5a5f 45%, #c13ad6)\" }}>\n          <span className=\"block rounded-full bg-[var(--sc-bg)] p-[2px]\">\n            <Avatar author={v.author} size={34} />\n          </span>\n        </span>\n        <div className=\"flex min-w-0 flex-1 items-center gap-1 text-[14px] leading-none\">\n          <span className=\"truncate font-semibold\">{v.author.handle}</span>\n          {v.author.verified && <Seal color={v.badge} size={14} />}\n          <span className=\"text-[var(--sc-muted)]\">·</span>\n          <span className=\"text-[var(--sc-muted)]\">{v.timestamp}</span>\n        </div>\n        <IconButton label=\"More options\" className=\"-mr-1.5\">\n          <Icon name=\"more\" size={20} />\n        </IconButton>\n      </header>\n\n      {v.image && (\n        <div data-in className=\"relative overflow-hidden bg-[var(--sc-line)]\" style={{ aspectRatio: v.ratio }} onDoubleClick={v.likeFromPhoto}>\n          <img src={v.image} alt={v.imageAlt} className=\"block h-full w-full object-cover\" draggable={false} decoding=\"async\" />\n          <svg data-bigheart viewBox=\"0 0 24 24\" aria-hidden=\"true\" className=\"pointer-events-none absolute top-1/2 left-1/2 -mt-14 -ml-14 h-28 w-28 opacity-0 drop-shadow-[0_8px_24px_rgba(0,0,0,0.35)]\" fill=\"#fff\">\n            <path d={heartPath} />\n          </svg>\n        </div>\n      )}\n\n      <div data-in className=\"flex items-center gap-1 px-2.5 pt-2.5\">\n        <LikeButton v={v} size={26} className=\"p-1.5\" />\n        <IconButton label=\"Comment\" className=\"p-1.5 hover:opacity-60\">\n          <Icon name=\"comment\" size={25} />\n        </IconButton>\n        <IconButton label=\"Share\" className=\"p-1.5 hover:opacity-60\">\n          <Icon name=\"send\" size={25} />\n        </IconButton>\n        <button type=\"button\" onClick={v.toggleSave} aria-pressed={v.saved} aria-label={v.saved ? \"Remove from saved\" : \"Save\"} className=\"ml-auto cursor-pointer rounded-md p-1.5 transition-opacity hover:opacity-60 focus-visible:outline-2 focus-visible:outline-offset-1\">\n          <span data-pop=\"save\" className=\"block\">\n            <Icon name=\"bookmark\" size={25} fill={v.saved} />\n          </span>\n        </button>\n      </div>\n\n      <div data-in className=\"px-3.5 pt-1 pb-4 text-[14px] leading-[1.45]\">\n        <p className=\"font-semibold\">{exact.format(v.likes)} likes</p>\n        {v.content && (\n          <p className=\"mt-1 break-words whitespace-pre-wrap\">\n            <span className=\"mr-1.5 font-semibold\">{v.author.handle}</span>\n            <RichText text={v.content} color={v.badge} />\n          </p>\n        )}\n        {v.comments > 0 && <p className=\"mt-1.5 text-[var(--sc-muted)]\">View all {exact.format(v.comments)} comments</p>}\n      </div>\n    </>\n  );\n}\n\nfunction Timeline(v: View) {\n  const actionClass = \"group flex cursor-pointer items-center gap-0.5 text-[var(--sc-muted)] transition-colors focus-visible:outline-2\";\n  // The round tint that blooms behind an icon on hover\n  const bloom = \"grid h-[34px] w-[34px] place-items-center rounded-full transition-colors\";\n  return (\n    <div className=\"flex gap-3 px-4 pt-4 pb-2\">\n      <div data-in className=\"shrink-0\">\n        <Avatar author={v.author} size={44} />\n      </div>\n      <div className=\"min-w-0 flex-1\">\n        <div data-in className=\"flex items-center gap-1 text-[15px] leading-5\">\n          <span className=\"truncate font-bold\">{v.author.name}</span>\n          {v.author.verified && <Seal color={v.badge} size={18} />}\n          <span className=\"truncate text-[var(--sc-muted)]\">@{v.author.handle}</span>\n          <span className=\"text-[var(--sc-muted)]\">·</span>\n          <span className=\"shrink-0 text-[var(--sc-muted)]\">{v.timestamp}</span>\n          <IconButton label=\"More options\" className=\"-mr-2 ml-auto text-[var(--sc-muted)]\">\n            <Icon name=\"more\" size={18} />\n          </IconButton>\n        </div>\n\n        {v.content && (\n          <p data-in className=\"mt-0.5 text-[15px] leading-5 break-words whitespace-pre-wrap\">\n            <RichText text={v.content} color={v.badge} />\n          </p>\n        )}\n\n        {v.image && (\n          <div data-in className=\"mt-3 overflow-hidden rounded-2xl border border-[var(--sc-line)] bg-[var(--sc-line)]\" style={{ aspectRatio: v.ratio }}>\n            <img src={v.image} alt={v.imageAlt} className=\"block h-full w-full object-cover\" draggable={false} decoding=\"async\" />\n          </div>\n        )}\n\n        <div data-in className=\"-ml-2 mt-1.5 flex items-center justify-between pr-1 text-[13px]\">\n          <button type=\"button\" aria-label=\"Reply\" className={`${actionClass} hover:text-[#1d9bf0]`}>\n            <span className={`${bloom} group-hover:bg-[#1d9bf0]/10`}>\n              <Icon name=\"comment\" size={18} />\n            </span>\n            <span className=\"min-w-6\">{v.comments > 0 && compact.format(v.comments)}</span>\n          </button>\n          <button type=\"button\" onClick={v.toggleRepost} aria-pressed={v.reposted} aria-label={v.reposted ? \"Undo repost\" : \"Repost\"} className={`${actionClass} hover:text-[#00ba7c] ${v.reposted ? \"!text-[#00ba7c]\" : \"\"}`}>\n            <span className={`${bloom} group-hover:bg-[#00ba7c]/10`}>\n              <span data-pop=\"repost\" className=\"block\">\n                <Icon name=\"repost\" size={18} />\n              </span>\n            </span>\n            <span className=\"min-w-6\">{v.shares > 0 && compact.format(v.shares)}</span>\n          </button>\n          <button type=\"button\" onClick={v.toggleLike} aria-pressed={v.liked} aria-label={v.liked ? \"Unlike\" : \"Like\"} className={`${actionClass} hover:text-[#f91880] ${v.liked ? \"!text-[#f91880]\" : \"\"}`}>\n            <span className={`${bloom} relative group-hover:bg-[#f91880]/10`}>\n              <Dots />\n              <span data-pop=\"like\" className=\"block\">\n                <Icon name=\"heart\" size={18} fill={v.liked} />\n              </span>\n            </span>\n            <span className=\"min-w-6\">{v.likes > 0 && compact.format(v.likes)}</span>\n          </button>\n          <button type=\"button\" onClick={v.toggleSave} aria-pressed={v.saved} aria-label={v.saved ? \"Remove bookmark\" : \"Bookmark\"} className={`${actionClass} hover:text-[#1d9bf0] ${v.saved ? \"!text-[#1d9bf0]\" : \"\"}`}>\n            <span className={`${bloom} group-hover:bg-[#1d9bf0]/10`}>\n              <span data-pop=\"save\" className=\"block\">\n                <Icon name=\"bookmark\" size={18} fill={v.saved} />\n              </span>\n            </span>\n          </button>\n          <button type=\"button\" aria-label=\"Share\" className={`${actionClass} hover:text-[#1d9bf0]`}>\n            <span className={`${bloom} group-hover:bg-[#1d9bf0]/10`}>\n              <Icon name=\"share\" size={18} />\n            </span>\n          </button>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction NewsFeed(v: View) {\n  const action = \"flex flex-1 cursor-pointer items-center justify-center gap-2 rounded-lg py-2 text-[14px] font-semibold text-[var(--sc-muted)] transition-colors hover:bg-[var(--sc-hover)] focus-visible:outline-2\";\n  return (\n    <>\n      <header data-in className=\"flex items-center gap-2.5 px-4 pt-3.5 pb-2.5\">\n        <Avatar author={v.author} size={42} />\n        <div className=\"min-w-0 flex-1 leading-tight\">\n          <p className=\"flex items-center gap-1 text-[15px] font-semibold\">\n            <span className=\"truncate\">{v.author.name}</span>\n            {v.author.verified && <Seal color={v.badge} size={15} />}\n          </p>\n          <p className=\"mt-0.5 flex items-center gap-1 text-[13px] text-[var(--sc-muted)]\">\n            {v.timestamp}\n            <span>·</span>\n            <Icon name=\"globe\" size={12} />\n          </p>\n        </div>\n        <IconButton label=\"More options\" className=\"-mr-1.5 text-[var(--sc-muted)]\">\n          <Icon name=\"more\" size={20} />\n        </IconButton>\n      </header>\n\n      {v.content && (\n        <p data-in className=\"px-4 pb-3 text-[15px] leading-[1.4] break-words whitespace-pre-wrap\">\n          <RichText text={v.content} color={v.badge} />\n        </p>\n      )}\n\n      {v.image && (\n        <div data-in className=\"overflow-hidden bg-[var(--sc-line)]\" style={{ aspectRatio: v.ratio }}>\n          <img src={v.image} alt={v.imageAlt} className=\"block h-full w-full object-cover\" draggable={false} decoding=\"async\" />\n        </div>\n      )}\n\n      <div data-in className=\"flex items-center gap-1.5 px-4 pt-2.5 pb-2.5 text-[14px] text-[var(--sc-muted)]\">\n        <span className=\"grid h-[18px] w-[18px] place-items-center rounded-full text-white\" style={{ background: v.badge }}>\n          <Icon name=\"thumb\" size={11} fill />\n        </span>\n        <span>{compact.format(v.likes)}</span>\n        <span className=\"ml-auto\">\n          {compact.format(v.comments)} comments · {compact.format(v.shares)} shares\n        </span>\n      </div>\n\n      <div data-in className=\"mx-4 mb-1 flex gap-1 border-t border-[var(--sc-line)] py-1\">\n        <button type=\"button\" onClick={v.toggleLike} aria-pressed={v.liked} aria-label={v.liked ? \"Unlike\" : \"Like\"} className={action} style={v.liked ? { color: v.badge } : undefined}>\n          <span data-pop=\"like\" className=\"relative block\">\n            <Dots />\n            <Icon name=\"thumb\" size={20} fill={v.liked} />\n          </span>\n          Like\n        </button>\n        <button type=\"button\" aria-label=\"Comment\" className={action}>\n          <Icon name=\"comment\" size={20} />\n          Comment\n        </button>\n        <button type=\"button\" aria-label=\"Share\" className={action}>\n          <Icon name=\"send\" size={20} />\n          Share\n        </button>\n      </div>\n      <div className=\"h-1.5\" />\n    </>\n  );\n}\n\n// ---------------------------------------------------------------------------------------------\n// Small parts\n// ---------------------------------------------------------------------------------------------\n\nfunction LikeButton({ v, size, className = \"\" }: { v: View; size: number; className?: string }) {\n  return (\n    <button type=\"button\" onClick={v.toggleLike} aria-pressed={v.liked} aria-label={v.liked ? \"Unlike\" : \"Like\"} className={`relative cursor-pointer rounded-md transition-opacity hover:opacity-60 focus-visible:outline-2 focus-visible:outline-offset-1 ${className}`}>\n      <Dots />\n      <span data-pop=\"like\" className=\"block\" style={v.liked ? { color: \"var(--sc-pop)\" } : undefined}>\n        <Icon name=\"heart\" size={size} fill={v.liked} />\n      </span>\n    </button>\n  );\n}\n\n/** Eight dots in the middle of a button. The like pop flies them outwards. */\nfunction Dots() {\n  return (\n    <span aria-hidden=\"true\" className=\"pointer-events-none absolute top-1/2 left-1/2\">\n      {Array.from({ length: 8 }, (_, i) => (\n        <span key={i} data-dot className=\"absolute -mt-[2px] -ml-[2px] h-1 w-1 rounded-full bg-[var(--sc-pop)] opacity-0\" />\n      ))}\n    </span>\n  );\n}\n\nfunction IconButton({ label, className = \"\", children }: { label: string; className?: string; children: ReactNode }) {\n  return (\n    <button type=\"button\" aria-label={label} className={`grid cursor-pointer place-items-center rounded-full p-1.5 transition-colors hover:bg-[var(--sc-hover)] focus-visible:outline-2 ${className}`}>\n      {children}\n    </button>\n  );\n}\n\nfunction Avatar({ author, size }: { author: SocialCardAuthor; size: number }) {\n  const box = { width: size, height: size } as const;\n  if (author.avatar) return <img src={author.avatar} alt={author.name} style={box} className=\"block shrink-0 rounded-full bg-[var(--sc-line)] object-cover\" draggable={false} decoding=\"async\" />;\n  const initials = author.name\n    .split(/\\s+/)\n    .map((word) => word[0] ?? \"\")\n    .join(\"\")\n    .slice(0, 2)\n    .toUpperCase();\n  return (\n    <span aria-label={author.name} role=\"img\" style={{ ...box, fontSize: size * 0.38 }} className=\"grid shrink-0 place-items-center rounded-full bg-[var(--sc-hover)] font-semibold text-[var(--sc-muted)]\">\n      {initials}\n    </span>\n  );\n}\n\n/** The verified badge: a plain scalloped seal with a tick, drawn from two rounded squares */\nfunction Seal({ color, size }: { color: string; size: number }) {\n  return (\n    <svg role=\"img\" aria-label=\"Verified\" viewBox=\"0 0 24 24\" width={size} height={size} className=\"shrink-0\">\n      <rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"4.5\" fill={color} />\n      <rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"4.5\" fill={color} transform=\"rotate(45 12 12)\" />\n      <path d=\"M8.2 12.4l2.5 2.5 5.1-5.4\" fill=\"none\" stroke=\"#fff\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n    </svg>\n  );\n}\n\n/** Text with #hashtags and @mentions picked out in the look's accent colour */\nfunction RichText({ text, color }: { text: string; color: string }) {\n  return (\n    <>\n      {text.split(/([#@][\\p{L}\\p{N}_.]*[\\p{L}\\p{N}_])/u).map((part, i) =>\n        /^[#@]/.test(part) ? (\n          <span key={i} style={{ color }}>\n            {part}\n          </span>\n        ) : (\n          part\n        ),\n      )}\n    </>\n  );\n}\n\nconst heartPath = \"M12 21s-7.6-4.7-9.4-9.4C1.4 8.4 3.2 5 6.7 5c2.1 0 3.6 1.1 5.3 3.3C13.7 6.1 15.2 5 17.3 5c3.5 0 5.3 3.4 4.1 6.6C19.6 16.3 12 21 12 21z\";\n\nconst paths = {\n  heart: heartPath,\n  comment: \"M21 11.5a8.5 8.5 0 0 1-12.4 7.6L3 20.6l1.5-5.1A8.5 8.5 0 1 1 21 11.5z\",\n  send: \"M21.5 3 10.6 13.9M21.5 3l-6.8 18-4.1-7.1L3.5 9.8z\",\n  bookmark: \"M6.5 3.5h11v17.2L12 16.5l-5.5 4.2z\",\n  repost: \"M17 3l4 4-4 4M3 11V9.5A2.5 2.5 0 0 1 5.5 7H21M7 21l-4-4 4-4M21 13v1.5a2.5 2.5 0 0 1-2.5 2.5H3\",\n  share: \"M12 15V3.5M7.5 8 12 3.5 16.5 8M5 13v6a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6\",\n  thumb: \"M7.5 10.5V21H3.5V10.5zM7.5 10.5 11.4 3c1.6 0 2.6 1.3 2.3 2.8L13.1 9H19a2 2 0 0 1 2 2.4l-1.4 7.2a2 2 0 0 1-2 1.6H7.5\",\n} as const;\n\ntype IconName = keyof typeof paths | \"more\" | \"globe\";\n\nfunction Icon({ name, size, fill = false }: { name: IconName; size: number; fill?: boolean }) {\n  const common = { width: size, height: size, viewBox: \"0 0 24 24\", \"aria-hidden\": true as const, className: \"block\" };\n  if (name === \"more\") {\n    return (\n      <svg {...common} fill=\"currentColor\">\n        <circle cx=\"5\" cy=\"12\" r=\"1.8\" />\n        <circle cx=\"12\" cy=\"12\" r=\"1.8\" />\n        <circle cx=\"19\" cy=\"12\" r=\"1.8\" />\n      </svg>\n    );\n  }\n  if (name === \"globe\") {\n    return (\n      <svg {...common} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n        <circle cx=\"12\" cy=\"12\" r=\"9\" />\n        <path d=\"M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18\" />\n      </svg>\n    );\n  }\n  return (\n    <svg {...common} fill={fill ? \"currentColor\" : \"none\"} stroke=\"currentColor\" strokeWidth=\"1.9\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n      <path d={paths[name]} />\n    </svg>\n  );\n}\n"}],"categories":["cards","gsap","card","social","post","like","feed"],"docs":"import { SocialCard } from \"@/components/dimaac/social-card\". Give its parent a size: it fills it. Docs, playground and props: https://ui.dimaac.com/components/social-card","meta":{"tier":"free","preview":"https://ui.dimaac.com/components/social-card","credits":[{"by":"rena","url":"https://www.lummi.ai/photo/modern-architectural-design-aozka","what":"Photo","source":"Lummi","licence":"Lummi License"},{"by":"Benginur Hajjaj","url":"https://www.lummi.ai/photo/calm-portrait-d1zqo","what":"Profile picture","source":"Lummi","licence":"Lummi License"}]}}