{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "match-timeline",
  "title": "Match Timeline",
  "description": "Chronological feed of match events (goals, cards, substitutions).",
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/mrdoge-ui/match-timeline/match-timeline.tsx",
      "content": "import { CircleDot, Square } from \"lucide-react\"\nimport type { ComponentType } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction GoalPostIcon({ className }: { className?: string }) {\n  return (\n    <svg version=\"1.1\" id=\"Capa_1\" xmlns=\"http://www.w3.org/2000/svg\" x=\"0px\" y=\"0px\"\n      width=\"457.338px\" height=\"457.338px\" viewBox=\"0 0 457.338 457.338\"\n      className={cn(className, \"scale-125\")}>\n      <g>\n        <g>\n          <path d=\"M13.738,345.63c7.59,0,13.739-6.155,13.739-13.737V140.401c7.348,1.741,17.236,5.759,27.429,14.942\n\t\t\tc22.019,19.841,48.255,66.575,48.255,176.549c0,7.582,6.146,13.737,13.74,13.737c7.59,0,13.737-6.155,13.737-13.737\n\t\t\tc0-92.563-17.805-157.244-52.874-192.601h246.218v192.601c0,7.582,6.14,13.737,13.737,13.737c7.591,0,13.737-6.155,13.737-13.737\n\t\t\tV139.834c7.515,1.351,18.631,5.144,30.145,15.517c22.024,19.841,48.262,66.576,48.262,176.549c0,7.582,6.146,13.737,13.737,13.737\n\t\t\tc7.598,0,13.737-6.155,13.737-13.737c0-97.324-19.648-163.908-58.396-197.893c-17.909-15.715-35.87-20.514-47.484-21.841v-0.338\n\t\t\th-3.734c-3.687-0.22-6.368-0.114-7.843,0H21.029c-3.689-0.22-6.376-0.114-7.843,0H0v220.08C0,339.475,6.144,345.63,13.738,345.63z\n\t\t\t\"/>\n        </g>\n      </g>\n    </svg>\n  )\n}\n\nexport interface MatchTimelineEntry {\n  id: string\n  /**\n   * Pre-formatted time label, e.g. \"45+2'\", \"90+3'\". Not every sport has a\n   * meaningful running clock, so this is a string the caller formats\n   * rather than a raw minute count; omit when there's none.\n   */\n  time?: string\n  /**\n   * Event kind: open string, sport-specific (e.g. \"goal\", \"yellow-card\").\n   * Unrecognized types get a generic marker rather than being rejected,\n   * since new event types can appear without warning.\n   */\n  type: string\n  side: \"home\" | \"away\" | \"match\"\n  description: string\n  /** Goal-type events only. Renders a running score pill instead of plain text. */\n  score?: { home: number; away: number }\n  /** \"match\"-side entries only. Colors the divider red instead of muted, for the current live period. */\n  live?: boolean\n}\n\nexport interface MatchTimelineProps {\n  /** Entries ordered most-recent-first. */\n  entries: MatchTimelineEntry[]\n  className?: string\n}\n\nconst knownEventIcon: Record<string, ComponentType<{ className?: string }>> = {\n  goal: CircleDot,\n  \"own-goal\": CircleDot,\n  \"yellow-card\": Square,\n  \"red-card\": Square,\n  penalty: GoalPostIcon,\n}\n\nconst knownEventColor: Record<string, string> = {\n  goal: \"text-foreground\",\n  \"own-goal\": \"text-destructive\",\n  \"yellow-card\": \"fill-current text-yellow-500\",\n  \"red-card\": \"fill-current text-destructive\",\n  penalty: \"text-muted-foreground\",\n}\n\nexport function MatchTimelineSkeleton({ rowCount = 4, className }: { rowCount?: number; className?: string }) {\n  return (\n    <ol className={cn(\"flex flex-col\", className)}>\n      {Array.from({ length: rowCount }).map((_, index) => (\n        <li\n          key={index}\n          className={cn(\"flex items-center gap-3 border-l py-2 pl-4\", index % 2 === 1 && \"flex-row-reverse border-r border-l-0 pr-4 pl-0\")}\n        >\n          {/* h-4, not h-3: matches text-xs' real 1rem line-height, not just its font-size */}\n          <span className=\"h-4 w-6 shrink-0 animate-pulse rounded bg-muted\" />\n          <span className=\"size-3.5 shrink-0 animate-pulse rounded-full bg-muted\" />\n          {/* h-5, not h-3: matches text-sm's real 1.25rem line-height, not just its font-size */}\n          <span className=\"h-5 w-32 animate-pulse rounded bg-muted\" />\n        </li>\n      ))}\n    </ol>\n  )\n}\n\nexport function MatchTimeline({ entries, className }: MatchTimelineProps) {\n  return (\n    <ol className={cn(\"flex flex-col\", className)}>\n      {entries.map((entry) => {\n        if (entry.side === \"match\") {\n          return (\n            <li key={entry.id} className=\"flex items-center gap-3 py-3\">\n              <span className={cn(\"h-px flex-1\", entry.live ? \"bg-destructive/40\" : \"bg-border\")} />\n              <span\n                className={cn(\n                  \"shrink-0 text-xs font-semibold tabular-nums\",\n                  entry.live ? \"text-destructive\" : \"text-muted-foreground\"\n                )}\n              >\n                {entry.description}\n              </span>\n              <span className={cn(\"h-px flex-1\", entry.live ? \"bg-destructive/40\" : \"bg-border\")} />\n            </li>\n          )\n        }\n\n        const Icon = knownEventIcon[entry.type] ?? CircleDot\n        const color = knownEventColor[entry.type] ?? \"text-muted-foreground\"\n        const isAway = entry.side === \"away\"\n\n        return (\n          <li\n            key={entry.id}\n            className={cn(\n              \"flex items-center gap-3 border-l py-2 pl-4\",\n              isAway && \"flex-row-reverse border-r border-l-0 pr-4 pl-0 text-right\"\n            )}\n          >\n            <span className=\"w-6 shrink-0 text-xs tabular-nums text-muted-foreground\">{entry.time}</span>\n            <Icon className={cn(\"size-3.5 shrink-0\", color)} />\n            {entry.score ? (\n              <span className=\"flex shrink-0 items-center rounded-full border border-blue-500 px-2 py-0.5 text-sm font-bold tabular-nums text-blue-600 dark:text-blue-400\">\n                {entry.score.home} - {entry.score.away}\n              </span>\n            ) : null}\n            <span className=\"text-sm\">{entry.description}</span>\n          </li>\n        )\n      })}\n    </ol>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}