{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "nx-app-icon",
  "title": "Nx App Icon",
  "description": "The Google-style Nexus product app-icon family: the arrow emblem on a rounded tile, tinted by fixed brand tones — a set of identity artefacts that stay constant across light/dark and brand variants.",
  "registryDependencies": [
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/nx-app-icon.tsx",
      "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/**\n * NxAppIcon — the Google-style Nexus product-icon family: the arrow emblem\n * centred-low on a rounded tile, one tile per product, tinted by a `tone` drawn\n * from the brand-variant colour system.\n *\n * IMPORTANT — these are BRAND ARTEFACTS, not theme tokens. Every colour here is\n * a fixed hex by design: an app icon is an identity that must look identical on\n * every surface and platform, so it must NOT flip with light/dark mode or the\n * `data-brand` variant. That is why this component uses literal hex fills rather\n * than the `--color-*` CSS variables the rest of nx-ui builds on. Do not\n * \"tokenise\" these — it would break the family's consistency.\n *\n * The arrow always points down-left; never flip, mirror or rotate it. It is\n * anchored slightly toward the tile's bottom-left (per the arrow's direction)\n * with optical margins, occupying ~56% of the tile.\n *\n * @example\n * <NxAppIcon tone=\"ocean\" glyph=\"Geo\" label=\"Nexus Geo\" size={64} />\n * <NxAppIcon tone=\"navy\" size={28} />\n */\n\n/** Arrow silhouette (solid) in the 512-emblem canvas units. */\nconst ARROW_SOLID =\n  \"M128.055 9.93506H87.925H7.92499V50.0651H59.555L7.92499 101.685L22.115 115.875L36.305 130.065L87.935 78.4351V130.065H127.905L128.055 9.93506Z\"\n/** The inset knockout that turns the fill into a keyline for the bold weight. */\nconst ARROW_INSET =\n  \"M124.625 126.635V50.0651L124.615 50.0551V13.3651H11.355V46.6351H67.845L61.985 52.4951L12.785 101.695L24.545 113.455L36.305 125.215L85.505 76.0151L91.365 70.1551V126.635H124.625Z\"\n\nexport type NxAppIconTone =\n  | \"navy\"\n  | \"yellow\"\n  | \"paper\"\n  | \"ultraviolet\"\n  | \"evergreen\"\n  | \"ocean\"\n  | \"ember\"\n  | \"graphite\"\n\nexport type NxAppIconShape = \"squircle\" | \"rounded\" | \"circle\"\n\n/**\n * The mark drawn on the tile. `emblem` (default) is the Nexus arrow — the\n * brand fallback. The other marks give individual apps their own identity\n * while staying inside the same tile system (fixed-hex fills, ~55% of the\n * tile, centred). Add a new mark by adding one entry to `MARK_PATHS` — all\n * marks are single-colour fills (the tile's `fg`) drawn directly in the\n * 100-unit tile canvas so they hold up from 24px upward.\n */\nexport type NxAppIconMark =\n  | \"emblem\"\n  | \"user\"\n  | \"shield\"\n  | \"bolt\"\n  | \"mail\"\n  | \"exchange\"\n  | \"sliders\"\n  | \"pin\"\n\n/** Glyph fills for the non-emblem marks, in the 100-unit tile canvas. Keep\n * every mark inside the central ~22–78 box (the arrow's footprint). */\nconst MARK_PATHS: Record<\n  Exclude<NxAppIconMark, \"emblem\">,\n  { d?: string; evenodd?: boolean; circles?: Array<{ cx: number; cy: number; r: number }> }\n> = {\n  // Nexus Account — a simple, balanced person silhouette: profile and identity.\n  user: {\n    d: \"M50 21 A14 14 0 1 0 50 49 A14 14 0 1 0 50 21 Z M25 78 C27 62 36 54 50 54 C64 54 73 62 75 78 Z\",\n    evenodd: true,\n  },\n  // Assurance — a shield with a check knocked out: compliance, evidence, trust.\n  shield: {\n    d: \"M50 20 L74 28 V46 C74 62 64 73 50 80 C36 73 26 62 26 46 V28 Z M42 48.5 L47.5 54 L60 41 L64 44.5 L47.5 62 L38 52 Z\",\n    evenodd: true,\n  },\n  // Agent Services — a lightning bolt: automation, agents at work.\n  bolt: {\n    d: \"M55 20 L33 53 H47 L42 80 L67 45 H52 Z\",\n  },\n  // Email Hub — an envelope with the flap knocked out.\n  mail: {\n    d: \"M24 32 H76 V68 H24 Z M27 35 L50 53 L73 35 V41 L50 59 L27 41 Z\",\n    evenodd: true,\n  },\n  // Materials Exchange — two opposed transfer arrows: reuse in, materials out.\n  exchange: {\n    d: \"M24 34 H62 L54 26 L60 22 L74 38 L60 54 L54 50 L62 42 H24 Z M76 66 H38 L46 74 L40 78 L26 62 L40 46 L46 50 L38 58 H76 Z\",\n  },\n  // Manage — console sliders: the operations panel.\n  sliders: {\n    d: \"M24 33 H76 V37 H24 Z M24 48 H76 V52 H24 Z M24 63 H76 V67 H24 Z\",\n    circles: [\n      { cx: 41, cy: 35, r: 7.5 },\n      { cx: 60, cy: 50, r: 7.5 },\n      { cx: 33, cy: 65, r: 7.5 },\n    ],\n  },\n  // Geo — a map pin with the hole knocked out: place, routing, geography.\n  pin: {\n    d: \"M50 20 C36.5 20 27 30.5 27 43.5 C27 60.5 50 79 50 79 C50 79 73 60.5 73 43.5 C73 30.5 63.5 20 50 20 Z M50 36.5 A7 7 0 1 0 50 50.5 A7 7 0 1 0 50 36.5 Z\",\n    evenodd: true,\n  },\n}\n\n/**\n * Fixed brand hexes per tone. Product tones read the 600-level hex from each\n * brand variant (styles/themes/*.css) so the icon family matches the theme\n * system, but they are frozen here as literals — an icon must not change with\n * the active theme. `bg2` (when present) is the darker stop of a subtle vertical\n * gradient; `fg` is the emblem colour chosen for contrast on that tile.\n */\nconst TONES: Record<\n  NxAppIconTone,\n  { bg: string; bg2?: string; fg: string; ring?: string }\n> = {\n  // Nexus navy — brand-600 → brand-700 vertical gradient, yellow arrow.\n  navy: { bg: \"#121541\", bg2: \"#0E1034\", fg: \"#F7EC33\" },\n  // Accent yellow — flat, navy arrow.\n  yellow: { bg: \"#F7EC33\", fg: \"#121541\" },\n  // Paper — white tile with a hairline inset ring, navy arrow.\n  paper: { bg: \"#FFFFFF\", fg: \"#121541\", ring: \"#E4E7EC\" },\n  // Product tones keyed to the brand-variant 600 hexes; white arrow for contrast.\n  ultraviolet: { bg: \"#7F56D9\", bg2: \"#6941C6\", fg: \"#FFFFFF\" },\n  evergreen: { bg: \"#0E5F45\", bg2: \"#0A4A36\", fg: \"#FFFFFF\" },\n  ocean: { bg: \"#1570EF\", bg2: \"#175CD3\", fg: \"#FFFFFF\" },\n  ember: { bg: \"#E04F16\", bg2: \"#B93815\", fg: \"#FFFFFF\" },\n  graphite: { bg: \"#101828\", bg2: \"#0C111D\", fg: \"#FFFFFF\" },\n}\n\nfunction NxAppIcon({\n  tone = \"navy\",\n  shape = \"squircle\",\n  size = 64,\n  /** Corner radius as a fraction of size (rounded/squircle only). Default 24%. */\n  radius = 0.24,\n  /** The mark drawn on the tile — `emblem` (the Nexus arrow) by default, or a\n   * per-app identity mark. */\n  mark = \"emblem\",\n  /** Emblem weight — the outlined `bold` keyline is the house style; pass\n   * `solid` only where a tile renders too small for the keyline to read.\n   * Ignored for non-emblem marks (they are always solid fills). */\n  weight = \"bold\",\n  /** Optional short string (2–3 chars) or icon element rendered small, top-right. */\n  glyph,\n  /** Accessible name for the tile. When set, the SVG is labelled (not hidden). */\n  label,\n  className,\n  ...props\n}: Omit<React.ComponentProps<\"svg\">, \"children\"> & {\n  tone?: NxAppIconTone\n  shape?: NxAppIconShape\n  size?: number\n  radius?: number\n  mark?: NxAppIconMark\n  weight?: \"bold\" | \"solid\"\n  glyph?: React.ReactNode\n  label?: string\n}) {\n  const t = TONES[tone]\n  const gradientId = React.useId()\n  const clipId = React.useId()\n  const useGradient = Boolean(t.bg2)\n\n  // Tile geometry on a 100-unit canvas; the SVG scales to `size`.\n  const S = 100\n  const r =\n    shape === \"circle\" ? S / 2 : Math.max(0, Math.min(0.5, radius)) * S\n\n  // Squircle: a superellipse approximated with a rounded-rect whose corners use\n  // a larger radius and smoother join (visually closer to iOS/Google tiles).\n  const tilePath =\n    shape === \"squircle\"\n      ? squirclePath(S, Math.min(r * 1.15, S / 2))\n      : undefined\n\n  // Emblem placement. After the source group transform the arrow's artwork\n  // occupies an EXACT [96,416]×[96,416] square (a 320-unit box) inside the 512\n  // emblem canvas. We map that box into the tile: sized to ~55% of the tile,\n  // centred horizontally with a small down-left bias so the mark sits with the\n  // arrow's down-left direction and keeps optical margins.\n  const ART_ORIGIN = 96 // artwork top-left in the emblem's own transformed space\n  const ART_SIZE = 320 // artwork side length in that space\n  const emblemFrac = 0.55 // fraction of the tile the artwork fills\n  const emblemScale = (emblemFrac * S) / ART_SIZE\n  const rendered = emblemFrac * S\n  const centredTopLeft = (S - rendered) / 2\n  // Down-left bias (in tile units).\n  const biasX = -S * 0.015\n  const biasY = S * 0.03\n  const emblemX = centredTopLeft + biasX - ART_ORIGIN * emblemScale\n  const emblemY = centredTopLeft + biasY - ART_ORIGIN * emblemScale\n\n  return (\n    <svg\n      data-slot=\"nx-app-icon\"\n      data-tone={tone}\n      width={size}\n      height={size}\n      viewBox={`0 0 ${S} ${S}`}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      role={label ? \"img\" : undefined}\n      aria-hidden={label ? undefined : true}\n      aria-label={label}\n      className={cn(\"shrink-0\", className)}\n      {...props}\n    >\n      {label ? <title>{label}</title> : null}\n      {useGradient ? (\n        <defs>\n          <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n            <stop offset=\"0%\" stopColor={t.bg} />\n            <stop offset=\"100%\" stopColor={t.bg2} />\n          </linearGradient>\n        </defs>\n      ) : null}\n\n      {/* Tile background. */}\n      <defs>\n        {tilePath ? (\n          <clipPath id={clipId}>\n            <path d={tilePath} />\n          </clipPath>\n        ) : (\n          <clipPath id={clipId}>\n            {shape === \"circle\" ? (\n              <circle cx={S / 2} cy={S / 2} r={S / 2} />\n            ) : (\n              <rect width={S} height={S} rx={r} ry={r} />\n            )}\n          </clipPath>\n        )}\n      </defs>\n\n      {tilePath ? (\n        <path d={tilePath} fill={useGradient ? `url(#${gradientId})` : t.bg} />\n      ) : shape === \"circle\" ? (\n        <circle\n          cx={S / 2}\n          cy={S / 2}\n          r={S / 2}\n          fill={useGradient ? `url(#${gradientId})` : t.bg}\n        />\n      ) : (\n        <rect\n          width={S}\n          height={S}\n          rx={r}\n          ry={r}\n          fill={useGradient ? `url(#${gradientId})` : t.bg}\n        />\n      )}\n\n      {/* Paper tone: 1px inset hairline ring so a white tile has an edge. */}\n      {t.ring ? (\n        tilePath ? (\n          <path\n            d={tilePath}\n            fill=\"none\"\n            stroke={t.ring}\n            strokeWidth={1.5}\n            clipPath={`url(#${clipId})`}\n          />\n        ) : shape === \"circle\" ? (\n          <circle\n            cx={S / 2}\n            cy={S / 2}\n            r={S / 2 - 0.75}\n            fill=\"none\"\n            stroke={t.ring}\n            strokeWidth={1.5}\n          />\n        ) : (\n          <rect\n            x={0.75}\n            y={0.75}\n            width={S - 1.5}\n            height={S - 1.5}\n            rx={Math.max(0, r - 0.75)}\n            ry={Math.max(0, r - 0.75)}\n            fill=\"none\"\n            stroke={t.ring}\n            strokeWidth={1.5}\n          />\n        )\n      ) : null}\n\n      {/* The mark. Emblem: its artwork occupies the exact [96,416] square of\n          the 512 emblem canvas; the outer transform maps that box into the\n          tile. Identity marks are drawn directly in the tile canvas. */}\n      {mark === \"emblem\" ? (\n        <g transform={`translate(${emblemX} ${emblemY}) scale(${emblemScale})`}>\n          <g transform=\"translate(74.8895363356364 69.5353367185549) scale(2.6637809040206446)\">\n            {weight === \"solid\" ? (\n              <path d={ARROW_SOLID} fill={t.fg} />\n            ) : (\n              <path\n                d={`${ARROW_SOLID}${ARROW_INSET}`}\n                fill={t.fg}\n                fillRule=\"evenodd\"\n                clipRule=\"evenodd\"\n                stroke={t.fg}\n                strokeWidth={6.5}\n                strokeLinejoin=\"round\"\n              />\n            )}\n          </g>\n        </g>\n      ) : (\n        <g fill={t.fg}>\n          <path\n            d={MARK_PATHS[mark].d}\n            fillRule={MARK_PATHS[mark].evenodd ? \"evenodd\" : undefined}\n            clipRule={MARK_PATHS[mark].evenodd ? \"evenodd\" : undefined}\n          />\n          {MARK_PATHS[mark].circles?.map((c) => (\n            <circle key={`${c.cx}-${c.cy}`} cx={c.cx} cy={c.cy} r={c.r} />\n          ))}\n        </g>\n      )}\n\n      {/* Optional per-app glyph, small in the top-right at reduced opacity. */}\n      {glyph != null ? (\n        typeof glyph === \"string\" ? (\n          <text\n            x={S - 9}\n            y={17}\n            textAnchor=\"end\"\n            fontSize={13}\n            fontWeight={700}\n            fill={t.fg}\n            opacity={0.55}\n            fontFamily=\"Inter, system-ui, sans-serif\"\n          >\n            {glyph}\n          </text>\n        ) : (\n          <g\n            transform={`translate(${S - 26} 6)`}\n            opacity={0.55}\n            color={t.fg}\n          >\n            {glyph}\n          </g>\n        )\n      ) : null}\n    </svg>\n  )\n}\n\n/**\n * A smooth superellipse-ish tile path (the iOS/Google \"squircle\" corner). Built\n * as a rounded rect whose corners use cubic curves with a continuity factor so\n * the join is smoother than a plain quarter-circle. `n` is the corner radius.\n */\nfunction squirclePath(size: number, n: number): string {\n  const k = 0.55 // corner smoothing (0 = circular, higher = squarer/smoother join)\n  const c = n * (1 - k) // control-point inset\n  const s = size\n  return [\n    `M ${n} 0`,\n    `L ${s - n} 0`,\n    `C ${s - c} 0 ${s} ${c} ${s} ${n}`,\n    `L ${s} ${s - n}`,\n    `C ${s} ${s - c} ${s - c} ${s} ${s - n} ${s}`,\n    `L ${n} ${s}`,\n    `C ${c} ${s} 0 ${s - c} 0 ${s - n}`,\n    `L 0 ${n}`,\n    `C 0 ${c} ${c} 0 ${n} 0`,\n    \"Z\",\n  ].join(\" \")\n}\n\nexport { NxAppIcon }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}