{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "civilization-card",
  "type": "registry:component",
  "title": "Civilization Card",
  "description": "Civilization overview with hero band, BCE/CE era timeline, key stats, achievements, and notable leaders.",
  "dependencies": [
    "@vllnt/ui@^0.2.1",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/civilization-card/civilization-card.tsx",
      "content": "import {\n  type ComponentPropsWithoutRef,\n  forwardRef,\n  type ReactNode,\n} from \"react\";\n\nimport { Globe } from \"lucide-react\";\n\nimport { cn } from \"@vllnt/ui\";\nimport { Badge } from \"@vllnt/ui\";\n\nconst CIVILIZATION_COLOR_VARIANTS: Record<\n  CivilizationCardColor,\n  { gradient: string; ring: string }\n> = {\n  amber: {\n    gradient: \"from-amber-500/20 to-amber-700/40\",\n    ring: \"ring-amber-500/30\",\n  },\n  blue: {\n    gradient: \"from-blue-500/20 to-blue-700/40\",\n    ring: \"ring-blue-500/30\",\n  },\n  emerald: {\n    gradient: \"from-emerald-500/20 to-emerald-700/40\",\n    ring: \"ring-emerald-500/30\",\n  },\n  neutral: {\n    gradient: \"from-muted to-muted-foreground/10\",\n    ring: \"ring-border\",\n  },\n  purple: {\n    gradient: \"from-purple-500/20 to-purple-700/40\",\n    ring: \"ring-purple-500/30\",\n  },\n  red: {\n    gradient: \"from-red-500/20 to-red-700/40\",\n    ring: \"ring-red-500/30\",\n  },\n};\n\n/**\n * Color theme for the {@link CivilizationCard} hero band.\n *\n * @public\n */\nexport type CivilizationCardColor =\n  | \"amber\"\n  | \"blue\"\n  | \"emerald\"\n  | \"neutral\"\n  | \"purple\"\n  | \"red\";\n\n/**\n * Era span (start / end years) for {@link CivilizationCardProps}.\n *\n * Use positive integers for CE / negative integers for BCE. `end` may be\n * omitted when the civilization is extant.\n *\n * @public\n */\nexport type CivilizationCardEra = {\n  /** End year. Negative for BCE; omit when extant. */\n  end?: number;\n  /** Start year. Negative for BCE. */\n  start: number;\n};\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type CivilizationCardLabels = {\n  /** Heading above the achievements list. Defaults to `\"Achievements\"`. */\n  achievements?: string;\n  /** Caption for the capital row. Defaults to `\"Capital\"`. */\n  capital?: string;\n  /** Caption for the duration stat. Defaults to `\"Duration\"`. */\n  duration?: string;\n  /** Heading above the leaders list. Defaults to `\"Notable leaders\"`. */\n  leaders?: string;\n  /** Caption for the peak population stat. Defaults to `\"Peak population\"`. */\n  peakPopulation?: string;\n  /** Aria-label on the era timeline bar. Defaults to `\"Era timeline\"`. */\n  timeline?: string;\n};\n\n/**\n * Props for {@link CivilizationCard}.\n *\n * @public\n */\nexport type CivilizationCardProps = {\n  /** Notable achievements / cultural contributions. */\n  achievements?: ReactNode[];\n  /** Optional primary CTA href. Renders the card as a link card when set. */\n  actionHref?: string;\n  /** Optional capital city. */\n  capital?: ReactNode;\n  /** Color theme for the hero band. Defaults to `\"neutral\"`. */\n  color?: CivilizationCardColor;\n  /** Era span. */\n  era?: CivilizationCardEra;\n  /** Optional hero image src. Falls back to a globe icon. */\n  image?: string;\n  /** Localizable captions. */\n  labels?: CivilizationCardLabels;\n  /** Notable leaders. */\n  leaders?: ReactNode[];\n  /** Display name. */\n  name: ReactNode;\n  /** Optional peak population stat (string). */\n  peakPopulation?: ReactNode;\n  /** Optional geographic region. */\n  region?: ReactNode;\n} & ComponentPropsWithoutRef<\"article\">;\n\nconst DEFAULT_LABELS = {\n  achievements: \"Achievements\",\n  capital: \"Capital\",\n  duration: \"Duration\",\n  leaders: \"Notable leaders\",\n  peakPopulation: \"Peak population\",\n  timeline: \"Era timeline\",\n} as const satisfies Required<CivilizationCardLabels>;\n\nfunction formatEraYear(year: number): string {\n  if (year < 0) return `${Math.abs(year).toString()} BCE`;\n  return `${year.toString()} CE`;\n}\n\nfunction formatEra(era: CivilizationCardEra): string {\n  const start = formatEraYear(era.start);\n  if (era.end === undefined) return `${start} – present`;\n  return `${start} – ${formatEraYear(era.end)}`;\n}\n\nfunction getDuration(era: CivilizationCardEra | undefined): string | undefined {\n  if (!era) return undefined;\n  const end = era.end ?? new Date().getFullYear();\n  const years = end - era.start;\n  if (years <= 0) return undefined;\n  return `${years.toString()} years`;\n}\n\ntype HeroProps = {\n  color: CivilizationCardColor;\n  image?: string;\n  imageAlt?: string;\n};\n\nfunction CivilizationHero({ color, image, imageAlt }: HeroProps): ReactNode {\n  const palette = CIVILIZATION_COLOR_VARIANTS[color];\n  return (\n    <div\n      className={cn(\n        \"relative h-32 w-full overflow-hidden rounded-t-2xl bg-gradient-to-br\",\n        palette.gradient,\n      )}\n    >\n      {image ? (\n        <img\n          alt={imageAlt ?? \"\"}\n          className=\"h-full w-full object-cover mix-blend-multiply\"\n          src={image}\n        />\n      ) : (\n        <div className=\"flex h-full w-full items-center justify-center text-muted-foreground/50\">\n          <Globe aria-hidden=\"true\" className=\"size-12\" />\n        </div>\n      )}\n    </div>\n  );\n}\n\ntype EraTimelineProps = {\n  era: CivilizationCardEra;\n  label: string;\n};\n\nfunction EraTimeline({ era, label }: EraTimelineProps): ReactNode {\n  const eraLabel = formatEra(era);\n  return (\n    <div\n      aria-label={`${label}: ${eraLabel}`}\n      className=\"flex flex-col gap-1\"\n      role=\"img\"\n    >\n      <span className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n        {eraLabel}\n      </span>\n      <div className=\"h-1.5 w-full rounded-full bg-muted\">\n        <span className=\"block h-full w-2/3 rounded-full bg-primary\" />\n      </div>\n    </div>\n  );\n}\n\ntype StatsProps = {\n  capital?: ReactNode;\n  capitalCaption: string;\n  durationCaption: string;\n  durationValue?: string;\n  peakCaption: string;\n  peakPopulation?: ReactNode;\n};\n\nfunction CivilizationStats({\n  capital,\n  capitalCaption,\n  durationCaption,\n  durationValue,\n  peakCaption,\n  peakPopulation,\n}: StatsProps): ReactNode {\n  const items: { caption: string; value: ReactNode }[] = [];\n  if (capital) items.push({ caption: capitalCaption, value: capital });\n  if (peakPopulation) {\n    items.push({ caption: peakCaption, value: peakPopulation });\n  }\n  if (durationValue) {\n    items.push({ caption: durationCaption, value: durationValue });\n  }\n  if (items.length === 0) return null;\n  return (\n    <dl className=\"grid grid-cols-2 gap-x-3 gap-y-2 text-sm\">\n      {items.map((item) => (\n        <div className=\"flex flex-col\" key={item.caption}>\n          <dt className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n            {item.caption}\n          </dt>\n          <dd className=\"font-medium text-foreground\">{item.value}</dd>\n        </div>\n      ))}\n    </dl>\n  );\n}\n\ntype ListBlockProps = {\n  emptyHidden?: boolean;\n  heading: string;\n  items: ReactNode[];\n  variant: \"badge\" | \"list\";\n};\n\nfunction CivilizationListBlock({\n  heading,\n  items,\n  variant,\n}: ListBlockProps): ReactNode {\n  if (items.length === 0) return null;\n  return (\n    <div className=\"flex flex-col gap-2\">\n      <h4 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n        {heading}\n      </h4>\n      {variant === \"badge\" ? (\n        <div className=\"flex flex-wrap gap-1.5\">\n          {items.map((item, index) => (\n            <Badge key={`${heading}-${index.toString()}`} variant=\"secondary\">\n              {item}\n            </Badge>\n          ))}\n        </div>\n      ) : (\n        <ul className=\"flex flex-col gap-1 text-sm text-foreground\">\n          {items.map((item, index) => (\n            <li\n              className=\"leading-tight\"\n              key={`${heading}-${index.toString()}`}\n            >\n              {item}\n            </li>\n          ))}\n        </ul>\n      )}\n    </div>\n  );\n}\n\n/**\n * Overview card for historical civilizations: hero band with optional image\n * + color theme, era timeline (BCE / CE / present), key stats, achievement\n * chips, notable leaders, and an optional follow-up link.\n *\n * @example\n * ```tsx\n * <CivilizationCard\n *   name=\"Roman Empire\"\n *   era={{ start: -27, end: 476 }}\n *   region=\"Mediterranean\"\n *   capital=\"Rome\"\n *   peakPopulation=\"70 million\"\n *   color=\"red\"\n *   achievements={[\"Aqueducts\", \"Roads\", \"Law\", \"Architecture\"]}\n *   leaders={[\"Augustus\", \"Trajan\", \"Marcus Aurelius\"]}\n *   actionHref=\"/civilizations/rome\"\n * />\n * ```\n *\n * @public\n */\ntype CivilizationBodyProps = {\n  achievements?: ReactNode[];\n  actionHref?: string;\n  capital?: ReactNode;\n  era?: CivilizationCardEra;\n  labels: Required<CivilizationCardLabels>;\n  leaders?: ReactNode[];\n  name: ReactNode;\n  peakPopulation?: ReactNode;\n  region?: ReactNode;\n};\n\nfunction CivilizationBody({\n  achievements,\n  actionHref,\n  capital,\n  era,\n  labels,\n  leaders,\n  name,\n  peakPopulation,\n  region,\n}: CivilizationBodyProps): ReactNode {\n  const durationValue = getDuration(era);\n  return (\n    <div className=\"flex flex-col gap-4 p-5\">\n      <header className=\"flex flex-col gap-1\">\n        <h3 className=\"text-lg font-semibold leading-tight tracking-tight\">\n          {name}\n        </h3>\n        {region ? (\n          <p className=\"text-sm text-muted-foreground\">{region}</p>\n        ) : null}\n      </header>\n\n      {era ? <EraTimeline era={era} label={labels.timeline} /> : null}\n\n      <CivilizationStats\n        capital={capital}\n        capitalCaption={labels.capital}\n        durationCaption={labels.duration}\n        durationValue={durationValue}\n        peakCaption={labels.peakPopulation}\n        peakPopulation={peakPopulation}\n      />\n\n      {achievements && achievements.length > 0 ? (\n        <CivilizationListBlock\n          heading={labels.achievements}\n          items={achievements}\n          variant=\"badge\"\n        />\n      ) : null}\n\n      {leaders && leaders.length > 0 ? (\n        <CivilizationListBlock\n          heading={labels.leaders}\n          items={leaders}\n          variant=\"list\"\n        />\n      ) : null}\n\n      {actionHref ? (\n        <a\n          className=\"text-sm font-medium text-primary underline-offset-4 hover:underline\"\n          href={actionHref}\n        >\n          Explore →\n        </a>\n      ) : null}\n    </div>\n  );\n}\n\nexport const CivilizationCard = forwardRef<HTMLElement, CivilizationCardProps>(\n  (props, ref) => {\n    const {\n      achievements,\n      actionHref,\n      capital,\n      className,\n      color = \"neutral\",\n      era,\n      image,\n      labels,\n      leaders,\n      name,\n      peakPopulation,\n      region,\n      ...rest\n    } = props;\n\n    const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n    const palette = CIVILIZATION_COLOR_VARIANTS[color];\n    const altName = typeof name === \"string\" ? name : undefined;\n\n    return (\n      <article\n        className={cn(\n          \"flex flex-col overflow-hidden rounded-2xl border bg-background text-foreground shadow-sm ring-1\",\n          palette.ring,\n          className,\n        )}\n        ref={ref}\n        {...rest}\n      >\n        <CivilizationHero color={color} image={image} imageAlt={altName} />\n        <CivilizationBody\n          achievements={achievements}\n          actionHref={actionHref}\n          capital={capital}\n          era={era}\n          labels={resolvedLabels}\n          leaders={leaders}\n          name={name}\n          peakPopulation={peakPopulation}\n          region={region}\n        />\n      </article>\n    );\n  },\n);\nCivilizationCard.displayName = \"CivilizationCard\";\n\n/**\n * Props for {@link CivilizationComparison}.\n *\n * @public\n */\nexport type CivilizationComparisonProps = ComponentPropsWithoutRef<\"div\">;\n\n/**\n * Side-by-side comparison container for {@link CivilizationCard}s. Renders\n * children in a responsive grid (single column on mobile, two columns on\n * `md`, three on `lg`).\n *\n * @public\n */\nexport const CivilizationComparison = forwardRef<\n  HTMLDivElement,\n  CivilizationComparisonProps\n>(({ children, className, ...rest }, ref) => {\n  return (\n    <div\n      className={cn(\n        \"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3\",\n        className,\n      )}\n      ref={ref}\n      {...rest}\n    >\n      {children}\n    </div>\n  );\n});\nCivilizationComparison.displayName = \"CivilizationComparison\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
