{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dk-product-card",
  "title": "DKProductCard",
  "description": "A product card for MDX blogs. Title, description, image, status strip, and note-style bodies. Pairs with the @diklein/dkproductcard remark plugin, which turns bare Amazon URLs into cards automatically.",
  "dependencies": [
    "@diklein/dkproductcard"
  ],
  "files": [
    {
      "path": "registry/dk-product-card/product-card.tsx",
      "content": "import type { ReactNode } from 'react'\n\nexport interface DKProductCardProps {\n  /** Product name, set in a medium weight above the description. */\n  title?: string\n  /** One-line supporting copy, muted. */\n  description?: string\n  /** Where the card links. Omit it (and asin) for a card that is not clickable. */\n  href?: string\n  /** Amazon ASIN. Builds the href (amazon.com/dp/ASIN) and switches the label to \"Amazon\". */\n  asin?: string\n  /** Link label. Defaults to \"Amazon\" for Amazon links, \"View\" otherwise. */\n  label?: string\n  /** Product image URL, rendered in a fixed square slot on the left. */\n  image?: string\n  /** Alt text for the product image. Falls back to the title. */\n  imageAlt?: string\n  /** Sugar for strip=\"Retired\". */\n  retired?: boolean\n  /** Status-strip text across the top of the card, e.g. \"Retired\" or \"Updated\". */\n  strip?: string\n  /** Rich body content for note-style cards (a strip plus a sentence, no image or title).\n   *  Markdown links inside survive from MDX and pick up the accent treatment. */\n  children?: ReactNode\n  /** Render the product image yourself (e.g. with next/image). Apply ctx.className\n   *  and ctx.alt to the element you return. */\n  renderImage?: (src: string, ctx: { alt: string; className: string }) => ReactNode\n}\n\nexport function DKProductCard({\n  title,\n  description,\n  href,\n  asin,\n  label,\n  image,\n  imageAlt,\n  retired,\n  strip,\n  children,\n  renderImage,\n}: DKProductCardProps) {\n  const url = href ?? (asin ? `https://www.amazon.com/dp/${asin}` : undefined)\n  const isAmazon = !!asin || (url?.includes('amazon.com') ?? false)\n  const displayLabel = label ?? (isAmazon ? 'Amazon' : 'View')\n\n  /* Some products cannot be linked (discontinued, page gone). A card with a dead\n     \"View\" pointing at \"#\" is worse than a card that simply does not claim to be\n     clickable, so with no href and no asin this renders as a plain div: no link,\n     no label, none of the hover affordances that would promise one. */\n  const Shell = url ? 'a' : 'div'\n  const shellProps = url ? { href: url, target: '_blank', rel: 'noopener noreferrer' } : {}\n\n  const stripText = strip ?? (retired ? 'Retired' : null)\n  const isNote = !image && !title && !!children\n  const alt = imageAlt ?? title ?? ''\n\n  return (\n    <div className=\"dkpc-scope dkpc-root\">\n      <Shell {...shellProps} className={`dkpc-card${url ? ' dkpc-linked' : ''}`}>\n        {/* The state belongs to the whole card, not to the title, so it reads better\n            as a band across the top than as a pill floating inside the content. */}\n        {stripText && <div className=\"dkpc-strip\">{stripText}</div>}\n        {/* Note-style cards tuck the body under the strip with matching padding, so\n            the sentence left-aligns with the strip label. Product cards keep the\n            roomier frame. */}\n        <div className={`dkpc-row${isNote ? ' dkpc-row-note' : ''}`}>\n          {image && (\n            <div className=\"dkpc-image-slot\">\n              {renderImage ? (\n                renderImage(image, { alt, className: 'dkpc-image' })\n              ) : (\n                <img src={image} alt={alt} className=\"dkpc-image\" loading=\"lazy\" />\n              )}\n            </div>\n          )}\n          <div className=\"dkpc-main\">\n            {title && <p className=\"dkpc-title\">{title}</p>}\n            {children && <div className=\"dkpc-body\">{children}</div>}\n            {description && <p className=\"dkpc-desc\">{description}</p>}\n            {url && (\n              <p className=\"dkpc-label\">\n                <span className=\"dkpc-label-text\">{displayLabel}</span>\n              </p>\n            )}\n          </div>\n        </div>\n      </Shell>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dk-product-card/product-card.tsx"
    },
    {
      "path": "registry/dk-product-card/dk-product-card.css",
      "content": "/* DKProductCard — theme tokens + every rule the card needs. No Tailwind required.\n *\n * Every color routes through a --dkpc-* variable so theming is one override away:\n *\n *   .dkpc-scope { --dkpc-accent: rebeccapurple; }\n *\n * Dark mode follows the shadcn convention (a `.dark` class on <html>) and falls back to\n * prefers-color-scheme when no theme class is in play. Fonts default to the system stack;\n * point --dkpc-font-sans at your own face to match your site. */\n\n.dkpc-scope {\n  --dkpc-fg: #1a1a1a;\n  --dkpc-muted: #6b6b6b;\n  --dkpc-surface: rgb(0 0 0 / 0.05);\n  --dkpc-accent: #e03131;\n  --dkpc-outline: rgb(0 0 0 / 0.1);\n  --dkpc-font-sans: ui-sans-serif, system-ui, sans-serif;\n}\n\n.dark .dkpc-scope {\n  --dkpc-fg: #ededed;\n  --dkpc-muted: #9a9a9a;\n  --dkpc-surface: rgb(255 255 255 / 0.08);\n  --dkpc-outline: rgb(255 255 255 / 0.1);\n}\n\n/* No theme class anywhere up the tree? Track the OS. (:where() keeps specificity at zero\n   so the .dark rule above always wins when present.) */\n@media (prefers-color-scheme: dark) {\n  :where(html:not(.light) .dkpc-scope) {\n    --dkpc-fg: #ededed;\n    --dkpc-muted: #9a9a9a;\n    --dkpc-surface: rgb(255 255 255 / 0.08);\n    --dkpc-outline: rgb(255 255 255 / 0.1);\n  }\n}\n\n.dkpc-root {\n  font-family: var(--dkpc-font-sans);\n  color: var(--dkpc-fg);\n  margin-bottom: 2rem;\n}\n\n/* A run of consecutive cards reads as one group: tighten the gap between them. */\n.dkpc-root:has(+ .dkpc-root) {\n  margin-bottom: 1.5rem;\n}\n\n.dkpc-card {\n  display: block;\n  overflow: hidden;\n  border: 1px solid var(--dkpc-outline);\n  color: inherit;\n  text-decoration: none;\n}\n\n.dkpc-linked {\n  transition: border-color 0.15s ease;\n}\n\n.dkpc-linked:hover,\n.dkpc-linked:active {\n  border-color: var(--dkpc-muted);\n}\n\n/* The status strip: a sliver of chrome that labels the card before its content does.\n   Foreground color, not muted: it is a label the eye should catch, and max contrast\n   holds up in both themes. */\n.dkpc-strip {\n  font-size: 0.875rem;\n  font-weight: 500;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  color: var(--dkpc-fg);\n  background: var(--dkpc-surface);\n  padding: 0.625rem 1.5rem 0.625rem 1rem;\n}\n\n.dkpc-row {\n  display: flex;\n  align-items: flex-start;\n  gap: 2rem;\n  padding: 1.5rem;\n}\n\n/* Note-style cards (strip + a sentence): tighter padding, matched to the strip's\n   left inset so the sentence aligns with the strip label. */\n.dkpc-row-note {\n  padding: 0.75rem 1rem 1rem;\n}\n\n.dkpc-image-slot {\n  position: relative;\n  flex-shrink: 0;\n  width: 6rem;\n  height: 6rem;\n}\n\n@media (min-width: 900px) {\n  .dkpc-image-slot {\n    width: 9rem;\n    height: 9rem;\n  }\n}\n\n.dkpc-image {\n  width: 100%;\n  height: 100%;\n  object-fit: contain;\n}\n\n/* The body column takes the row's remaining width, so full-width bodies can spread. */\n.dkpc-main {\n  min-width: 0;\n  flex: 1;\n}\n\n.dkpc-title {\n  margin: 0;\n  font-weight: 500;\n  line-height: 1.375;\n}\n\n.dkpc-body {\n  line-height: 1.6;\n}\n\n.dkpc-body p {\n  margin: 0;\n}\n\n/* Multi-paragraph note bodies keep their rhythm; a single sentence stays flush. */\n.dkpc-body p + p {\n  margin-top: 0.75rem;\n}\n\n/* MDX can hand the body over as bare inline content (no <p> wrapper), which prose\n   selectors like `article p a` never match. Style links on the container instead,\n   so a link in a note card cannot silently render as plain text. */\n.dkpc-body a {\n  color: var(--dkpc-accent);\n  text-decoration: underline;\n  text-underline-offset: 3px;\n  text-decoration-color: color-mix(in oklch, var(--dkpc-accent) 30%, transparent);\n  transition: text-decoration-color 0.15s ease;\n}\n\n.dkpc-body a:hover {\n  text-decoration-color: var(--dkpc-accent);\n}\n\n.dkpc-desc {\n  margin: 0.75rem 0 0;\n  font-size: 1.125rem;\n  color: var(--dkpc-muted);\n}\n\n.dkpc-label {\n  margin: 1rem 0 0;\n  font-size: 1.125rem;\n  color: var(--dkpc-accent);\n}\n\n.dkpc-label-text {\n  text-decoration: underline;\n  text-underline-offset: 3px;\n  text-decoration-color: color-mix(in oklch, var(--dkpc-accent) 30%, transparent);\n  transition: text-decoration-color 0.15s ease;\n}\n\n.dkpc-linked:hover .dkpc-label-text {\n  text-decoration-color: var(--dkpc-accent);\n}\n",
      "type": "registry:file",
      "target": "components/dk-product-card/dk-product-card.css"
    }
  ],
  "type": "registry:component"
}