{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "card-create",
  "title": "Card Create",
  "description": "A phone app issues a yellow debit card and flips through color options.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "registryDependencies": ["@remocn/gsh"],
  "files": [
    {
      "path": "registry/remocn/card-create/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/HankenGrotesk\";\nimport type React from \"react\";\nimport { useId } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport type { GradientParams } from \"@/lib/remocn/gsh/gradient-engine\";\nimport { GshGround } from \"@/lib/remocn/gsh/gsh-ground\";\nimport type { GshPresetId } from \"@/lib/remocn/gsh/presets\";\n\nconst { fontFamily } = loadFont(\"normal\", {\n  weights: [\"300\", \"400\"],\n  subsets: [\"latin\"],\n});\n\nexport interface CardCreateProps {\n  /** Engraved on the new card. */\n  holder?: string;\n  last4?: string;\n  /** Swatches; the card prints in colors[0] and gets tapped through colors[1..3]. */\n  colors?: string[];\n  finishes?: string[];\n  accent?: string;\n  speed?: number;\n  theme?: \"light\" | \"dark\";\n  /** Five lines beside the phone, one per beat. */\n  lines?: [string, string, string, string, string];\n  ground?: GshPresetId;\n  groundParams?: Partial<GradientParams>;\n}\n\nconst SW = 390;\nconst SH = 844;\nconst BEZEL = 12;\nconst BW = SW + BEZEL * 2;\nconst BH = SH + BEZEL * 2;\n\nconst CW = 330;\nconst CHT = Math.round(CW / 1.586);\nconst CX = (SW - CW) / 2;\nconst SLOT_Y = 170;\nconst CARD_Y = 188;\nconst SWATCH_Y = 424;\nconst TOOLS_Y = 484;\nconst TAP_NEW = 62;\nconst OUT = 64;\nconst SLOT = 72;\nconst PRINT = 84;\nconst SWATCH_TAPS = [136, 150, 164];\nconst NAME_TAB = 182;\nconst ENGRAVE = 188;\nconst DETAILS_TAB = 222;\nconst FLIP = 226;\nconst REVEAL_TAP = 256;\nconst SCRAMBLE = 258;\nconst ADD_TAP = 302;\nconst WALLET = 306;\n\nconst clamp = { extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\" } as const;\nconst MOVE = Easing.bezier(0.65, 0, 0.35, 1);\nconst ENTER = Easing.bezier(0.32, 0.72, 0, 1);\nconst EXIT = Easing.bezier(0.55, 0, 1, 0.45);\nconst HEAVY = { mass: 1, damping: 26, stiffness: 120 };\nconst CARD = { mass: 0.8, damping: 18, stiffness: 160 };\nconst CHIP = { mass: 0.5, damping: 12, stiffness: 200 };\n\nconst DARK = {\n  copy: \"#f4f4f6\",\n  copyMuted: \"rgba(244,244,246,0.52)\",\n  scrim: \"rgba(2,3,8,0.3)\",\n  frame:\n    \"linear-gradient(155deg,#55565c 0%,#2a2b2f 30%,#3d3e43 62%,#1f2023 100%)\",\n  phoneShadow:\n    \"0 50px 100px -20px rgba(0,0,0,0.7), 0 18px 36px -12px rgba(0,0,0,0.5)\",\n  screen: \"#000000\",\n  text: \"#ffffff\",\n  muted: \"#8d8d93\",\n  chip: \"#1c1c1e\",\n  pill: \"#ffffff\",\n  pillText: \"#000000\",\n  touch: \"rgba(255,255,255,0.18)\",\n};\nconst LIGHT: typeof DARK = {\n  copy: \"#141518\",\n  copyMuted: \"rgba(20,21,24,0.5)\",\n  scrim: \"rgba(243,241,237,0.28)\",\n  frame:\n    \"linear-gradient(155deg,#f1f1f3 0%,#c4c4ca 30%,#e2e2e6 62%,#b3b3ba 100%)\",\n  phoneShadow:\n    \"0 50px 90px -24px rgba(30,32,44,0.32), 0 14px 30px -10px rgba(30,32,44,0.18)\",\n  screen: \"#f6f6f8\",\n  text: \"#111114\",\n  muted: \"#74747a\",\n  chip: \"#e8e8ec\",\n  pill: \"#111114\",\n  pillText: \"#ffffff\",\n  touch: \"rgba(20,20,30,0.2)\",\n};\ntype Palette = typeof DARK;\n\nconst STACK = [\n  { color: \"#2a2b30\", label: \"Metal\", last4: \"7702\" },\n  { color: \"#b9b4ab\", label: \"Stone\", last4: \"3158\" },\n  { color: \"\", label: \"Standard\", last4: \"0446\" },\n];\n\nfunction hash(n: number) {\n  const x = Math.sin(n * 91.7 + 47.3) * 43758.5453;\n  return x - Math.floor(x);\n}\n\nfunction inkOn(hex: string) {\n  const h = hex.replace(\"#\", \"\");\n  const n = Number.parseInt(\n    h.length === 3\n      ? h\n          .split(\"\")\n          .map((x) => x + x)\n          .join(\"\")\n      : h.slice(0, 6),\n    16,\n  );\n  const lum =\n    (0.2126 * ((n >> 16) & 255) +\n      0.7152 * ((n >> 8) & 255) +\n      0.0722 * (n & 255)) /\n    255;\n  return lum > 0.62 ? \"#15161a\" : \"#ffffff\";\n}\nconst GRAIN = `url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0.55 0'/></filter><rect width='160' height='160' filter='url(%23g)'/></svg>\")`;\nconst gloss = (color: string) =>\n  `${GRAIN}, linear-gradient(140deg, rgba(255,255,255,0.12) 0%, rgba(255,255,255,0.03) 38%, rgba(0,0,0,0) 60%, rgba(0,0,0,0.14) 100%), ${color}`;\nconst cardEdge = (color: string) =>\n  inkOn(color) === \"#ffffff\"\n    ? \"inset 0 1px 0 rgba(255,255,255,0.16), inset 0 0 0 1px rgba(255,255,255,0.07)\"\n    : \"inset 0 1px 0 rgba(255,255,255,0.45), inset 0 0 0 1px rgba(0,0,0,0.08)\";\n\nfunction Chip() {\n  const id = `chip${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  return (\n    <svg width={42} height={32} viewBox=\"0 0 42 32\">\n      <defs>\n        <linearGradient id={id} x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\">\n          <stop offset=\"0\" stopColor=\"#f3dca0\" />\n          <stop offset=\"0.5\" stopColor=\"#c9a65a\" />\n          <stop offset=\"1\" stopColor=\"#ecd394\" />\n        </linearGradient>\n      </defs>\n      <rect width={42} height={32} rx={6} fill={`url(#${id})`} />\n      <path\n        d=\"M0 11h13M0 21h13M29 11h13M29 21h13M13 0v32M29 0v32M13 16h16\"\n        stroke=\"rgba(90,60,10,0.45)\"\n        strokeWidth={1}\n        fill=\"none\"\n      />\n    </svg>\n  );\n}\n\nfunction Contactless({ color }: { color: string }) {\n  return (\n    <svg width={20} height={24} viewBox=\"0 0 20 24\" fill=\"none\">\n      {[4, 8, 12, 16].map((x, i) => (\n        <path\n          key={x}\n          d={`M${x} ${12 - 3 - i * 2.4}a${4 + i * 3} ${4 + i * 3} 0 010 ${6 + i * 4.8}`}\n          stroke={color}\n          strokeWidth={1.8}\n          strokeLinecap=\"round\"\n        />\n      ))}\n    </svg>\n  );\n}\n\nfunction CardFront({\n  color,\n  prev,\n  wipe = 1,\n  label,\n  holder,\n  shown,\n  engraveAt,\n  f,\n  last4,\n}: {\n  color: string;\n  prev?: string;\n  wipe?: number;\n  label: string;\n  holder: string;\n  shown: number;\n  engraveAt?: (i: number) => number;\n  f: number;\n  last4: string;\n}) {\n  const ink = inkOn(color);\n  const edge = wipe * 1.5 - 0.25;\n  const letters = holder.toUpperCase().split(\"\");\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        borderRadius: 16,\n        overflow: \"hidden\",\n        background: gloss(prev ?? color),\n        backgroundBlendMode: \"soft-light, normal, normal\",\n        boxShadow: cardEdge(color),\n        color: ink,\n      }}\n    >\n      {prev && wipe < 1 ? (\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            background: gloss(color),\n            backgroundBlendMode: \"soft-light, normal, normal\",\n            clipPath: `polygon(0 0, ${edge * 100 + 20}% 0, ${edge * 100}% 100%, 0 100%)`,\n          }}\n        />\n      ) : null}\n      <div\n        style={{\n          position: \"absolute\",\n          left: 22,\n          top: 18,\n          fontSize: 24,\n          fontWeight: 400,\n          letterSpacing: \"-0.045em\",\n          color: inkOn(prev && wipe < 0.5 ? prev : color),\n        }}\n      >\n        fernway\n      </div>\n      <div\n        style={{\n          position: \"absolute\",\n          right: 18,\n          top: 20,\n          fontSize: 12,\n          fontWeight: 400,\n          opacity: 0.75,\n          color: inkOn(prev && wipe < 0.5 ? prev : color),\n        }}\n      >\n        {label}\n      </div>\n      <div style={{ position: \"absolute\", left: 22, top: 78 }}>\n        <Chip />\n      </div>\n      <div style={{ position: \"absolute\", left: 74, top: 82, opacity: 0.8 }}>\n        <Contactless color={ink} />\n      </div>\n      <div\n        style={{\n          position: \"absolute\",\n          left: 22,\n          bottom: 20,\n          display: \"flex\",\n          fontSize: 14,\n          fontWeight: 400,\n          letterSpacing: \"0.12em\",\n          color: ink,\n          textShadow:\n            ink === \"#ffffff\"\n              ? \"0 -1px 0 rgba(0,0,0,0.35), 0 1px 0 rgba(255,255,255,0.25)\"\n              : \"0 1px 0 rgba(255,255,255,0.5), 0 -1px 0 rgba(0,0,0,0.25)\",\n        }}\n      >\n        {letters.slice(0, shown).map((ch, i) => {\n          const t = engraveAt ? f - engraveAt(i) : 99;\n          const settle = interpolate(t, [0, 5], [0, 1], {\n            ...clamp,\n            easing: Easing.bezier(0.22, 1, 0.36, 1),\n          });\n          return (\n            <span\n              key={i}\n              style={{\n                position: \"relative\",\n                display: \"inline-block\",\n                whiteSpace: \"pre\",\n                opacity: 0.2 + settle * 0.75,\n                transform: `translateY(${(1 - settle) * 3}px)`,\n                filter: `blur(${(1 - settle) * 1.5}px)`,\n              }}\n            >\n              {ch}\n            </span>\n          );\n        })}\n      </div>\n      <div\n        style={{\n          position: \"absolute\",\n          right: 20,\n          bottom: 16,\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"flex-end\",\n          color: ink,\n        }}\n      >\n        <span style={{ fontSize: 11, fontWeight: 400, opacity: 0.7 }}>\n          •• {last4}\n        </span>\n        <span\n          style={{\n            fontSize: 17,\n            fontWeight: 400,\n            fontStyle: \"italic\",\n            letterSpacing: \"-0.02em\",\n          }}\n        >\n          debit\n        </span>\n      </div>\n    </div>\n  );\n}\n\nfunction CardBack({\n  color,\n  number,\n  exp,\n  cvv,\n}: {\n  color: string;\n  number: string;\n  exp: string;\n  cvv: string;\n}) {\n  const ink = inkOn(color);\n  const label: React.CSSProperties = {\n    fontSize: 11,\n    fontWeight: 400,\n    opacity: 0.65,\n  };\n  const val: React.CSSProperties = {\n    fontSize: 17,\n    fontWeight: 400,\n    fontVariantNumeric: \"tabular-nums\",\n    letterSpacing: \"0.04em\",\n    marginTop: 3,\n  };\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        borderRadius: 16,\n        overflow: \"hidden\",\n        background: `linear-gradient(rgba(0,0,0,0.1), rgba(0,0,0,0.1)), ${gloss(color)}`,\n        backgroundBlendMode: \"normal, soft-light, normal, normal\",\n        boxShadow: cardEdge(color),\n        transform: \"rotateY(180deg)\",\n        backfaceVisibility: \"hidden\",\n        color: ink,\n        padding: \"22px 22px\",\n        boxSizing: \"border-box\",\n      }}\n    >\n      <div style={label}>Card number</div>\n      <div style={{ ...val, fontSize: 21 }}>{number}</div>\n      <div style={{ display: \"flex\", gap: 40, marginTop: 22 }}>\n        <div>\n          <div style={label}>Expiry</div>\n          <div style={val}>{exp}</div>\n        </div>\n        <div>\n          <div style={label}>CVV</div>\n          <div style={val}>{cvv}</div>\n        </div>\n      </div>\n      <div\n        style={{\n          position: \"absolute\",\n          right: 16,\n          top: 16,\n          width: 34,\n          height: 34,\n          borderRadius: 17,\n          background: \"rgba(255,255,255,0.18)\",\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n        }}\n      >\n        <svg width={18} height={18} viewBox=\"0 0 24 24\" fill=\"none\">\n          <path\n            d=\"M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12zM12 9a3 3 0 110 6 3 3 0 010-6z\"\n            stroke={ink}\n            strokeWidth={2}\n            strokeLinejoin=\"round\"\n          />\n        </svg>\n      </div>\n      <div\n        style={{\n          position: \"absolute\",\n          right: 20,\n          bottom: 16,\n          fontSize: 16,\n          fontWeight: 400,\n          letterSpacing: \"-0.045em\",\n          opacity: 0.85,\n        }}\n      >\n        fernway\n      </div>\n    </div>\n  );\n}\n\nfunction StatusBar({ color }: { color: string }) {\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        top: 0,\n        left: 0,\n        right: 0,\n        height: 54,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"space-between\",\n        padding: \"4px 30px 0 52px\",\n        zIndex: 30,\n      }}\n    >\n      <span\n        style={{ fontSize: 17, fontWeight: 400, color, letterSpacing: -0.3 }}\n      >\n        9:41\n      </span>\n      <div style={{ display: \"flex\", gap: 6, alignItems: \"center\" }}>\n        <svg width=\"18\" height=\"12\" viewBox=\"0 0 18 12\">\n          {[0, 1, 2, 3].map((i) => (\n            <rect\n              key={i}\n              x={i * 4.6}\n              y={9 - i * 3}\n              width={3.2}\n              height={3 + i * 3}\n              rx={0.8}\n              fill={color}\n            />\n          ))}\n        </svg>\n        <svg width=\"17\" height=\"12\" viewBox=\"0 0 17 12\">\n          <path\n            d=\"M8.5 11.5l2.4-2.9a3.6 3.6 0 00-4.8 0zM3.7 6.2a7 7 0 019.6 0l1.6-1.9a9.6 9.6 0 00-12.8 0zM.5 2.9a12 12 0 0116 0\"\n            fill={color}\n            stroke={color}\n            strokeWidth={0.6}\n          />\n        </svg>\n        <div\n          style={{\n            width: 26,\n            height: 12.5,\n            borderRadius: 4,\n            border: `1px solid ${color}`,\n            padding: 1.5,\n            boxSizing: \"border-box\",\n          }}\n        >\n          <div\n            style={{\n              width: \"80%\",\n              height: \"100%\",\n              borderRadius: 2,\n              background: color,\n            }}\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction Phone({ children, c }: { children: React.ReactNode; c: Palette }) {\n  const screen = c.screen;\n  const home = c.text;\n  const side = (s: \"left\" | \"right\", top: number, h: number) => (\n    <div\n      style={{\n        position: \"absolute\",\n        [s]: -2.5,\n        top,\n        width: 4,\n        height: h,\n        borderRadius: 2,\n        background: c.frame,\n      }}\n    />\n  );\n  return (\n    <div style={{ position: \"relative\", width: BW, height: BH }}>\n      {side(\"left\", 168, 30)}\n      {side(\"left\", 226, 60)}\n      {side(\"left\", 298, 60)}\n      {side(\"right\", 258, 96)}\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          borderRadius: 67,\n          background: c.frame,\n          boxShadow: `${c.phoneShadow}, inset 0 0 0 1px rgba(255,255,255,0.22)`,\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 3,\n          borderRadius: 64,\n          background: \"#050506\",\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          left: BEZEL,\n          top: BEZEL,\n          width: SW,\n          height: SH,\n          borderRadius: 55,\n          overflow: \"hidden\",\n          background: screen,\n        }}\n      >\n        {children}\n        <div\n          style={{\n            position: \"absolute\",\n            top: 11,\n            left: (SW - 124) / 2,\n            width: 124,\n            height: 36,\n            borderRadius: 18,\n            background: \"#000\",\n            zIndex: 40,\n          }}\n        />\n        <div\n          style={{\n            position: \"absolute\",\n            bottom: 8,\n            left: (SW - 136) / 2,\n            width: 136,\n            height: 5,\n            borderRadius: 3,\n            background: home,\n            zIndex: 40,\n          }}\n        />\n      </div>\n    </div>\n  );\n}\n\nexport function CardCreate({\n  holder = \"Maya Chen\",\n  last4 = \"4821\",\n  colors = [\"#232428\", \"#c4673f\", \"#8fa68a\", \"#d9bf86\", \"#3d5677\", \"#ebe8e1\"],\n  finishes = [\"Graphite\", \"Terracotta\", \"Sage\", \"Sand\", \"Harbour\", \"Bone\"],\n  accent = \"#3B5BFD\",\n  speed = 1,\n  theme = \"dark\",\n  lines = [\n    \"Spin up a new card.\",\n    \"Pick a finish.\",\n    \"Put your name on it.\",\n    \"Only you see the number.\",\n    \"Ready to spend.\",\n  ],\n  ground,\n  groundParams,\n}: CardCreateProps) {\n  const frame = useCurrentFrame();\n  const { fps, width, height } = useVideoConfig();\n  const f = frame * speed;\n  const dark = theme !== \"light\";\n  const c: Palette = dark ? DARK : LIGHT;\n  const tall = height > width;\n  const W = tall ? 720 : 1280;\n  const H = tall ? 1280 : 720;\n  const u = Math.min(width / W, height / H);\n  const k = tall ? (H * 0.72) / BH : (H * 0.94) / BH;\n  const phoneCx = tall ? W / 2 : W * 0.68;\n  const phoneCy = tall ? H - 30 - (BH * k) / 2 : H / 2;\n\n  const sp = (at: number, config: Partial<typeof CARD> = CARD) =>\n    spring({ frame: f - at, fps, config });\n  const bump = (at: number) =>\n    Math.max(0, Math.min(1, sp(at - 1, CHIP) - sp(at + 5, CHIP)));\n  const enter = (v: number, dy = 14): React.CSSProperties => ({\n    opacity: Math.min(1, v * 1.4),\n    transform: `translateY(${(1 - v) * dy}px)`,\n  });\n\n  const palette =\n    colors.length >= 4\n      ? colors\n      : [...colors, ...STACK.map((s) => s.color || accent)];\n  const swatches = palette.slice(0, 6);\n  const swatchX = (i: number) => SW / 2 + (i - (swatches.length - 1) / 2) * 50;\n  const taps = SWATCH_TAPS.map((t, i) => ({\n    t,\n    idx: Math.min(i + 1, swatches.length - 1),\n  }));\n  let colorIdx = 0;\n  let prevIdx = 0;\n  let wipe = 1;\n  for (const tp of taps) {\n    if (f >= tp.t + 1) {\n      prevIdx = colorIdx;\n      colorIdx = tp.idx;\n      wipe = interpolate(f, [tp.t + 1, tp.t + 13], [0, 1], {\n        ...clamp,\n        easing: Easing.bezier(0.65, 0, 0.35, 1),\n      });\n    }\n  }\n  const lastTap = taps.filter((tp) => f >= tp.t + 1).pop();\n  const ring = sp(lastTap ? lastTap.t : -99, CHIP);\n  const letters = holder.toUpperCase().split(\"\");\n  const engraveAt = (i: number) => ENGRAVE + i * 2.4;\n  const shown = letters.filter((_, i) => f >= engraveAt(i)).length;\n  const flipA = sp(FLIP, HEAVY);\n  const wal = sp(WALLET, HEAVY);\n  const angle = flipA * 180 + wal * 180;\n  const lift = Math.sin(Math.min(1, flipA) * Math.PI) * 0.06;\n  const digits = `5274 8820 1934 ${last4}`;\n  const scr = (s: string, offset: number) =>\n    s\n      .split(\"\")\n      .map((ch, j) => {\n        if (ch === \" \" || ch === \"/\" || offset + j >= 15) return ch;\n        const settle = SCRAMBLE + (offset + j) * 0.9;\n        if (f < SCRAMBLE) return \"•\";\n        if (f >= settle + 4) return ch;\n        return String(\n          Math.floor(hash(j * 13 + offset + Math.floor(f / 2)) * 10),\n        );\n      })\n      .join(\"\");\n  const number = f < SCRAMBLE ? `•••• •••• •••• ${last4}` : scr(digits, 0);\n  const exp = scr(\"09/29\", 19);\n  const cvv = scr(\"417\", 25);\n  const stackIn = STACK.map((_, i) => sp(8 + i * 6, CARD));\n  const out = sp(OUT, HEAVY);\n  const back = sp(WALLET + 6, CARD);\n  const slot = sp(SLOT, CARD);\n  const slotGone = interpolate(f, [PRINT + 34, PRINT + 44], [1, 0], clamp);\n  const print = interpolate(f, [PRINT, PRINT + 30], [0, 1], {\n    ...clamp,\n    easing: ENTER,\n  });\n  const sweep = interpolate(f, [PRINT + 18, PRINT + 40], [-0.4, 1.4], {\n    ...clamp,\n    easing: MOVE,\n  });\n  const custIn = sp(SLOT + 6, CARD);\n  const custOut = interpolate(f, [ADD_TAP + 1, ADD_TAP + 7], [1, 0], {\n    ...clamp,\n    easing: EXIT,\n  });\n  const tool = f >= DETAILS_TAB + 1 ? 2 : f >= NAME_TAB + 1 ? 1 : 0;\n  const toast = sp(WALLET + 26, CHIP);\n  const newY =\n    f < WALLET\n      ? CARD_Y - (1 - print) * (CHT + 18)\n      : CARD_Y + wal * (190 + 3 * 52 - CARD_Y);\n  const toolX = (i: number) => SW / 2 + (i - 1) * 112;\n  const path = [\n    { t: 44, x: 300, y: 700 },\n    { t: TAP_NEW, x: SW / 2 - 64, y: 580 },\n    ...taps.map((tp) => ({ t: tp.t, x: swatchX(tp.idx), y: SWATCH_Y + 18 })),\n    { t: NAME_TAB, x: toolX(1), y: TOOLS_Y + 19 },\n    { t: DETAILS_TAB, x: toolX(2), y: TOOLS_Y + 19 },\n    { t: REVEAL_TAP, x: CX + CW - 33, y: CARD_Y + 33 },\n    { t: ADD_TAP, x: SW / 2 + 30, y: SH - 69 },\n  ];\n  let touch = path[0];\n  for (let i = 0; i < path.length - 1; i++) {\n    const a = path[i];\n    const b = path[i + 1];\n    const s = a.t + 2;\n    const e = Math.max(s + 1, b.t - 2);\n    if (f >= s) {\n      const p = MOVE(Math.min(1, (f - s) / (e - s)));\n      const arc =\n        Math.sin(Math.PI * p) *\n        Math.min(40, Math.hypot(b.x - a.x, b.y - a.y) * 0.12);\n      touch = {\n        t: f,\n        x: a.x + (b.x - a.x) * p,\n        y: a.y + (b.y - a.y) * p - arc,\n      };\n    }\n  }\n  const touchOpacity = interpolate(\n    f,\n    [\n      44,\n      52,\n      TAP_NEW + 6,\n      TAP_NEW + 14,\n      SWATCH_TAPS[0] - 16,\n      SWATCH_TAPS[0] - 8,\n      ADD_TAP + 5,\n      ADD_TAP + 11,\n    ],\n    [0, 1, 1, 0, 0, 1, 1, 0],\n    clamp,\n  );\n  const touchPress = Math.max(...path.slice(1).map((p) => bump(p.t)));\n  const camS = interpolate(f, [0, 370], [1, 1.03], clamp);\n  const rise = sp(0, HEAVY);\n\n  const stackCard = (i: number, y: number, style: React.CSSProperties) => (\n    <div\n      key={STACK[i].label}\n      style={{\n        position: \"absolute\",\n        left: CX,\n        top: y,\n        width: CW,\n        height: CHT,\n        borderRadius: 16,\n        boxShadow: dark\n          ? \"0 -4px 14px rgba(0,0,0,0.4), 0 10px 28px rgba(0,0,0,0.35)\"\n          : \"0 -2px 8px rgba(20,20,30,0.06), 0 8px 24px rgba(20,20,30,0.12)\",\n        ...style,\n      }}\n    >\n      <CardFront\n        color={STACK[i].color || accent}\n        label={STACK[i].label}\n        holder={holder}\n        shown={99}\n        f={f}\n        last4={STACK[i].last4}\n      />\n    </div>\n  );\n  const walletPhase = f >= WALLET;\n  const count = walletPhase ? 4 : 3;\n  const titleA = walletPhase ? back : 1 - out;\n\n  const beats = [0, SWATCH_TAPS[0] - 14, NAME_TAB - 2, DETAILS_TAB - 2, WALLET];\n  const copyLine = (i: number) => {\n    const inAt = beats[i] + (i === 0 ? 8 : 0);\n    const outAt = beats[i + 1];\n    const pin = interpolate(f, [inAt, inAt + 14], [0, 1], {\n      ...clamp,\n      easing: ENTER,\n    });\n    const pout =\n      outAt === undefined\n        ? 0\n        : interpolate(f, [outAt - 2, outAt + 6], [0, 1], {\n            ...clamp,\n            easing: EXIT,\n          });\n    if (pin <= 0 || pout >= 1) return null;\n    return (\n      <div\n        key={i}\n        style={{\n          position: \"absolute\",\n          left: 0,\n          right: 0,\n          top: 0,\n          transform: `translateY(${(1 - pin) * 70 - pout * 50}%)`,\n          opacity: pin * (1 - pout),\n        }}\n      >\n        {lines[i]}\n      </div>\n    );\n  };\n\n  return (\n    <AbsoluteFill style={{ fontFamily, overflow: \"hidden\" }}>\n      <GshGround\n        preset={ground ?? (dark ? \"charcoal-satin\" : \"paper-crease\")}\n        params={groundParams}\n        timeOffset={dark ? 6 : 4}\n        timeScale={0.35}\n      />\n      <AbsoluteFill style={{ background: c.scrim }} />\n      <div\n        style={{\n          position: \"absolute\",\n          left: (width - W * u) / 2,\n          top: (height - H * u) / 2,\n          width: W,\n          height: H,\n          transformOrigin: \"0 0\",\n          transform: `scale(${u})`,\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: tall ? 56 : 88,\n            top: tall ? 84 : H / 2 - 112,\n            width: tall ? W - 112 : 480,\n            height: tall ? 140 : 160,\n            fontSize: tall ? 62 : 70,\n            fontWeight: 300,\n            lineHeight: 1.04,\n            letterSpacing: \"-0.035em\",\n            color: c.copy,\n            overflow: \"hidden\",\n            textWrap: \"balance\",\n          }}\n        >\n          {lines.map((_, i) => copyLine(i))}\n        </div>\n        <div\n          style={{\n            position: \"absolute\",\n            left: phoneCx - BW / 2,\n            top: phoneCy - BH / 2,\n            width: BW,\n            height: BH,\n            transform: `translateY(${(1 - rise) * H * 0.9}px) scale(${k * camS})`,\n          }}\n        >\n          <Phone c={c}>\n            <StatusBar color={c.text} />\n            <div\n              style={{\n                position: \"absolute\",\n                left: 22,\n                top: 96,\n                color: c.text,\n                opacity: titleA,\n                transform: `translateY(${(1 - titleA) * -14}px)`,\n              }}\n            >\n              <div\n                style={{\n                  fontSize: 34,\n                  fontWeight: 400,\n                  letterSpacing: \"-0.03em\",\n                }}\n              >\n                Cards\n              </div>\n              <div style={{ fontSize: 15, color: c.muted, marginTop: 2 }}>\n                {count} cards · Fernway\n              </div>\n            </div>\n            {STACK.map((_, i) => {\n              if (!walletPhase) {\n                const v = stackIn[i];\n                const o = sp(OUT + i * 3, HEAVY);\n                return stackCard(i, 196 + i * 60, {\n                  transform: `translateY(${(1 - v) * 640 - o * 340}px) rotate(${(1 - v) * (i - 1) * 8}deg) scale(${1 - o * 0.08})`,\n                  opacity: 1 - o,\n                });\n              }\n              const v = sp(WALLET + 6 + i * 3, CARD);\n              return stackCard(i, 190 + i * 52, {\n                transform: `translateY(${(1 - v) * -28}px) scale(${0.97 + v * 0.03})`,\n                opacity: Math.min(1, v * 1.4),\n              });\n            })}\n            <div\n              style={{\n                position: \"absolute\",\n                top: 560,\n                left: 0,\n                right: 0,\n                display: \"flex\",\n                justifyContent: \"center\",\n                gap: 10,\n                ...enter(sp(28, CARD) * (1 - out)),\n              }}\n            >\n              <div\n                style={{\n                  height: 42,\n                  padding: \"0 20px\",\n                  borderRadius: 21,\n                  background: c.pill,\n                  color: c.pillText,\n                  fontSize: 15,\n                  fontWeight: 400,\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  gap: 6,\n                  transform: `scale(${1 - bump(TAP_NEW) * 0.05})`,\n                }}\n              >\n                <span style={{ fontSize: 20, fontWeight: 400, marginTop: -2 }}>\n                  +\n                </span>\n                New card\n              </div>\n              <div\n                style={{\n                  height: 42,\n                  padding: \"0 20px\",\n                  borderRadius: 21,\n                  background: c.chip,\n                  color: c.text,\n                  fontSize: 15,\n                  fontWeight: 400,\n                  display: \"flex\",\n                  alignItems: \"center\",\n                }}\n              >\n                Manage\n              </div>\n            </div>\n            {f >= SLOT ? (\n              <div style={{ opacity: Math.min(custIn, custOut) }}>\n                <div\n                  style={{\n                    position: \"absolute\",\n                    top: 58,\n                    left: 20,\n                    right: 22,\n                    height: 40,\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    justifyContent: \"space-between\",\n                  }}\n                >\n                  <svg width={24} height={24} viewBox=\"0 0 24 24\" fill=\"none\">\n                    <path\n                      d=\"M19 12H5M11 6l-6 6 6 6\"\n                      stroke={c.text}\n                      strokeWidth={2}\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                  <span\n                    style={{ fontSize: 16, fontWeight: 400, color: accent }}\n                  >\n                    Skip\n                  </span>\n                </div>\n                <div\n                  style={{\n                    position: \"absolute\",\n                    left: 22,\n                    top: 104,\n                    fontSize: 30,\n                    fontWeight: 400,\n                    letterSpacing: \"-0.03em\",\n                    color: c.text,\n                    ...enter(custIn),\n                  }}\n                >\n                  Customise card\n                </div>\n                <div\n                  style={{\n                    position: \"absolute\",\n                    left: 20,\n                    right: 20,\n                    top: SLOT_Y - 5,\n                    height: 10,\n                    borderRadius: 5,\n                    background: dark ? \"#1a1a1d\" : \"#d9d9de\",\n                    boxShadow: \"inset 0 3px 5px rgba(0,0,0,0.6)\",\n                    transform: `scaleX(${slot})`,\n                    opacity: slotGone,\n                  }}\n                />\n                {swatches.map((col, i) => (\n                  <div\n                    key={col + i}\n                    style={{\n                      position: \"absolute\",\n                      left: swatchX(i) - 17,\n                      top: SWATCH_Y + 1,\n                      width: 34,\n                      height: 34,\n                      borderRadius: 17,\n                      background: col,\n                      boxShadow:\n                        i === colorIdx\n                          ? `inset 0 0 0 1px rgba(255,255,255,0.1), 0 0 0 3px ${c.screen}, 0 0 0 ${3 + 2 * ring}px ${c.text}`\n                          : `inset 0 0 0 1px ${dark ? \"rgba(255,255,255,0.1)\" : \"rgba(0,0,0,0.08)\"}`,\n                      transform: `scale(${1 - bump(taps.find((tp) => tp.idx === i)?.t ?? -99) * 0.12})`,\n                      ...enter(sp(PRINT + 20 + i * 2, CHIP), 10),\n                    }}\n                  />\n                ))}\n                {[\"Colour\", \"Name\", \"Details\"].map((t, i) => (\n                  <div\n                    key={t}\n                    style={{\n                      position: \"absolute\",\n                      left: toolX(i) - 50,\n                      top: TOOLS_Y,\n                      width: 100,\n                      height: 38,\n                      borderRadius: 19,\n                      background: tool === i ? c.chip : \"transparent\",\n                      boxShadow:\n                        tool === i ? \"none\" : `inset 0 0 0 1px ${c.chip}`,\n                      color: tool === i ? c.text : c.muted,\n                      fontSize: 14,\n                      fontWeight: 400,\n                      display: \"flex\",\n                      alignItems: \"center\",\n                      justifyContent: \"center\",\n                      ...enter(sp(PRINT + 28 + i * 3, CHIP), 10),\n                    }}\n                  >\n                    {t}\n                  </div>\n                ))}\n                {[\n                  `Virtual · ${finishes[colorIdx] ?? \"Custom\"}`,\n                  `Printed on the front as ${holder.toUpperCase()}`,\n                  \"Only you can see the full number\",\n                ].map((hint, i) => {\n                  const on = [PRINT + 34, NAME_TAB + 2, DETAILS_TAB + 2][i];\n                  const off = [NAME_TAB + 1, DETAILS_TAB + 1, 9999][i];\n                  const v =\n                    interpolate(f, [on, on + 6], [0, 1], clamp) *\n                    interpolate(f, [off, off + 4], [1, 0], clamp) *\n                    (i === 0 ? 0.35 + 0.65 * wipe : 1);\n                  return (\n                    <div\n                      key={i}\n                      style={{\n                        position: \"absolute\",\n                        left: 0,\n                        right: 0,\n                        top: TOOLS_Y + 62,\n                        textAlign: \"center\",\n                        fontSize: 15,\n                        color: i === 0 ? c.text : c.muted,\n                        opacity: v,\n                        transform: `translateY(${(1 - v) * 4}px)`,\n                      }}\n                    >\n                      {hint}\n                    </div>\n                  );\n                })}\n                <div\n                  style={{\n                    position: \"absolute\",\n                    left: 16,\n                    right: 16,\n                    bottom: 42,\n                    height: 54,\n                    borderRadius: 27,\n                    background: c.pill,\n                    color: c.pillText,\n                    fontSize: 17,\n                    fontWeight: 400,\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    justifyContent: \"center\",\n                    transform: `scale(${1 - bump(ADD_TAP) * 0.04})`,\n                    ...enter(sp(PRINT + 34, CARD), 20),\n                  }}\n                >\n                  Add to wallet\n                </div>\n              </div>\n            ) : null}\n            {f >= PRINT ? (\n              <div\n                style={{\n                  position: \"absolute\",\n                  left: CX - 20,\n                  top: f < WALLET ? SLOT_Y : 0,\n                  width: CW + 40,\n                  height: f < WALLET ? SH - SLOT_Y : SH,\n                  overflow: f < PRINT + 32 ? \"hidden\" : \"visible\",\n                  perspective: 1100,\n                  zIndex: 5,\n                }}\n              >\n                <div\n                  style={{\n                    position: \"absolute\",\n                    left: 20,\n                    top: newY - (f < WALLET ? SLOT_Y : 0),\n                    width: CW,\n                    height: CHT,\n                    transformStyle: \"preserve-3d\",\n                    transform: `rotateY(${angle}deg) scale(${1 + lift})`,\n                    boxShadow:\n                      Math.cos((angle * Math.PI) / 180) > 0.2 ||\n                      Math.cos((angle * Math.PI) / 180) < -0.2\n                        ? dark\n                          ? \"0 18px 40px rgba(0,0,0,0.45)\"\n                          : \"0 2px 4px rgba(20,20,30,0.06), 0 18px 36px rgba(20,20,30,0.16)\"\n                        : \"none\",\n                    borderRadius: 16,\n                  }}\n                >\n                  <div\n                    style={{\n                      position: \"absolute\",\n                      inset: 0,\n                      backfaceVisibility: \"hidden\",\n                    }}\n                  >\n                    <CardFront\n                      color={swatches[colorIdx]}\n                      prev={wipe < 1 ? swatches[prevIdx] : undefined}\n                      wipe={wipe}\n                      label=\"Virtual\"\n                      holder={holder}\n                      shown={shown}\n                      engraveAt={engraveAt}\n                      f={f}\n                      last4={last4}\n                    />\n                    <div\n                      style={{\n                        position: \"absolute\",\n                        inset: 0,\n                        borderRadius: 16,\n                        overflow: \"hidden\",\n                        pointerEvents: \"none\",\n                      }}\n                    >\n                      <div\n                        style={{\n                          position: \"absolute\",\n                          top: -40,\n                          bottom: -40,\n                          width: 90,\n                          left: `${sweep * 100}%`,\n                          transform: \"translateX(-50%) skewX(-16deg)\",\n                          background:\n                            \"linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,0.16), rgba(255,255,255,0))\",\n                        }}\n                      />\n                    </div>\n                  </div>\n                  <CardBack\n                    color={swatches[colorIdx]}\n                    number={number}\n                    exp={f < SCRAMBLE ? \"••/••\" : exp}\n                    cvv={f < SCRAMBLE ? \"•••\" : cvv}\n                  />\n                </div>\n              </div>\n            ) : null}\n            <div\n              style={{\n                position: \"absolute\",\n                left: \"50%\",\n                top: 600,\n                transform: `translateX(-50%) translateY(${(1 - toast) * 20}px) scale(${0.9 + toast * 0.1})`,\n                opacity: Math.min(1, toast * 1.5),\n                height: 44,\n                padding: \"0 18px 0 12px\",\n                borderRadius: 22,\n                background: c.chip,\n                color: c.text,\n                fontSize: 15,\n                fontWeight: 400,\n                display: \"flex\",\n                alignItems: \"center\",\n                gap: 8,\n                whiteSpace: \"nowrap\",\n              }}\n            >\n              <div\n                style={{\n                  width: 22,\n                  height: 22,\n                  borderRadius: 11,\n                  background: accent,\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  justifyContent: \"center\",\n                }}\n              >\n                <svg width={14} height={14} viewBox=\"0 0 24 24\" fill=\"none\">\n                  <path\n                    d=\"M5 12.5l4.5 4.5L19 7.5\"\n                    stroke=\"#fff\"\n                    strokeWidth={3}\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                  />\n                </svg>\n              </div>\n              Card ready to use\n            </div>\n            <div\n              style={{\n                position: \"absolute\",\n                left: touch.x - 22,\n                top: touch.y - 22,\n                width: 44,\n                height: 44,\n                borderRadius: 22,\n                background: c.touch,\n                boxShadow: dark\n                  ? \"inset 0 0 0 1.5px rgba(255,255,255,0.7)\"\n                  : \"inset 0 0 0 1.5px rgba(255,255,255,0.9), 0 4px 14px rgba(0,0,0,0.18)\",\n                opacity: touchOpacity,\n                transform: `scale(${1 - touchPress * 0.2})`,\n                zIndex: 35,\n              }}\n            />\n          </Phone>\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/card-create.tsx"
    }
  ],
  "type": "registry:component"
}
