{"version":1,"id":"service-card","framework":"next","kind":"block","files":[{"path":"src/blocks/gfa/service-card.tsx","content":"\"use client\";\n\nimport { Copy, ArrowUpRight } from \"lucide-react\";\nimport { motion } from \"motion/react\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { useId, useState } from \"react\";\nimport { ServiceCardGlbStage } from \"@/blocks/gfa/service-card-glb-stage\";\nimport { cn } from \"@/lib/utils\";\n\nexport type ServiceCardSegment = string;\n\nexport type ServiceCardProps = {\n  tone?: \"dark\" | \"light\";\n  title: string;\n  description: string;\n  imageSrc?: string;\n  imageAlt?: string;\n  glbSrc?: string;\n  href?: string;\n  shareUrl?: string;\n  segments?: ServiceCardSegment[];\n  defaultSegment?: ServiceCardSegment;\n  className?: string;\n  onCopyLink?: (url: string) => void;\n  onShareX?: (url: string) => void;\n};\n\nconst DEFAULT_SEGMENTS: ServiceCardSegment[] = [\"EO/IR\", \"RADAR\", \"SIGINT\"];\n\nfunction XLogo({ className }: { className?: string }) {\n  return (\n    <svg\n      viewBox=\"0 0 24 24\"\n      aria-hidden\n      className={className}\n      fill=\"currentColor\"\n    >\n      <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.743l7.727-8.835L1.254 2.25H8.08l4.258 5.686 5.906-5.686zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n    </svg>\n  );\n}\n\nexport function ServiceCard({\n  tone = \"dark\",\n  title,\n  description,\n  imageSrc,\n  imageAlt = \"\",\n  glbSrc,\n  href = \"#\",\n  shareUrl,\n  segments = DEFAULT_SEGMENTS,\n  defaultSegment,\n  className,\n  onCopyLink,\n  onShareX,\n}: ServiceCardProps) {\n  const options = segments;\n  const initial = defaultSegment ?? options[options.length - 1] ?? options[0];\n  const [segment, setSegment] = useState<ServiceCardSegment>(initial);\n  const [copied, setCopied] = useState(false);\n  const segmentLayoutId = useId();\n  const isDark = tone === \"dark\";\n  const url =\n    shareUrl ??\n    (typeof window !== \"undefined\" ? window.location.href : href);\n\n  const handleCopy = async () => {\n    onCopyLink?.(url);\n    try {\n      await navigator.clipboard.writeText(url);\n      setCopied(true);\n      window.setTimeout(() => setCopied(false), 1600);\n    } catch {\n      /* ignore */\n    }\n  };\n\n  const handleShareX = () => {\n    onShareX?.(url);\n    const intent = `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(title)}`;\n    window.open(intent, \"_blank\", \"noopener,noreferrer\");\n  };\n\n  return (\n    <article\n      data-slot=\"service-card\"\n      data-tone={tone}\n      className={cn(\n        \"flex w-full min-w-0 max-w-[440px] flex-col overflow-hidden rounded-[28px] p-3 sm:p-3.5\",\n        isDark\n          ? \"bg-black text-white\"\n          : \"bg-white text-zinc-950 ring-1 ring-zinc-200/80\",\n        className,\n      )}\n    >\n      <div\n        className={cn(\n          \"relative aspect-[5/4] overflow-hidden rounded-[20px]\",\n          isDark ? \"bg-white\" : \"bg-black\",\n        )}\n      >\n        {glbSrc ? (\n          <ServiceCardGlbStage\n            src={glbSrc}\n            background={isDark ? \"#ffffff\" : \"#000000\"}\n          />\n        ) : imageSrc ? (\n          <Image\n            src={imageSrc}\n            alt={imageAlt}\n            fill\n            sizes=\"(max-width: 640px) 100vw, 420px\"\n            className=\"object-cover\"\n            priority\n          />\n        ) : null}\n      </div>\n\n      <div className=\"flex flex-1 flex-col px-1.5 pb-1.5 pt-5 sm:px-2 sm:pt-6\">\n        <div className=\"flex items-start justify-between gap-3\">\n          <h2 className=\"max-w-[16ch] text-[1.35rem] font-semibold leading-[1.15] tracking-tight sm:text-[1.5rem]\">\n            {title}\n          </h2>\n          <Link\n            href={href}\n            aria-label=\"Open\"\n            className={cn(\n              \"group/arrow inline-flex size-10 shrink-0 items-center justify-center rounded-xl transition-colors\",\n              isDark\n                ? \"bg-white text-black hover:bg-zinc-900 hover:text-white\"\n                : \"bg-black text-white hover:bg-zinc-100 hover:text-black\",\n            )}\n          >\n            <ArrowUpRight\n              className=\"size-4 transition-transform group-hover/arrow:translate-x-0.5 group-hover/arrow:-translate-y-0.5\"\n              strokeWidth={2.25}\n            />\n          </Link>\n        </div>\n\n        <p\n          className={cn(\n            \"mt-3 text-[13px] leading-relaxed sm:text-sm\",\n            isDark\n              ? \"font-sans text-zinc-400\"\n              : \"font-mono text-[12px] leading-[1.55] text-zinc-500 sm:text-[13px]\",\n          )}\n        >\n          {description}\n        </p>\n\n        <div className=\"mt-auto flex w-full min-w-0 flex-nowrap items-center justify-between gap-1.5 pt-7\">\n          <div\n            className={cn(\n              \"inline-flex min-w-0 shrink rounded-full p-0.5\",\n              isDark ? \"bg-zinc-900\" : \"bg-zinc-100\",\n            )}\n            role=\"group\"\n            aria-label=\"Service mode\"\n          >\n            {options.map((item) => {\n              const active = item === segment;\n              return (\n                <button\n                  key={item}\n                  type=\"button\"\n                  onClick={() => setSegment(item)}\n                  className={cn(\n                    \"relative rounded-full px-1.5 py-1 text-[9px] font-medium tracking-wide transition sm:px-2 sm:text-[10px]\",\n                    active\n                      ? isDark\n                        ? \"text-black\"\n                        : \"text-zinc-950\"\n                      : isDark\n                        ? \"text-zinc-500 hover:text-zinc-300\"\n                        : \"text-zinc-500 hover:text-zinc-800\",\n                  )}\n                >\n                  {active ? (\n                    <motion.span\n                      layoutId={segmentLayoutId}\n                      className={cn(\n                        \"absolute inset-0 rounded-full\",\n                        isDark\n                          ? \"bg-white\"\n                          : \"bg-white shadow-sm ring-1 ring-zinc-200/80\",\n                      )}\n                      transition={{\n                        type: \"spring\",\n                        stiffness: 420,\n                        damping: 32,\n                      }}\n                    />\n                  ) : null}\n                  <span className=\"relative z-10 whitespace-nowrap\">{item}</span>\n                </button>\n              );\n            })}\n          </div>\n\n          <div className=\"flex shrink-0 flex-nowrap items-center gap-1\">\n            <button\n              type=\"button\"\n              onClick={handleCopy}\n              className={cn(\n                \"inline-flex h-7 items-center gap-1 rounded-lg px-1.5 text-[10px] font-medium transition sm:h-8 sm:px-2 sm:text-[11px]\",\n                isDark\n                  ? \"bg-zinc-800 text-white hover:bg-zinc-700\"\n                  : \"bg-zinc-100 text-zinc-800 hover:bg-zinc-200/80\",\n              )}\n            >\n              <Copy className=\"size-3 opacity-80\" aria-hidden />\n              <span className=\"whitespace-nowrap\">\n                {copied ? \"Copied\" : \"Copy\"}\n              </span>\n            </button>\n            <button\n              type=\"button\"\n              onClick={handleShareX}\n              className={cn(\n                \"inline-flex h-7 items-center gap-1 rounded-lg px-1.5 text-[10px] font-medium transition sm:h-8 sm:px-2 sm:text-[11px]\",\n                isDark\n                  ? \"bg-white text-black hover:bg-zinc-100\"\n                  : \"bg-black text-white hover:bg-zinc-800\",\n              )}\n            >\n              <XLogo className=\"size-2.5 shrink-0\" />\n              <span className=\"whitespace-nowrap\">Share</span>\n            </button>\n          </div>\n        </div>\n      </div>\n    </article>\n  );\n}\n\nexport { ServiceCard as ServiceCardBlock };\n"}],"meta":{"studioName":"GFA Development & Design Studio","siteUrl":"https://ui.gkhn.dev"}}