{
  "name": "modular-block-default",
  "type": "registry:block",
  "tier": "free",
  "dependencies": [],
  "registryDependencies": [
    "https://www.craftui.space/r/craftui-base.json"
  ],
  "files": [
    {
      "path": "components/craftui/illustrations/modular-block/default.tsx",
      "content": "// A cube with one corner block cut out and set down beside it. Registry items\n// ship as one self-contained file to a consumer's project, so the isometric\n// helpers below live here rather than in a shared module — a shared module\n// would arrive as a second file the consumer never asked for.\n//\n// No motion: the diagram is drawn once and holds still.\n\ninterface ModularBlockProps {\n  /** Additional classes on the <svg> root. */\n  className?: string;\n}\n\n// ─── Isometric projection ─────────────────────────────────────────────────────\n//\n// A true 2:1 isometric would use k = 0.5; 0.866 (cos 30°) gives the taller,\n// squarer look shared with the other isometric illustrations. ISO_S is the unit\n// size in px, ISO_OX/OY the origin — tuned so the cube plus the detached block\n// sit centred in the 420×450 viewBox.\n\nconst ISO_K = 0.8660254;\nconst ISO_S = 18;\nconst ISO_OX = 190;\nconst ISO_OY = 225;\n\ntype Pt = readonly [number, number];\n\nconst p = (x: number, y: number, z = 0): Pt => [\n  (x - y) * ISO_K * ISO_S + ISO_OX,\n  ((x + y) / 2 - z) * ISO_S + ISO_OY,\n];\n\n/** Sharp corners on purpose — every vertex is a hard miter, no bevel. */\nconst poly = (pts: Pt[]) =>\n  pts\n    .map(([x, y], i) => `${i ? \"L\" : \"M\"}${x.toFixed(2)} ${y.toFixed(2)}`)\n    .join(\" \") + \"Z\";\n\n// ─── The solid ────────────────────────────────────────────────────────────────\n//\n// C is the cube's edge, N where the notch starts on all three axes, so the\n// removed piece is the cube x,y,z ∈ [N, C] — the corner that projects nearest\n// the viewer. OFF slides that piece out along +x and −y by equal amounts, which\n// on screen is pure horizontal travel: (x − y) grows while (x + y) / 2 holds, so\n// the block moves right without drifting up or down out of its own socket.\n\nconst C = 10;\nconst N = 5.5;\nconst OFF = 4;\n\n/** The three faces of a box that are visible from this camera. */\nconst boxFaces = (\n  x: number,\n  y: number,\n  z: number,\n  w: number,\n  d: number,\n  h: number,\n) => ({\n  top: [\n    p(x, y, z + h),\n    p(x + w, y, z + h),\n    p(x + w, y + d, z + h),\n    p(x, y + d, z + h),\n  ] as Pt[],\n  front: [\n    p(x, y + d, z),\n    p(x + w, y + d, z),\n    p(x + w, y + d, z + h),\n    p(x, y + d, z + h),\n  ] as Pt[],\n  flank: [\n    p(x + w, y, z),\n    p(x + w, y + d, z),\n    p(x + w, y + d, z + h),\n    p(x + w, y, z + h),\n  ] as Pt[],\n});\n\n// ─── Ink ──────────────────────────────────────────────────────────────────────\n//\n// Neutral greys, no hue at all. Light keeps the simple three-step ramp — one\n// value per face orientation, lightest on the up-facing planes — so the shading\n// does the work a light source would and the solid never needs a gradient.\n//\n// Dark does NOT reuse that ramp. It is lifted value-for-value from the Paper\n// file (\"SVG Frame 01\"), where the three groups were graded apart rather than\n// sharing one palette: the extracted piece and the socket it came out of keep a\n// bright #3A3A3A up-face, while the host cube's top is knocked back to #282828.\n// That gap is the whole idea — the eye lands on the piece and the hole first,\n// and the big form recedes. The two inner walls go darker still, and the left\n// one darkest of all, which is what gives the socket its depth.\n//\n// In Paper these came out of a base fill plus a black overlay on the outline\n// path; the overlays only ever darken, so each pair is folded into one flat\n// value here. Same pixels, half the paths, no alpha stacking to reason about.\n//\n// Every colour is a class pair rather than a fill/stroke attribute, because an\n// attribute cannot answer to the theme.\n//\n// Classes rather than cssVars on purpose: cssVars are written into a consumer's\n// stylesheet at install time, so they do nothing for the copy rendering from\n// source in this repo's own gallery. This travels with the file. CSS beats a\n// presentation attribute, so the fill=\"none\" left on the <svg> root does not\n// fight them.\n\nconst INK = \"stroke-[#1A1A1A] dark:stroke-[#A8A8A8]\";\n\n/** The one warm edge in the drawing — the host cube's lower-left silhouette. */\nconst INK_WARM = \"stroke-[#1A1A1A] dark:stroke-[#D7CECD]\";\n\nconst SURFACE = {\n  cubeTop: \"fill-[#FFFFFF] dark:fill-[#282828]\",\n  cubeFront: \"fill-[#EFEFEF] dark:fill-[#131313]\",\n  cubeFlank: \"fill-[#DADADA] dark:fill-[#0D0D0D]\",\n\n  notchFloor: \"fill-[#FFFFFF] dark:fill-[#3A3A3A]\",\n  notchFront: \"fill-[#EFEFEF] dark:fill-[#131313]\",\n  notchFlank: \"fill-[#DADADA] dark:fill-[#060606]\",\n\n  pieceTop: \"fill-[#FFFFFF] dark:fill-[#3A3A3A]\",\n  pieceFront: \"fill-[#EFEFEF] dark:fill-[#131313]\",\n  pieceFlank: \"fill-[#DADADA] dark:fill-[#141414]\",\n} as const;\n\nconst LINE = {\n  fill: \"none\",\n  strokeLinejoin: \"miter\",\n  strokeLinecap: \"square\",\n} as const;\n\nexport function ModularBlock({ className }: ModularBlockProps) {\n  // Outer skin. Each of the three visible faces loses the same square corner to\n  // the notch, so all three are L-shaped rather than plain quads.\n  const topFace: Pt[] = [\n    p(0, 0, C),\n    p(C, 0, C),\n    p(C, N, C),\n    p(N, N, C),\n    p(N, C, C),\n    p(0, C, C),\n  ];\n  const frontFace: Pt[] = [\n    p(0, C, 0),\n    p(C, C, 0),\n    p(C, C, N),\n    p(N, C, N),\n    p(N, C, C),\n    p(0, C, C),\n  ];\n  const flankFace: Pt[] = [\n    p(C, 0, 0),\n    p(C, C, 0),\n    p(C, C, N),\n    p(C, N, N),\n    p(C, N, C),\n    p(C, 0, C),\n  ];\n\n  // Cut surfaces inside the notch: a floor and the two walls that face the\n  // camera. The third wall of the socket points away and is never seen.\n  const notchFloor: Pt[] = [p(N, N, N), p(C, N, N), p(C, C, N), p(N, C, N)];\n  const notchFlank: Pt[] = [p(N, N, N), p(N, C, N), p(N, C, C), p(N, N, C)];\n  const notchFront: Pt[] = [p(N, N, N), p(C, N, N), p(C, N, C), p(N, N, C)];\n\n  // The removed piece, slid out of its socket along the screen horizontal.\n  const size = C - N;\n  const piece = boxFaces(N + OFF, N - OFF, N, size, size, size);\n\n  return (\n    // viewBox-driven with no width/height attributes, so the diagram fills\n    // whatever box its container reserves and scales down instead of\n    // overflowing.\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 420 450\"\n      className={className}\n      fill=\"none\"\n      role=\"img\"\n      aria-label=\"Isometric diagram: a cube with one corner block cut out and set beside it\"\n    >\n      {/* Painter's algorithm. The recessed cut surfaces are farthest from the\n          camera, so they go down first; the outer skin overdraws them where the\n          two meet; the detached piece is nearest and lands last. Fills for a\n          group precede its outlines, because the outlines have to survive the\n          next fill that touches them. */}\n      <g>\n        <path d={poly(notchFlank)} className={SURFACE.notchFlank} />\n        <path d={poly(notchFront)} className={SURFACE.notchFront} />\n        <path d={poly(notchFloor)} className={SURFACE.notchFloor} />\n        <g {...LINE} className={INK} strokeWidth={1.3}>\n          <path d={poly(notchFlank)} />\n          <path d={poly(notchFront)} />\n          <path d={poly(notchFloor)} />\n        </g>\n      </g>\n\n      <g>\n        <path d={poly(flankFace)} className={SURFACE.cubeFlank} />\n        <path d={poly(frontFace)} className={SURFACE.cubeFront} />\n        <path d={poly(topFace)} className={SURFACE.cubeTop} />\n        <g {...LINE} strokeWidth={1.3}>\n          <path d={poly(flankFace)} className={INK} />\n          <path d={poly(frontFace)} className={INK_WARM} />\n          <path d={poly(topFace)} className={INK} />\n        </g>\n      </g>\n\n      <g>\n        <path d={poly(piece.flank)} className={SURFACE.pieceFlank} />\n        <path d={poly(piece.front)} className={SURFACE.pieceFront} />\n        <path d={poly(piece.top)} className={SURFACE.pieceTop} />\n        <g {...LINE} className={INK} strokeWidth={1.3}>\n          <path d={poly(piece.flank)} />\n          <path d={poly(piece.front)} />\n          <path d={poly(piece.top)} />\n        </g>\n      </g>\n    </svg>\n  );\n}\n",
      "type": "registry:component",
      "target": "~/components/craftui/illustrations/modular-block/default.tsx"
    }
  ],
  "meta": {
    "displayName": "Modular Block - Modular Block",
    "description": "An isometric cube with one corner block cut out and set down beside it — sharp corners, flat shading, nothing else on the page.",
    "category": "abstract",
    "tags": [
      "illustration",
      "isometric",
      "cube",
      "modular",
      "abstract",
      "diagram",
      "svg",
      "static"
    ],
    "animationType": "none"
  }
}