{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stack-letters",
  "title": "Stack Letters",
  "description": "Letters tumble and stack into place to spell a caps word over a warm gradient.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/stack-letters/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/SpaceGrotesk\";\nimport { useId, useMemo } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst { fontFamily: sans } = loadFont(\"normal\", {\n  weights: [\"500\"],\n  subsets: [\"latin\"],\n});\n\nexport interface StackLettersProps {\n  word?: string;\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  speed?: number;\n}\n\n// Space Grotesk 500 advance widths (1/1000 em) for ASCII 32–126.\nconst SPACE_500 = [\n  255, 276, 470, 632, 609, 765, 598, 273, 389, 383, 530, 620, 273, 443, 276,\n  379, 645, 435, 598, 606, 630, 599, 615, 559, 611, 615, 276, 276, 620, 620,\n  620, 579, 1020, 632, 663, 643, 665, 559, 537, 661, 656, 254, 602, 618, 544,\n  874, 667, 673, 603, 673, 632, 609, 588, 669, 619, 887, 641, 617, 577, 347,\n  379, 347, 620, 620, 288, 577, 641, 592, 641, 584, 439, 640, 615, 256, 258,\n  551, 256, 857, 615, 615, 641, 641, 387, 525, 458, 614, 544, 792, 593, 614,\n  519, 449, 249, 449, 620,\n];\n// Ink boxes for \"!\"..\"~\" at weight 500: xMin, yMin, xMax, yMax in 1/1000 em.\nconst INK_500 = [\n  \"56,-14,220,700 56,430,414,714 41,0,591,700 41,-116,569,816 30,-13,735,713\",\n  \"36,0,578,700 56,430,217,714 55,-240,380,750 3,-240,328,750 45,261,485,700\",\n  \"101,141,519,559 56,-136,217,148 74,209,370,303 56,-14,220,149 -8,-200,389,700\",\n  \"55,-14,590,714 15,0,362,700 48,0,555,714 40,-14,561,700 29,0,606,700\",\n  \"44,-14,559,700 55,-14,570,714 29,0,531,700 37,-14,574,714 46,-14,560,714\",\n  \"56,-14,220,506 56,-136,220,506 100,130,520,570 101,222,519,478 100,130,520,570\",\n  \"38,-14,535,714 72,-200,952,709 21,0,610,700 47,0,615,700 55,-14,596,714\",\n  \"47,0,616,700 73,0,519,700 73,0,505,700 55,-14,595,714 73,0,583,700\",\n  \"73,0,181,700 36,-14,559,700 73,0,610,700 73,0,518,700 73,0,801,700\",\n  \"73,0,594,700 55,-14,618,714 73,0,573,700 55,-171,618,714 73,0,586,700\",\n  \"41,-14,569,714 25,0,563,700 68,-14,601,700 21,0,597,700 33,0,853,700\",\n  \"27,0,614,700 11,0,606,700 48,0,531,700 76,-240,329,750 -10,-200,388,700\",\n  \"17,-240,271,750 72,424,548,700 71,-142,549,-49 47,558,241,700 44,-14,553,507\",\n  \"76,-14,589,700 52,-14,549,507 52,-14,565,700 52,-14,536,507 27,0,410,700\",\n  \"52,-200,564,507 76,0,543,700 57,0,199,700 -18,-200,201,700 76,0,534,700\",\n  \"76,0,179,700 76,0,785,501 76,0,543,501 52,-14,563,507 76,-200,589,507\",\n  \"52,-200,565,507 76,0,356,495 46,-14,481,507 27,0,413,656 71,-9,538,493\",\n  \"24,0,520,493 46,0,746,493 34,0,558,493 71,-200,538,493 56,0,467,493\",\n  \"37,-240,432,750 73,-246,176,806 17,-240,412,750 63,253,556,447\",\n]\n  .join(\" \")\n  .split(\" \")\n  .map((box) => box.split(\",\").map((v) => Number(v) / 1000));\n// Kerning pairs (1/1000 em to tighten; negative values loosen).\nconst KERN_PAIRS = [\n  \"AT70 AU12 AV49 AW22 AY74 Af23 At23 Av14 BT13 BV12 BX14 BY15 CT15 CY17\",\n  \"DB23 DD23 DT29 DV15 DX24 DY28 DZ14 D.20 D,20 FA44 F.75 F,75 GT29 GV18\",\n  \"GY33 JA35 JC16 JG16 JJ31 JO16 JQ16 Ja30 Jc32 Jd32 Je32 Jg32 Jm30 Jn30\",\n  \"Jo32 Jp30 Jq32 Jr30 Ju30 Jy30 J.60 J,60 KC30 KG30 KO30 KQ30 Kc17 Kd17\",\n  \"Ke17 Kf27 Kg17 Ko17 Kq17 Ks10 Kt27 Ku11 Kv28 Kw16 Ky11 LC22 LG22 LO22\",\n  \"LQ22 LT93 LU18 LV74 LW25 LY118 Lf46 Lt46 Lv33 Lw11 OB23 OD23 OT29\",\n  \"OV15 OX24 OY28 OZ14 O.20 O,20 PA55 PJ48 PX21 PZ19 Pa11 Pc14 Pd14 Pe14\",\n  \"Pg14 Po14 Pq14 Ps12 P.104 P,104 QB23 QD23 QT29 QV15 QX24 QY28 QZ14\",\n  \"Q.20 Q,20 RT11 RV12 RY16 ST26 SV11 SX10 SY21 TA70 TC29 TG29 TJ87 TO29\",\n  \"TQ29 TS15 Ta87 Tc97 Td97 Te97 Tf52 Tg97 Ti11 Tj11 Tm72 Tn72 To97 Tp72\",\n  \"Tq97 Tr72 Ts82 Tt52 Tu74 Tv69 Tw72 Tx80 Ty74 Tz76 T.96 T,96 UA12 U.15\",\n  \"U,15 VA49 VC15 VG15 VO15 VQ15 Va33 Vc33 Vd33 Ve33 Vg33 Vo33 Vq33 Vs30\",\n  \"Vu11 Vv15 Vw13 Vx26 Vy11 V.72 V,72 WA22 Wc10 Wd10 We10 Wg10 Wo10 Wq10\",\n  \"W.40 W,40 XC24 XG24 XO24 XQ24 Xc17 Xd17 Xe17 Xf20 Xg17 Xo17 Xq17 Xt20\",\n  \"Xu10 Xv20 Xw11 Xy10 YA74 YC28 YG28 YJ95 YO28 YQ28 YS19 Ya83 Yc87 Yd87\",\n  \"Ye87 Yf46 Yg87 Ym73 Yn73 Yo87 Yp73 Yq87 Yr73 Ys80 Yt46 Yu75 Yv63 Yw65\",\n  \"Yx71 Yy75 Yz72 Y.105 Y,105 ZC14 ZG14 ZO14 ZQ14 Zc11 Zd11 Ze11 Zf16\",\n  \"Zg11 Zo11 Zq11 Zt16 Zv11 ac12 ad12 ae12 af34 ag12 ao12 aq12 at34 au13\",\n  \"av27 ay13 bf18 bt18 bv17 bx32 bz13 cf18 ct18 cv17 cx32 cz13 ef18 et18\",\n  \"ev17 ex32 ez13 fc20 fd20 fe20 fg20 fi26 fj26 fl26 fo20 fq20\",\n  \"ft13 f.63 f,63 hf14 ht14 hv15 ka11 kc28 kd28 ke28 kg28 ko28 kq28 ks13\",\n  \"mf14 mt14 mv15 nf14 nt14 nv15 of18 ot18 ov17 ox32 oz13 pf18 pt18 pv17\",\n  \"px32 pz13 ra14 rc18 rd18 re18 rg18 ro18 rq18 rs11 r.72 r,72 sf11 st11\",\n  \"sv19 tc18 td18 te18 tf27 tg18 to18 tq18 va14 vc17 vd17 ve17\",\n  \"vg17 vo17 vq17 vs13 v.41 v,41 w.26 w,26 xa10 xc32 xd32 xe32 xg32 xo32\",\n  \"xq32 xs11 zc14 zd14 ze14 zg14 zo14 zq14 .C20 .G20 .O20 .Q20 .T96 .U15\",\n  \".V72 .W40 .Y105 .f59 .t59 .v41 .w26 ,C20 ,G20 ,O20 ,Q20 ,T96 ,U15\",\n  \",V72 ,W40 ,Y105 ,f59 ,j-14 ,t59 ,v41 ,w26\",\n].join(\" \");\nconst KERN = new Map(\n  KERN_PAIRS.split(\" \").map((p) => [p.slice(0, 2), -Number(p.slice(2)) / 1000]),\n);\nconst TRACK = -0.02;\n// Space Grotesk typo metrics (USE_TYPO_METRICS): ascender 984, descender 292.\nconst ASC = 0.984;\nconst LINE = 1.276;\nconst STACK_GAP = 0.16;\n\nconst DROP_GAP = 8;\nconst FALL = 12;\nconst ROCK = 10;\nconst ROCK_DEG = 3.5;\nconst TOPPLE = 22;\nconst DETACH_DEG = 20;\nconst LAND_GAP = 4;\nconst LIFT_STEP = 0.26;\nconst FRAME_H = 0.78;\nconst FRAME_W = 0.86;\nconst CAM_SMOOTH = 6;\n\nconst EASE_OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst EASE_IO = Easing.bezier(0.65, 0, 0.35, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\n// gradients.sh palettes: \"grain\" (#ffd8b4, #e0703b, #8a2c1d, #2b1015 on\n// #070505) for dark, \"smear\" (#f2552c, #f7b23b, #e889b5 on #efe8dc) for light.\nconst THEMES = {\n  dark: {\n    bg: \"#070505\",\n    fg: \"#FFF3E8\",\n    rule: \"rgba(255,216,180,0.22)\",\n    ground: (x: number, y: number) =>\n      [\n        `radial-gradient(38% 34% at ${x}% ${y}%, rgba(255,216,180,0.4) 0%, transparent 62%)`,\n        `linear-gradient(${150 - x * 0.2}deg, #E0703B 0%, rgba(138,44,29,0.92) 22%, rgba(43,16,21,0.9) 46%, rgba(7,5,5,0) 74%)`,\n      ].join(\", \"),\n    grain: 0.08,\n  },\n  light: {\n    bg: \"#EFE8DC\",\n    fg: \"#1A1412\",\n    rule: \"rgba(26,20,18,0.16)\",\n    ground: (x: number, y: number) =>\n      [\n        `radial-gradient(38% 34% at ${x}% ${y}%, rgba(247,178,59,0.55) 0%, transparent 62%)`,\n        `linear-gradient(${150 - x * 0.2}deg, rgba(242,85,44,0.78) 0%, rgba(232,137,181,0.55) 24%, rgba(239,232,220,0) 52%)`,\n      ].join(\", \"),\n    grain: 0.05,\n  },\n} as const;\n\ntype Pt = { x: number; y: number };\n\nfunction advance(ch: string) {\n  const code = ch.charCodeAt(0);\n  return (code >= 32 && code < 127 ? SPACE_500[code - 32] : 620) / 1000;\n}\n\nfunction ink(ch: string) {\n  const code = ch.charCodeAt(0);\n  const box = code > 32 && code < 127 ? INK_500[code - 33] : null;\n  if (!box) return { l: 0.05, r: advance(ch) - 0.05, top: 0.546, desc: 0 };\n  return {\n    l: box[0],\n    r: box[2],\n    top: box[3],\n    desc: box[1] < -0.05 ? -box[1] : 0,\n  };\n}\n\n/** CSS rotate(deg) is clockwise in screen space; mirror that here. */\nfunction rotate(p: Pt, about: Pt, deg: number): Pt {\n  const r = (deg * Math.PI) / 180;\n  const dx = p.x - about.x;\n  const dy = p.y - about.y;\n  return {\n    x: about.x + dx * Math.cos(r) - dy * Math.sin(r),\n    y: about.y + dx * Math.sin(r) + dy * Math.cos(r),\n  };\n}\n\nfunction bezier(p0: Pt, p1: Pt, p2: Pt, p3: Pt, u: number): Pt {\n  const v = 1 - u;\n  const a = v * v * v;\n  const b = 3 * v * v * u;\n  const c = 3 * v * u * u;\n  const d = u * u * u;\n  return {\n    x: a * p0.x + b * p1.x + c * p2.x + d * p3.x,\n    y: a * p0.y + b * p1.y + c * p2.y + d * p3.y,\n  };\n}\n\n/** Landing squash: a one-frame hit, then a damped return with a hair of overshoot. */\nfunction squash(d: number) {\n  if (d < 0 || d > 14) return 0;\n  if (d < 1) return d;\n  const k = d - 1;\n  return Math.exp(-k / 2.2) * Math.cos(k * 0.66);\n}\n\nfunction Grain({ opacity }: { opacity: number }) {\n  const id = `g${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  return (\n    <svg\n      width=\"100%\"\n      height=\"100%\"\n      style={{ position: \"absolute\", inset: 0, opacity, pointerEvents: \"none\" }}\n      aria-hidden\n    >\n      <filter id={id}>\n        <feTurbulence\n          type=\"fractalNoise\"\n          baseFrequency=\"0.9\"\n          numOctaves={2}\n          seed={53}\n          stitchTiles=\"stitch\"\n        />\n        <feColorMatrix values=\"0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0.9 0\" />\n      </filter>\n      <rect width=\"100%\" height=\"100%\" filter={`url(#${id})`} />\n    </svg>\n  );\n}\n\nexport function StackLetters({\n  word = \"STACK\",\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  speed = 1,\n}: StackLettersProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const c = THEMES[theme];\n\n  const chars = useMemo(() => Array.from(word), [word]);\n  const n = chars.length;\n  const boxes = useMemo(() => chars.map(ink), [chars]);\n\n  const em = useMemo(() => {\n    const xs: number[] = [];\n    let x = 0;\n    chars.forEach((ch, i) => {\n      if (i > 0) x += KERN.get(chars[i - 1] + ch) ?? 0;\n      xs.push(x);\n      x += advance(ch) + TRACK;\n    });\n    const total = x - TRACK;\n    return { xs: xs.map((v) => v - total / 2), total };\n  }, [chars]);\n\n  const F = Math.min(H * 0.4, (W * 0.6) / Math.max(em.total, 0.1));\n\n  const geo = useMemo(() => {\n    const centers = boxes.map((b, i) => (em.xs[i] + (b.l + b.r) / 2) * F);\n    const half = boxes.map((b) => ((b.r - b.l) / 2) * F);\n    const bases: number[] = [];\n    boxes.forEach((b, i) => {\n      if (i === 0) bases.push(0);\n      else\n        bases.push(\n          bases[i - 1] - boxes[i - 1].top * F - STACK_GAP * F - b.desc * F,\n        );\n    });\n    const towerH = -(bases[n - 1] - boxes[n - 1].top * F);\n    const axis = centers[0];\n    return {\n      centers,\n      half,\n      bases,\n      towerH,\n      axis,\n      pivotL: { x: axis - half[0], y: 0 },\n      pivotR: { x: axis + half[0], y: 0 },\n    };\n  }, [boxes, em, F, n]);\n\n  const drop = (k: number) => k * DROP_GAP - 6;\n  const lastStack = drop(n - 1) + FALL;\n  const rockAt = lastStack + 9;\n  const toppleAt = rockAt + ROCK;\n  const toppleDeg = (time: number) => {\n    const u = (time - toppleAt) / TOPPLE;\n    return 95 * u * u;\n  };\n  // Every letter lets go on the same frame and carries the tower's velocity,\n  // so the stack keeps its spacing and only spreads from there.\n  const release = toppleAt + TOPPLE * Math.sqrt(DETACH_DEG / 95);\n  const landAt = (k: number) => release + 7 + k * LAND_GAP;\n\n  const towerPose = (deg: number, pivot: Pt, k: number) => {\n    const p = rotate({ x: geo.axis, y: geo.bases[k] }, pivot, deg);\n    return { ...p, rot: deg };\n  };\n\n  /** Rigid-body pose of letter k once the stack is complete (no squash). */\n  const flight = (k: number, time: number) => {\n    if (time < toppleAt) {\n      if (time < rockAt) return { x: geo.axis, y: geo.bases[k], rot: 0 };\n      const u = (time - rockAt) / ROCK;\n      const deg = -ROCK_DEG * Math.sin(Math.PI * EASE_IO(u));\n      return towerPose(deg, geo.pivotL, k);\n    }\n    if (time < release) return towerPose(toppleDeg(time), geo.pivotR, k);\n    const land = landAt(k);\n    const end = { x: geo.centers[k], y: 0 };\n    if (time >= land) return { ...end, rot: 0 };\n    const u = (time - release) / (land - release);\n    const deg0 = toppleDeg(release);\n    if (k === 0) return towerPose(deg0 * (1 - u * u), geo.pivotR, 0);\n    const omega =\n      ((2 * 95 * (release - toppleAt)) / (TOPPLE * TOPPLE)) * (Math.PI / 180);\n    const start = towerPose(deg0, geo.pivotR, k);\n    const r = { x: start.x - geo.pivotR.x, y: start.y - geo.pivotR.y };\n    const lead = (land - release) / 3;\n    // Low letters get a shorter approach so they never hop up into the next.\n    const reach = Math.min(1, Math.max(0.3, (end.y - start.y) / (F * 1.4)));\n    const p = bezier(\n      start,\n      { x: start.x - r.y * omega * lead, y: start.y + r.x * omega * lead },\n      // Come in from the upper right, parallel to a left-leaning stroke, so a\n      // kerned pair (T over A) slides together instead of interlocking.\n      { x: end.x + F * 0.4 * reach, y: end.y - F * 0.85 * reach },\n      end,\n      u,\n    );\n    // Higher letters ride a higher arc, so neighbours keep clear air.\n    const lift = Math.sin(Math.PI * u) ** 2 * k * LIFT_STEP * F;\n    // Hold the tilt briefly: righting at once would swing the top into the\n    // letter above.\n    const turn = Math.min(1, Math.max(0, (u - 0.2) / 0.45));\n    return {\n      ...p,\n      y: p.y - lift,\n      rot: deg0 * (1 - turn * turn * (3 - 2 * turn)),\n    };\n  };\n\n  // Camera: frame whatever the letters occupy, smoothed over a short window.\n  const fit = (time: number) => {\n    let x0 = Number.POSITIVE_INFINITY;\n    let x1 = Number.NEGATIVE_INFINITY;\n    let y0 = Number.POSITIVE_INFINITY;\n    let y1 = Number.NEGATIVE_INFINITY;\n    boxes.forEach((box, k) => {\n      const p = flight(k, time);\n      const hw = geo.half[k];\n      for (const [lx, ly] of [\n        [-hw, -box.top * F],\n        [hw, -box.top * F],\n        [-hw, box.desc * F],\n        [hw, box.desc * F],\n      ]) {\n        const q = rotate({ x: p.x + lx, y: p.y + ly }, p, p.rot);\n        x0 = Math.min(x0, q.x);\n        x1 = Math.max(x1, q.x);\n        y0 = Math.min(y0, q.y);\n        y1 = Math.max(y1, q.y);\n      }\n    });\n    return {\n      x: (x0 + x1) / 2,\n      y: (y0 + y1) / 2,\n      s: Math.min(1, (H * FRAME_H) / (y1 - y0), (W * FRAME_W) / (x1 - x0)),\n    };\n  };\n  const cam0 = fit(toppleAt);\n  let camX = 0;\n  let camY = 0;\n  let scale = 0;\n  for (let d = -CAM_SMOOTH; d <= CAM_SMOOTH; d++) {\n    const f = fit(Math.max(toppleAt, t + d));\n    camX += f.x;\n    camY += f.y;\n    scale += f.s;\n  }\n  const samples = CAM_SMOOTH * 2 + 1;\n  camX /= samples;\n  camY /= samples;\n  scale /= samples;\n  const viewTop = cam0.y - H / 2 / cam0.s;\n\n  const pose = (k: number) => {\n    const box = boxes[k];\n    const d = drop(k);\n    const settled = d + FALL;\n    if (t < settled) {\n      const u = Math.max(0, (t - d) / FALL);\n      const y0 = viewTop - box.desc * F - 4;\n      return {\n        x: geo.axis,\n        y: y0 + (geo.bases[k] - y0) * u * u,\n        rot: 0,\n        hit: 0,\n        visible: t >= d,\n      };\n    }\n    const p = flight(k, t);\n    const hit = t < rockAt ? squash(t - settled) : squash(t - landAt(k));\n    return { ...p, hit, visible: true };\n  };\n\n  const ruleH = Math.max(2, F * 0.012);\n  // The light source drifts a few percent over the whole shot.\n  const drift = interpolate(t, [0, landAt(n - 1) + 30], [0, 1], {\n    ...CLAMP,\n    easing: EASE_IO,\n  });\n  const ruleIn = interpolate(t, [0, 14], [0, 1], {\n    ...CLAMP,\n    easing: EASE_OUT,\n  });\n\n  return (\n    <AbsoluteFill style={{ background: c.bg, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          background: c.ground(10 + 6 * drift, 8 + 4 * drift),\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          top: 0,\n          transformOrigin: \"0 0\",\n          transform: `translate(${W / 2}px, ${H / 2}px) scale(${scale}) translate(${-camX}px, ${-camY}px)`,\n          fontFamily: sans,\n          fontWeight: 500,\n          fontSize: F,\n          fontKerning: \"none\",\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: -W * 3,\n            top: F * 0.014,\n            width: W * 6,\n            height: ruleH / scale,\n            background: c.rule,\n            opacity: ruleIn,\n          }}\n        />\n        {chars.map((_, k) => {\n          const land = landAt(k);\n          const flash = interpolate(t - land, [0, 1, 16], [0, 1, 0], {\n            ...CLAMP,\n            easing: EASE_OUT,\n          });\n          const grow = interpolate(t - land, [0, 8], [0.4, 1], {\n            ...CLAMP,\n            easing: EASE_OUT,\n          });\n          if (flash <= 0.001) return null;\n          return (\n            <div\n              key={`tick-${k}`}\n              style={{\n                position: \"absolute\",\n                left: geo.centers[k] - geo.half[k],\n                top: F * 0.014,\n                width: geo.half[k] * 2,\n                height: (ruleH * 2) / scale,\n                background: accent,\n                opacity: flash,\n                transform: `scaleX(${grow})`,\n              }}\n            />\n          );\n        })}\n        {chars.map((ch, k) => {\n          const p = pose(k);\n          if (!p.visible) return null;\n          const box = boxes[k];\n          const ox = (box.l + box.r) / 2;\n          const sy = 1 - 0.09 * p.hit;\n          const sx = 1 + 0.06 * p.hit;\n          return (\n            <div\n              key={`${k}-${ch}`}\n              style={{\n                position: \"absolute\",\n                left: 0,\n                top: 0,\n                color: c.fg,\n                transformOrigin: \"0 0\",\n                transform: `translate(${p.x}px, ${p.y}px) rotate(${p.rot}deg) scale(${sx}, ${sy})`,\n              }}\n            >\n              <span\n                style={{\n                  position: \"absolute\",\n                  left: -ox * F,\n                  top: -ASC * F,\n                  lineHeight: LINE,\n                  whiteSpace: \"pre\",\n                }}\n              >\n                {ch}\n              </span>\n            </div>\n          );\n        })}\n      </div>\n      <Grain opacity={c.grain} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/stack-letters.tsx"
    }
  ],
  "type": "registry:component"
}
