{"version":1,"id":"credit-card","framework":"next","kind":"block","files":[{"path":"src/blocks/gfa/credit-card.tsx","content":"import type { ReactNode } from \"react\";\nimport \"@/blocks/gfa/credit-card.css\";\nimport { CreditCardBalanceOdometer } from \"@/blocks/gfa/credit-card-balance-odometer\";\nimport { CreditCardPanDisplay } from \"@/blocks/gfa/credit-card-pan-display\";\nimport { CreditCardChip } from \"@/blocks/gfa/credit-card-chip\";\nimport { MastercardLogo, VisaLogo } from \"@/blocks/gfa/credit-card-payment-logos\";\nimport { cn } from \"@/lib/utils\";\n\nexport type CreditCardProps = {\n  className?: string;\n  holderName?: string;\n  lastFour?: string;\n  balance?: number;\n};\n\nfunction normalizeLastFour(lastFour: string): string {\n  const digits = lastFour.replace(/\\D/g, \"\").slice(-4);\n  return digits.padStart(4, \"0\");\n}\n\nfunction CardFluidBackground() {\n  return (\n    <>\n      <div className=\"pointer-events-none absolute inset-0 bg-[#050505]\" aria-hidden />\n      <div\n        className=\"gfa-cc-fluid-silver pointer-events-none absolute -left-[18%] top-[8%] h-[95%] w-[78%] rounded-[42%] bg-[radial-gradient(ellipse_at_center,rgba(255,255,255,0.22)_0%,rgba(180,190,210,0.12)_28%,transparent_68%)] blur-[1px]\"\n        aria-hidden\n      />\n      <div\n        className=\"gfa-cc-fluid-chrome pointer-events-none absolute -right-[8%] top-[-6%] h-[88%] w-[62%] rounded-[40%] bg-[radial-gradient(ellipse_at_30%_40%,rgba(248,250,252,0.38)_0%,rgba(203,213,225,0.24)_30%,rgba(148,163,184,0.14)_52%,transparent_72%)]\"\n        aria-hidden\n      />\n      <div\n        className=\"gfa-cc-fluid-blue pointer-events-none absolute bottom-[-6%] left-[14%] h-[78%] w-[58%] origin-center rounded-[38%] bg-[radial-gradient(ellipse_at_center,rgba(59,130,246,0.35)_0%,rgba(30,64,175,0.15)_40%,transparent_72%)] blur-[0.5px]\"\n        aria-hidden\n      />\n      <div\n        className=\"gfa-cc-fluid-sheen pointer-events-none absolute inset-0 bg-[linear-gradient(145deg,rgba(255,255,255,0.06)_0%,transparent_38%,rgba(0,0,0,0.35)_100%)]\"\n        aria-hidden\n      />\n      <div\n        className=\"pointer-events-none absolute inset-0 opacity-[0.07] mix-blend-overlay [background-image:repeating-linear-gradient(115deg,transparent,transparent_2px,rgba(255,255,255,0.5)_2px,rgba(255,255,255,0.5)_3px)]\"\n        aria-hidden\n      />\n    </>\n  );\n}\n\nfunction FieldLabel({ children }: { children: ReactNode }) {\n  return (\n    <p className=\"gfa-cc-label text-[10px] uppercase text-white/40 sm:text-[11px]\">\n      {children}\n    </p>\n  );\n}\n\n/** Premium dark credit card with fluid metal background (front only). */\nexport function CreditCard({\n  className,\n  holderName = \"Gokhan Akman\",\n  lastFour = \"4149\",\n  balance = 46_917.37,\n}: CreditCardProps) {\n  const last4 = normalizeLastFour(lastFour);\n\n  return (\n    <article\n      data-gfa-credit-card=\"\"\n      className={cn(\n        \"relative aspect-[1.586] w-full max-w-[min(100%,420px)] overflow-hidden rounded-[1.35rem] p-5 sm:rounded-[1.5rem] sm:p-6\",\n        \"shadow-[0_24px_48px_-12px_rgba(0,0,0,0.55)]\",\n        \"ring-1 ring-inset ring-white/[0.08]\",\n        className,\n      )}\n      aria-label=\"Kredi kartı\"\n    >\n      <CardFluidBackground />\n      <div className=\"relative flex h-full flex-col\">\n        <div className=\"grid grid-cols-[minmax(0,1fr)_auto] items-start gap-x-4\">\n          <div className=\"min-w-0\">\n            <FieldLabel>Card number</FieldLabel>\n            <p className=\"gfa-cc-pan mt-1 text-[0.8rem] leading-none sm:text-[0.9rem]\">\n              <CreditCardPanDisplay lastFour={last4} />\n            </p>\n          </div>\n          <div className=\"shrink-0 text-right\">\n            <FieldLabel>Name</FieldLabel>\n            <p className=\"gfa-cc-metal gfa-cc-name mt-1 whitespace-nowrap\">\n              {holderName}\n            </p>\n          </div>\n        </div>\n\n        <div className=\"mt-5 sm:mt-6\">\n          <CreditCardChip />\n        </div>\n\n        <div className=\"mt-auto flex items-end justify-between gap-4 pt-6\">\n          <div>\n            <p className=\"gfa-cc-balance-label text-sm text-white/85 sm:text-base\">Balance</p>\n            <p className=\"gfa-cc-balance-value mt-0.5 text-2xl leading-none sm:text-[1.65rem]\">\n              <CreditCardBalanceOdometer value={balance} />\n            </p>\n          </div>\n          <div className=\"flex shrink-0 items-center gap-2.5 pb-0.5\" aria-hidden>\n            <VisaLogo className=\"h-4 w-auto sm:h-5\" />\n            <MastercardLogo metallic />\n          </div>\n        </div>\n      </div>\n    </article>\n  );\n}\n"},{"path":"src/blocks/gfa/credit-card-chip.tsx","content":"\"use client\";\n\nimport { useId } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type CreditCardChipProps = {\n  className?: string;\n};\n\n/**\n * EMV chip artwork (Kibo UI geometry) with silver/chrome face matching card metallics.\n */\nexport function CreditCardChip({ className }: CreditCardChipProps) {\n  const uid = useId().replace(/:/g, \"\");\n  const faceId = `gfa-credit-card-chip-face-${uid}`;\n  const contactId = `gfa-credit-card-chip-contact-${uid}`;\n\n  return (\n    <svg\n      enableBackground=\"new 0 0 110 92\"\n      viewBox=\"0 0 110 92\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={cn(\"h-10 w-auto shrink-0 sm:h-11\", className)}\n      aria-hidden\n    >\n      <title>Chip</title>\n      <defs>\n        <linearGradient id={faceId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n          <stop offset=\"0%\" stopColor=\"#f8fafc\" />\n          <stop offset=\"18%\" stopColor=\"#e2e8f0\" />\n          <stop offset=\"42%\" stopColor=\"#94a3b8\" />\n          <stop offset=\"62%\" stopColor=\"#f1f5f9\" />\n          <stop offset=\"82%\" stopColor=\"#cbd5e1\" />\n          <stop offset=\"100%\" stopColor=\"#64748b\" />\n        </linearGradient>\n        <linearGradient id={contactId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n          <stop offset=\"0%\" stopColor=\"#334155\" />\n          <stop offset=\"35%\" stopColor=\"#1e293b\" />\n          <stop offset=\"68%\" stopColor=\"#475569\" />\n          <stop offset=\"100%\" stopColor=\"#0f172a\" />\n        </linearGradient>\n      </defs>\n      <path\n        fill={`url(#${faceId})`}\n        d=\"M1 13A12 12 0 0 1 13 1h84a12 12 0 0 1 12 12v66a12 12 0 0 1-12 12H13A12 12 0 0 1 1 79V13Z\"\n      />\n      <path\n        fill={`url(#${contactId})`}\n        d=\"M108 71.5H83.65L70.53 60.87A21.41 21.41 0 0 1 56 67.47V90h41a11 11 0 0 0 11-11v-7.5ZM76.48 47a21.38 21.38 0 0 1-4.63 12.36l12.5 10.14H108V47H76.48ZM2 69.5h24.14l12.02-10.12A21.38 21.38 0 0 1 33.52 47H2v22.5Zm53-43c-5.85 0-11.1 2.57-14.68 6.66A19.4 19.4 0 0 0 35.5 46a19.4 19.4 0 0 0 4.82 12.84A19.43 19.43 0 0 0 55 65.5c5.85 0 11.1-2.57 14.68-6.66A19.4 19.4 0 0 0 74.5 46a19.4 19.4 0 0 0-4.82-12.84A19.43 19.43 0 0 0 55 26.5Zm16.85 6.14A21.38 21.38 0 0 1 76.48 45H108V22.5H84.35l-12.5 10.14ZM2 45h31.52a21.38 21.38 0 0 1 4.64-12.38L26.14 22.5H2V45Zm0 34a11 11 0 0 0 11 11h41V67.47a21.41 21.41 0 0 1-14.52-6.59L27.14 71.26l-.27.24H2V79Zm106-66A11 11 0 0 0 97 2H56v22.52c5.7.27 10.83 2.74 14.53 6.61L83.65 20.5H108V13ZM2 20.5h24.87l.27.24 12.34 10.38A21.41 21.41 0 0 1 54 24.52V2H13A11 11 0 0 0 2 13v7.5ZM110 79a13 13 0 0 1-13 13H13A13 13 0 0 1 0 79V13A13 13 0 0 1 13 0h84a13 13 0 0 1 13 13v66Z\"\n      />\n    </svg>\n  );\n}\n"},{"path":"src/blocks/gfa/credit-card-payment-logos.tsx","content":"\"use client\";\n\nimport { useId } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ntype LogoProps = { className?: string; metallic?: boolean; shimmer?: boolean };\n\n/** Shared visual height for card corner payment marks. */\nexport const PAYMENT_LOGO_CLASS = \"h-6 w-auto sm:h-7\";\n\nconst VISA_TRANSFORM = \"matrix(4.4299631,0,0,4.4299631,-81.165783,-105.04783)\";\n\nfunction VisaClipGeometry() {\n  return (\n    <>\n      <polygon points=\"116.145,95.719 97.858,95.719 109.296,24.995 127.582,24.995\" />\n      <path d=\"m 182.437,26.724 c -3.607,-1.431 -9.328,-3.011 -16.402,-3.011 -18.059,0 -30.776,9.63 -30.854,23.398 -0.15,10.158 9.105,15.8 16.027,19.187 7.075,3.461 9.48,5.72 9.48,8.805 -0.072,4.738 -5.717,6.922 -10.982,6.922 -7.301,0 -11.213,-1.126 -17.158,-3.762 l -2.408,-1.13 -2.559,15.876 c 4.289,1.954 12.191,3.688 20.395,3.764 19.188,0 31.68,-9.481 31.828,-24.153 0.073,-8.051 -4.814,-14.22 -15.35,-19.261 -6.396,-3.236 -10.313,-5.418 -10.313,-8.729 0.075,-3.01 3.313,-6.093 10.533,-6.093 5.945,-0.151 10.313,1.278 13.622,2.708 l 1.654,0.751 2.487,-15.272 0,0 z\" />\n      <path d=\"m 206.742,70.664 c 1.506,-4.063 7.301,-19.788 7.301,-19.788 -0.076,0.151 1.503,-4.138 2.406,-6.771 l 1.278,6.094 c 0,0 3.463,16.929 4.215,20.465 -2.858,0 -11.588,0 -15.2,0 l 0,0 z m 22.573,-45.669 -14.145,0 c -4.362,0 -7.676,1.278 -9.558,5.868 l -27.163,64.855 19.188,0 c 0,0 3.159,-8.729 3.838,-10.609 2.105,0 20.771,0 23.479,0 0.525,2.483 2.182,10.609 2.182,10.609 l 16.932,0 -14.753,-70.723 0,0 z\" />\n      <path d=\"M 82.584,24.995 64.675,73.222 62.718,63.441 C 59.407,52.155 49.023,39.893 37.435,33.796 l 16.404,61.848 19.338,0 28.744,-70.649 -19.337,0 0,0 z\" />\n      <path d=\"m 48.045,24.995 -29.422,0 -0.301,1.429 c 22.951,5.869 38.151,20.016 44.396,37.02 L 56.322,30.94 c -1.053,-4.517 -4.289,-5.796 -8.277,-5.945 l 0,0 z\" />\n    </>\n  );\n}\n\nfunction SvgLogoShimmer({\n  clipId,\n  shimmerGradId,\n  width,\n  height,\n  delay = \"0s\",\n}: {\n  clipId: string;\n  shimmerGradId: string;\n  width: number;\n  height: number;\n  delay?: string;\n}) {\n  const band = width * 0.45;\n\n  return (\n    <g clipPath={`url(#${clipId})`} className=\"gfa-cc-payment-logo-shimmer-svg\" aria-hidden>\n      <defs>\n        <linearGradient\n          id={shimmerGradId}\n          gradientUnits=\"userSpaceOnUse\"\n          x1={0}\n          y1={0}\n          x2={band}\n          y2={height}\n        >\n          <stop offset=\"0%\" stopColor=\"#ffffff\" stopOpacity=\"0\" />\n          <stop offset=\"38%\" stopColor=\"#ffffff\" stopOpacity=\"0.05\" />\n          <stop offset=\"50%\" stopColor=\"#ffffff\" stopOpacity=\"0.65\" />\n          <stop offset=\"62%\" stopColor=\"#ffffff\" stopOpacity=\"0.08\" />\n          <stop offset=\"100%\" stopColor=\"#ffffff\" stopOpacity=\"0\" />\n          <animateTransform\n            attributeName=\"gradientTransform\"\n            type=\"translate\"\n            from={`${-band} 0`}\n            to={`${width + band} 0`}\n            dur=\"3.6s\"\n            begin={delay}\n            repeatCount=\"indefinite\"\n          />\n        </linearGradient>\n      </defs>\n      <rect\n        x={0}\n        y={0}\n        width={width}\n        height={height}\n        fill={`url(#${shimmerGradId})`}\n        opacity={0.95}\n      />\n    </g>\n  );\n}\n\n/** Visa 2006 wordmark (Wikimedia / public domain artwork). */\nexport function VisaLogo({ className, metallic = true, shimmer = true }: LogoProps) {\n  const uid = useId().replace(/:/g, \"\");\n  const silverId = `gfa-visa-silver-${uid}`;\n  const goldId = `gfa-visa-gold-${uid}`;\n  const clipId = `gfa-visa-clip-${uid}`;\n  const shimmerGradId = `gfa-visa-shimmer-${uid}`;\n\n  const mainFill = metallic ? `url(#${silverId})` : \"#00579f\";\n  const accentFill = metallic ? `url(#${goldId})` : \"#faa61a\";\n\n  return (\n    <svg\n      className={cn(PAYMENT_LOGO_CLASS, className)}\n      viewBox=\"0 0 1000.046 323.653\"\n      role=\"img\"\n      aria-label=\"Visa\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <defs>\n        {metallic ? (\n          <>\n            <linearGradient id={silverId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n              <stop offset=\"0%\" stopColor=\"#f8fafc\" />\n              <stop offset=\"22%\" stopColor=\"#e2e8f0\" />\n              <stop offset=\"48%\" stopColor=\"#94a3b8\" />\n              <stop offset=\"72%\" stopColor=\"#f1f5f9\" />\n              <stop offset=\"100%\" stopColor=\"#64748b\" />\n            </linearGradient>\n            <linearGradient id={goldId} x1=\"0%\" y1=\"100%\" x2=\"100%\" y2=\"0%\">\n              <stop offset=\"0%\" stopColor=\"#e8edf2\" />\n              <stop offset=\"35%\" stopColor=\"#b8c4d0\" />\n              <stop offset=\"68%\" stopColor=\"#7a8a9a\" />\n              <stop offset=\"100%\" stopColor=\"#dce4ec\" />\n            </linearGradient>\n          </>\n        ) : null}\n        <clipPath id={clipId}>\n          <g transform={VISA_TRANSFORM}>\n            <VisaClipGeometry />\n          </g>\n        </clipPath>\n      </defs>\n      <g transform={VISA_TRANSFORM}>\n        <polygon points=\"116.145,95.719 97.858,95.719 109.296,24.995 127.582,24.995\" fill={mainFill} />\n        <path\n          fill={mainFill}\n          d=\"m 182.437,26.724 c -3.607,-1.431 -9.328,-3.011 -16.402,-3.011 -18.059,0 -30.776,9.63 -30.854,23.398 -0.15,10.158 9.105,15.8 16.027,19.187 7.075,3.461 9.48,5.72 9.48,8.805 -0.072,4.738 -5.717,6.922 -10.982,6.922 -7.301,0 -11.213,-1.126 -17.158,-3.762 l -2.408,-1.13 -2.559,15.876 c 4.289,1.954 12.191,3.688 20.395,3.764 19.188,0 31.68,-9.481 31.828,-24.153 0.073,-8.051 -4.814,-14.22 -15.35,-19.261 -6.396,-3.236 -10.313,-5.418 -10.313,-8.729 0.075,-3.01 3.313,-6.093 10.533,-6.093 5.945,-0.151 10.313,1.278 13.622,2.708 l 1.654,0.751 2.487,-15.272 0,0 z\"\n        />\n        <path\n          fill={mainFill}\n          d=\"m 206.742,70.664 c 1.506,-4.063 7.301,-19.788 7.301,-19.788 -0.076,0.151 1.503,-4.138 2.406,-6.771 l 1.278,6.094 c 0,0 3.463,16.929 4.215,20.465 -2.858,0 -11.588,0 -15.2,0 l 0,0 z m 22.573,-45.669 -14.145,0 c -4.362,0 -7.676,1.278 -9.558,5.868 l -27.163,64.855 19.188,0 c 0,0 3.159,-8.729 3.838,-10.609 2.105,0 20.771,0 23.479,0 0.525,2.483 2.182,10.609 2.182,10.609 l 16.932,0 -14.753,-70.723 0,0 z\"\n        />\n        <path\n          fill={mainFill}\n          d=\"M 82.584,24.995 64.675,73.222 62.718,63.441 C 59.407,52.155 49.023,39.893 37.435,33.796 l 16.404,61.848 19.338,0 28.744,-70.649 -19.337,0 0,0 z\"\n        />\n        <path fill={accentFill} d=\"m 48.045,24.995 -29.422,0 -0.301,1.429 c 22.951,5.869 38.151,20.016 44.396,37.02 L 56.322,30.94 c -1.053,-4.517 -4.289,-5.796 -8.277,-5.945 l 0,0 z\" />\n      </g>\n      {metallic && shimmer ? (\n        <SvgLogoShimmer\n          clipId={clipId}\n          shimmerGradId={shimmerGradId}\n          width={1000.046}\n          height={323.653}\n        />\n      ) : null}\n    </svg>\n  );\n}\n\n/** Simplified Mastercard mark for UI demos (not an official brand asset). */\nexport function MastercardLogo({ className, metallic = true, shimmer = true }: LogoProps) {\n  const uid = useId().replace(/:/g, \"\");\n  const leftId = `gfa-mc-left-${uid}`;\n  const rightId = `gfa-mc-right-${uid}`;\n  const overlapId = `gfa-mc-overlap-${uid}`;\n  const clipId = `gfa-mc-clip-${uid}`;\n  const shimmerGradId = `gfa-mc-shimmer-${uid}`;\n\n  if (!metallic) {\n    return (\n      <svg\n        className={cn(PAYMENT_LOGO_CLASS, className)}\n        viewBox=\"0 0 32 20\"\n        role=\"img\"\n        aria-label=\"Mastercard\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <circle cx=\"11\" cy=\"10\" r=\"8\" fill=\"#EB001B\" />\n        <circle cx=\"21\" cy=\"10\" r=\"8\" fill=\"#F79E1B\" />\n        <path fill=\"#FF5F00\" d=\"M16 4.2a8 8 0 0 0 0 11.6 8 8 0 0 0 0-11.6z\" />\n      </svg>\n    );\n  }\n\n  return (\n    <svg\n      className={cn(PAYMENT_LOGO_CLASS, className)}\n      viewBox=\"0 0 32 20\"\n      role=\"img\"\n      aria-label=\"Mastercard\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <defs>\n        <linearGradient id={leftId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n          <stop offset=\"0%\" stopColor=\"#f1f5f9\" />\n          <stop offset=\"45%\" stopColor=\"#94a3b8\" />\n          <stop offset=\"100%\" stopColor=\"#475569\" />\n        </linearGradient>\n        <linearGradient id={rightId} x1=\"100%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n          <stop offset=\"0%\" stopColor=\"#f4f7fa\" />\n          <stop offset=\"38%\" stopColor=\"#c5ced8\" />\n          <stop offset=\"100%\" stopColor=\"#5c6b7a\" />\n        </linearGradient>\n        <linearGradient id={overlapId} x1=\"0%\" y1=\"50%\" x2=\"100%\" y2=\"50%\">\n          <stop offset=\"0%\" stopColor=\"#a8b4c0\" />\n          <stop offset=\"50%\" stopColor=\"#e8edf2\" />\n          <stop offset=\"100%\" stopColor=\"#6b7a88\" />\n        </linearGradient>\n        <clipPath id={clipId}>\n          <circle cx=\"11\" cy=\"10\" r=\"8\" />\n          <circle cx=\"21\" cy=\"10\" r=\"8\" />\n        </clipPath>\n      </defs>\n      <circle cx=\"11\" cy=\"10\" r=\"8\" fill={`url(#${leftId})`} />\n      <circle cx=\"21\" cy=\"10\" r=\"8\" fill={`url(#${rightId})`} />\n      <path fill={`url(#${overlapId})`} d=\"M16 4.2a8 8 0 0 0 0 11.6 8 8 0 0 0 0-11.6z\" />\n      {shimmer ? (\n        <SvgLogoShimmer\n          clipId={clipId}\n          shimmerGradId={shimmerGradId}\n          width={32}\n          height={20}\n          delay=\"1.65s\"\n        />\n      ) : null}\n    </svg>\n  );\n}\n\nexport function PaymentLogosRow({ className, metallic = true }: LogoProps) {\n  return (\n    <div className={cn(\"flex items-center gap-2.5\", className)} aria-hidden>\n      <VisaLogo metallic={metallic} />\n      <MastercardLogo metallic={metallic} />\n    </div>\n  );\n}\n"},{"path":"src/blocks/gfa/credit-card-pan-display.tsx","content":"\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { useReducedMotion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\nconst MASKED_PAN = \"**** **** ****\";\nconst SCRAMBLE_POOL = \"*#•0123456789ABCDEF\";\nconst DIGIT_POOL = \"0123456789\";\n\nfunction scrambleTemplate(template: string, pool: string): string {\n  return template\n    .split(\"\")\n    .map((char) => (char === \" \" ? \" \" : pool[Math.floor(Math.random() * pool.length)]))\n    .join(\"\");\n}\n\nexport type CreditCardPanDisplayProps = {\n  lastFour: string;\n  className?: string;\n};\n\n/** Masked PAN scrambles like encrypted data, then locks to asterisks + last four. */\nexport function CreditCardPanDisplay({ lastFour, className }: CreditCardPanDisplayProps) {\n  const reducedMotion = useReducedMotion();\n  /** Deterministic SSR/first paint — scramble only after mount (avoids hydration mismatch). */\n  const [masked, setMasked] = useState(MASKED_PAN);\n  const [visibleLast4, setVisibleLast4] = useState(lastFour);\n\n  useEffect(() => {\n    if (reducedMotion) {\n      return;\n    }\n\n    setMasked(scrambleTemplate(MASKED_PAN, SCRAMBLE_POOL));\n    setVisibleLast4(scrambleTemplate(lastFour, DIGIT_POOL));\n\n    const maskedTemplate = MASKED_PAN.split(\"\");\n    const last4Template = lastFour.split(\"\");\n    const intervals: ReturnType<typeof setInterval>[] = [];\n    const timeouts: ReturnType<typeof setTimeout>[] = [];\n    let cancelled = false;\n\n    const scrambleTick = setInterval(() => {\n      if (cancelled) return;\n      setMasked(scrambleTemplate(MASKED_PAN, SCRAMBLE_POOL));\n      setVisibleLast4(scrambleTemplate(lastFour, DIGIT_POOL));\n    }, 48);\n    intervals.push(scrambleTick);\n\n    const beginResolve = setTimeout(() => {\n      if (cancelled) return;\n      clearInterval(scrambleTick);\n\n      let maskedIndex = -1;\n      const resolveMasked = setInterval(() => {\n        if (cancelled) return;\n        maskedIndex += 1;\n        setMasked(\n          maskedTemplate\n            .map((target, index) => {\n              if (target === \" \") return \" \";\n              if (index <= maskedIndex) return \"*\";\n              return SCRAMBLE_POOL[Math.floor(Math.random() * SCRAMBLE_POOL.length)]!;\n            })\n            .join(\"\"),\n        );\n        if (maskedIndex >= maskedTemplate.length - 1) {\n          clearInterval(resolveMasked);\n        }\n      }, 32);\n      intervals.push(resolveMasked);\n\n      const beginLast4 = setTimeout(() => {\n        if (cancelled) return;\n        let last4Index = -1;\n        const resolveLast4 = setInterval(() => {\n          if (cancelled) return;\n          last4Index += 1;\n          setVisibleLast4(\n            last4Template\n              .map((digit, index) => {\n                if (index <= last4Index) return digit;\n                return DIGIT_POOL[Math.floor(Math.random() * DIGIT_POOL.length)]!;\n              })\n              .join(\"\"),\n          );\n          if (last4Index >= last4Template.length - 1) {\n            clearInterval(resolveLast4);\n          }\n        }, 55);\n        intervals.push(resolveLast4);\n      }, 180);\n      timeouts.push(beginLast4);\n    }, 720);\n    timeouts.push(beginResolve);\n\n    return () => {\n      cancelled = true;\n      for (const id of intervals) clearInterval(id);\n      for (const id of timeouts) clearTimeout(id);\n    };\n  }, [lastFour, reducedMotion]);\n\n  return (\n    <span className={cn(\"inline\", className)} aria-label={`Kart numarası, son dört hane ${lastFour}`}>\n      <span className=\"gfa-cc-metal-muted gfa-cc-pan-mask tracking-[0.08em]\">{masked}</span>\n      <span className=\"gfa-cc-metal ml-1.5 tracking-[0.18em] tabular-nums sm:ml-2\">{visibleLast4}</span>\n    </span>\n  );\n}\n"},{"path":"src/blocks/gfa/credit-card-balance-odometer.tsx","content":"\"use client\";\n\nimport { LazyMotion, domAnimation, m, useReducedMotion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\nexport function formatCreditCardBalance(value: number): string {\n  return new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency: \"USD\",\n    minimumFractionDigits: 2,\n    maximumFractionDigits: 2,\n  }).format(value);\n}\n\nconst ODOMETER_EASE = [0.16, 1, 0.3, 1] as const;\nconst ROLL_DURATION = 1.35;\nconst DIGIT_STAGGER = 0.055;\n\nfunction OdometerDigit({\n  digit,\n  delay,\n}: {\n  digit: number;\n  delay: number;\n}) {\n  return (\n    <span\n      className=\"relative inline-block h-[1em] w-[0.58em] overflow-hidden leading-none sm:w-[0.56em]\"\n      aria-hidden\n    >\n      <m.span\n        className=\"absolute inset-x-0 top-0 flex flex-col\"\n        initial={{ y: 0 }}\n        animate={{ y: `calc(${-digit} * 1em)` }}\n        transition={{\n          duration: ROLL_DURATION,\n          delay,\n          ease: ODOMETER_EASE,\n        }}\n      >\n        {Array.from({ length: 10 }, (_, i) => (\n          <span\n            key={i}\n            className=\"gfa-cc-odometer-digit block h-[1em] leading-[1em] tabular-nums\"\n          >\n            {i}\n          </span>\n        ))}\n      </m.span>\n    </span>\n  );\n}\n\nexport type CreditCardBalanceOdometerProps = {\n  value: number;\n  className?: string;\n};\n\n/** Balance rolls up digit-by-digit on first paint (odometer). */\nexport function CreditCardBalanceOdometer({ value, className }: CreditCardBalanceOdometerProps) {\n  const reducedMotion = useReducedMotion();\n  const formatted = formatCreditCardBalance(value);\n\n  if (reducedMotion) {\n    return <span className={className}>{formatted}</span>;\n  }\n\n  let digitIndex = 0;\n\n  return (\n    <LazyMotion features={domAnimation}>\n      <span className={cn(\"gfa-cc-odometer inline-flex items-end leading-none\", className)} aria-label={formatted}>\n        {formatted.split(\"\").map((char, index) => {\n          if (/\\d/.test(char)) {\n            const digit = Number(char);\n            const delay = digitIndex * DIGIT_STAGGER;\n            digitIndex += 1;\n            return <OdometerDigit key={`d-${index}`} digit={digit} delay={delay} />;\n          }\n\n          const symbolDelay = digitIndex * DIGIT_STAGGER + 0.08;\n\n          return (\n            <m.span\n              key={`s-${index}-${char}`}\n              className=\"gfa-cc-odometer-symbol inline-flex h-[1em] items-end leading-none\"\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              transition={{ duration: 0.4, delay: symbolDelay, ease: ODOMETER_EASE }}\n            >\n              {char}\n            </m.span>\n          );\n        })}\n      </span>\n    </LazyMotion>\n  );\n}\n"},{"path":"src/blocks/gfa/credit-card-fonts.ts","content":"import { DM_Mono, Outfit } from \"next/font/google\";\n\n/** Labels, name, balance — geometric sans suited to payment UI. */\nexport const creditCardDisplay = Outfit({\n  variable: \"--font-gfa-cc-display\",\n  subsets: [\"latin\"],\n  weight: [\"400\", \"500\", \"600\"],\n  display: \"swap\",\n});\n\n/** PAN / last four — refined monospace (not Geist Mono). */\nexport const creditCardMono = DM_Mono({\n  variable: \"--font-gfa-cc-mono\",\n  subsets: [\"latin\"],\n  weight: [\"400\", \"500\"],\n  display: \"swap\",\n});\n"},{"path":"src/blocks/gfa/credit-card.css","content":"/* Credit card block — scoped to [data-gfa-credit-card] */\n\n/* Floating fluid metal blobs */\n@keyframes gfa-cc-fluid-silver {\n  0%,\n  100% {\n    transform: rotate(-18deg) translate3d(0, 0, 0) scale(1);\n  }\n  35% {\n    transform: rotate(-14deg) translate3d(4%, -3%, 0) scale(1.05);\n  }\n  70% {\n    transform: rotate(-21deg) translate3d(-3%, 2%, 0) scale(1.02);\n  }\n}\n\n@keyframes gfa-cc-fluid-chrome {\n  0%,\n  100% {\n    transform: rotate(12deg) translate3d(0, 0, 0) scale(1);\n  }\n  50% {\n    transform: rotate(17deg) translate3d(-5%, 5%, 0) scale(1.07);\n  }\n}\n\n@keyframes gfa-cc-fluid-blue {\n  0%,\n  100% {\n    transform: rotate(6deg) translate3d(0, 0, 0) scale(1);\n  }\n  28% {\n    transform: rotate(11deg) translate3d(7%, -9%, 0) scale(1.08);\n  }\n  55% {\n    transform: rotate(3deg) translate3d(-6%, -4%, 0) scale(1.04);\n  }\n  78% {\n    transform: rotate(9deg) translate3d(4%, 5%, 0) scale(1.07);\n  }\n}\n\n@keyframes gfa-cc-fluid-sheen {\n  0%,\n  100% {\n    opacity: 1;\n    transform: translate3d(0, 0, 0);\n  }\n  50% {\n    opacity: 0.88;\n    transform: translate3d(-1%, 1%, 0);\n  }\n}\n\n[data-gfa-credit-card] .gfa-cc-fluid-silver {\n  animation: gfa-cc-fluid-silver 14s ease-in-out infinite;\n  will-change: transform;\n}\n\n[data-gfa-credit-card] .gfa-cc-fluid-chrome {\n  animation: gfa-cc-fluid-chrome 18s ease-in-out infinite;\n  animation-delay: -5s;\n  will-change: transform;\n}\n\n[data-gfa-credit-card] .gfa-cc-fluid-blue {\n  animation: gfa-cc-fluid-blue 15s ease-in-out infinite;\n  animation-delay: -7s;\n  transform-origin: center center;\n  will-change: transform;\n}\n\n[data-gfa-credit-card] .gfa-cc-fluid-sheen {\n  animation: gfa-cc-fluid-sheen 11s ease-in-out infinite;\n  will-change: transform, opacity;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  [data-gfa-credit-card] .gfa-cc-fluid-silver,\n  [data-gfa-credit-card] .gfa-cc-fluid-chrome,\n  [data-gfa-credit-card] .gfa-cc-fluid-blue,\n  [data-gfa-credit-card] .gfa-cc-fluid-sheen {\n    animation: none;\n    will-change: auto;\n  }\n}\n\n/* Typography (Outfit + DM Mono — wire variables in root layout) */\n[data-gfa-credit-card] {\n  font-family: var(--font-gfa-cc-display), var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;\n  font-feature-settings: \"kern\" 1, \"liga\" 1;\n}\n\n[data-gfa-credit-card] .gfa-cc-label {\n  font-weight: 500;\n  letter-spacing: 0.16em;\n}\n\n[data-gfa-credit-card] .gfa-cc-pan {\n  font-family: var(--font-gfa-cc-mono), ui-monospace, monospace;\n  font-weight: 500;\n  font-variant-numeric: tabular-nums;\n  letter-spacing: 0.14em;\n}\n\n[data-gfa-credit-card] .gfa-cc-pan-mask {\n  font-variant-numeric: tabular-nums;\n  letter-spacing: 0.12em;\n}\n\n[data-gfa-credit-card] .gfa-cc-name {\n  font-weight: 600;\n  font-size: 0.6875rem;\n  letter-spacing: 0.14em;\n  text-transform: uppercase;\n}\n\n@media (min-width: 640px) {\n  [data-gfa-credit-card] .gfa-cc-name {\n    font-size: 0.75rem;\n    letter-spacing: 0.12em;\n  }\n}\n\n[data-gfa-credit-card] .gfa-cc-balance-label {\n  font-weight: 500;\n  letter-spacing: 0.03em;\n}\n\n[data-gfa-credit-card] .gfa-cc-balance-value {\n  font-weight: 600;\n  font-variant-numeric: tabular-nums;\n  letter-spacing: -0.03em;\n}\n\n[data-gfa-credit-card] .gfa-cc-odometer {\n  align-items: flex-end;\n  line-height: 1;\n}\n\n[data-gfa-credit-card] .gfa-cc-odometer-digit,\n[data-gfa-credit-card] .gfa-cc-odometer-symbol {\n  background-image: linear-gradient(\n    100deg,\n    #7a8a9a 0%,\n    #d8e0e8 16%,\n    #ffffff 30%,\n    #e8edf2 44%,\n    #f8fafc 52%,\n    #b8c4d0 68%,\n    #f1f5f9 82%,\n    #8a9aaa 100%\n  );\n  background-size: 240% 100%;\n  -webkit-background-clip: text;\n  background-clip: text;\n  color: transparent;\n}\n\n/* Payment logo shimmer (SVG clipPath-masked) */\n[data-gfa-credit-card] .gfa-cc-payment-logo-shimmer-svg {\n  mix-blend-mode: screen;\n  pointer-events: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  [data-gfa-credit-card] .gfa-cc-payment-logo-shimmer-svg {\n    display: none;\n  }\n}\n\n/* Metallic typography */\n@keyframes gfa-cc-metal-shine {\n  0% {\n    background-position: 0% 50%;\n  }\n  100% {\n    background-position: 100% 50%;\n  }\n}\n\n[data-gfa-credit-card] .gfa-cc-metal {\n  background-image: linear-gradient(\n    102deg,\n    #64748b 0%,\n    #cbd5e1 18%,\n    #f8fafc 32%,\n    #e2e8f0 48%,\n    #f1f5f9 58%,\n    #94a3b8 72%,\n    #e8edf2 88%,\n    #6b7a8a 100%\n  );\n  background-size: 220% 100%;\n  background-position: 12% 50%;\n  -webkit-background-clip: text;\n  background-clip: text;\n  color: transparent;\n  animation: gfa-cc-metal-shine 7s cubic-bezier(0.42, 0, 0.58, 1) infinite alternate;\n}\n\n[data-gfa-credit-card] .gfa-cc-metal-muted {\n  background-image: linear-gradient(\n    102deg,\n    #475569 0%,\n    #94a3b8 28%,\n    #cbd5e1 46%,\n    #94a3b8 64%,\n    #64748b 100%\n  );\n  background-size: 200% 100%;\n  background-position: 8% 50%;\n  -webkit-background-clip: text;\n  background-clip: text;\n  color: transparent;\n  animation: gfa-cc-metal-shine 9s cubic-bezier(0.42, 0, 0.58, 1) infinite alternate;\n}\n\n[data-gfa-credit-card] .gfa-cc-metal-balance {\n  background-image: linear-gradient(\n    100deg,\n    #7a8a9a 0%,\n    #d8e0e8 16%,\n    #ffffff 30%,\n    #e8edf2 44%,\n    #f8fafc 52%,\n    #b8c4d0 68%,\n    #f1f5f9 82%,\n    #8a9aaa 100%\n  );\n  background-size: 240% 100%;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  [data-gfa-credit-card] .gfa-cc-metal,\n  [data-gfa-credit-card] .gfa-cc-metal-muted {\n    animation: none;\n    background-position: 50% 50%;\n  }\n}\n"}],"meta":{"studioName":"GFA Development & Design Studio","siteUrl":"https://ui.gkhn.dev"}}