{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "player-card",
  "title": "Player card",
  "description": "An identity card. The default is a compact tile (square emblem + name + class + rank + stats) for search results and fireteam lists.",
  "dependencies": [
    "@engram/core"
  ],
  "registryDependencies": [
    "@engram/bungie-image",
    "@engram/class-icon",
    "@engram/cn",
    "@engram/emblem-banner",
    "@engram/power-badge",
    "@engram/tokens"
  ],
  "files": [
    {
      "path": "src/components/player-card.tsx",
      "content": "import type { EmblemRef, GuardianClass } from \"@engram/core\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { BungieImage } from \"./bungie-image.js\";\nimport { ClassIcon } from \"./class-icon.js\";\nimport { EmblemBanner } from \"./emblem-banner.js\";\nimport { PowerBadge } from \"./power-badge.js\";\n\n// Bungie emblem banner art is 474×96 — cap the banner row to this aspect so it\n// never stretches (matches the Nameplate model).\nconst BANNER_RATIO = 474 / 96;\n\nexport interface PlayerCardProps\n  extends Omit<HTMLAttributes<HTMLDivElement>, \"color\"> {\n  name: string;\n  emblem: EmblemRef;\n  /** Guardian class — shows the class glyph beside the name. */\n  class?: GuardianClass;\n  /** Secondary line (Bungie code \"#1234\", platform, last-seen…). */\n  subtitle?: ReactNode;\n  /** Right-aligned slot — a rank/glory badge, fireteam role, or action. */\n  rank?: ReactNode;\n  /** Stats row rendered along the bottom (e.g. K/D, wins). */\n  stats?: ReactNode;\n  /**\n   * Use the wide emblem-banner art as the row background (the in-game PGCR /\n   * scoreboard / fireteam row) instead of the small square icon.\n   */\n  banner?: boolean;\n  /** Light level shown as a gold ◆ readout (banner rows). */\n  power?: number;\n  /** Accent color for the banner's bottom progress edge (team/emblem color). */\n  accent?: string;\n  /** Banner fill 0–1 — the thin progress edge under the name. */\n  progress?: number;\n  /** Pixel height of the banner row. Default 56. */\n  height?: number;\n}\n\n/**\n * An identity card. The default is a compact tile (square emblem + name + class\n * + rank + stats) for search results and fireteam lists. With `banner`, the wide\n * emblem art fills the row — the in-game PGCR / scoreboard / roster nameplate,\n * with the light level in gold and a thin accent progress edge.\n */\nexport function PlayerCard({\n  name,\n  emblem,\n  class: guardianClass,\n  subtitle,\n  rank,\n  stats,\n  banner = false,\n  power,\n  accent = \"var(--engram-accent)\",\n  progress,\n  height = 56,\n  className,\n  style,\n  ...props\n}: PlayerCardProps) {\n  if (banner) {\n    return (\n      <div\n        className={cn(\"flex w-full flex-col\", className)}\n        // Cap to the emblem banner's aspect ratio so the art shows at its natural\n        // proportions instead of stretching/zooming across a wide cell.\n        style={{ maxWidth: Math.round(height * BANNER_RATIO), ...style }}\n        {...props}\n      >\n        <EmblemBanner emblem={emblem} height={height} className=\"w-full\">\n          <div\n            className=\"flex h-full w-full items-center gap-2.5 pr-3\"\n            // Inset past the emblem device on the left so the name never covers\n            // the emblem art (the in-game PGCR row).\n            style={{ paddingLeft: Math.round(height * 1.05) }}\n          >\n            <div className=\"min-w-0 flex-1\">\n              <span className=\"block truncate font-engram-display font-bold text-white leading-tight [text-shadow:0_1px_3px_rgba(0,0,0,0.85)]\">\n                {name}\n              </span>\n              {subtitle != null ? (\n                <span className=\"block truncate text-[11px] text-white/75 [text-shadow:0_1px_2px_rgba(0,0,0,0.85)]\">\n                  {subtitle}\n                </span>\n              ) : null}\n            </div>\n            {power != null ? (\n              <PowerBadge\n                power={power}\n                size={Math.round(height * 0.3)}\n                className=\"shrink-0 [filter:drop-shadow(0_1px_2px_rgba(0,0,0,0.8))]\"\n              />\n            ) : null}\n            {rank != null ? <div className=\"shrink-0\">{rank}</div> : null}\n          </div>\n        </EmblemBanner>\n        {/* thin accent progress edge (the emblem/team color line under the row) */}\n        <div className=\"h-[3px] w-full bg-black/40\">\n          <div\n            className=\"h-full\"\n            style={{\n              width:\n                progress != null\n                  ? `${Math.max(0, Math.min(1, progress)) * 100}%`\n                  : \"100%\",\n              background: accent,\n            }}\n          />\n        </div>\n        {stats != null ? (\n          <div className=\"border-engram-border border-x border-b bg-engram-raised px-2.5 py-2\">\n            {stats}\n          </div>\n        ) : null}\n      </div>\n    );\n  }\n\n  return (\n    <div\n      className={cn(\n        \"flex flex-col border border-engram-border bg-engram-raised\",\n        className,\n      )}\n      style={style}\n      {...props}\n    >\n      <div className=\"flex items-center gap-3 p-2.5\">\n        <BungieImage\n          src={emblem.icon}\n          alt=\"\"\n          aria-hidden\n          className=\"size-10 shrink-0 border border-engram-border object-cover\"\n        />\n        <div className=\"min-w-0 flex-1\">\n          <div className=\"flex items-center gap-1.5\">\n            {guardianClass ? (\n              <ClassIcon\n                class={guardianClass}\n                size={14}\n                className=\"shrink-0 text-engram-muted\"\n              />\n            ) : null}\n            <span className=\"truncate font-engram-display font-bold text-engram-fg\">\n              {name}\n            </span>\n          </div>\n          {subtitle != null ? (\n            <div className=\"truncate font-engram-mono text-[11px] text-engram-faint\">\n              {subtitle}\n            </div>\n          ) : null}\n        </div>\n        {rank != null ? <div className=\"shrink-0\">{rank}</div> : null}\n      </div>\n      {stats != null ? (\n        <div className=\"border-engram-border border-t px-2.5 py-2\">{stats}</div>\n      ) : null}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/engram/player-card.tsx"
    }
  ],
  "meta": {
    "level": "component"
  },
  "docs": "Extend without forking: edit the copied source, use `asChild` (Radix Slot) to change the rendered element, pass the typed `annotations` prop for curated data (verdict/tags/per-plug), or use slot / render-prop props for arbitrary content. Requires the @engram/tokens theme (--engram-* CSS variables).",
  "categories": [
    "nameplate"
  ],
  "type": "registry:component"
}