Title

Logo Templates — Click-to-Zoom Lightbox (Preview Modal)

Goal

On /business-assets/logo-templates, clicking the template image opens a modal/lightbox with:

Large SVG preview (fit-to-screen, scrollable if larger).

Download SVG and Customize in Composer actions in the modal footer.

Close via X, ESC, or click outside.

Accessible: focus trap, aria-*, return focus to card on close.

Behavior

Desktop: click image → modal opens with zoomable preview; mousewheel scroll scales if using browser zoom; for now we’ll do simple click-to-fit + drag scroll/pan (native scroll).

Mobile: modal opens full-screen; actions pinned at bottom.

Implementation (2 small additions)
1) Add a reusable Preview modal component

File: client/src/components/common/PreviewLightbox.tsx

import React from "react";

type Props = {
  open: boolean;
  onClose: () => void;
  title?: string;
  imgSrc: string;         // svg or png url
  onDownload?: () => void;
  onCustomize?: () => void;
  customizeDisabled?: boolean;
};

export default function PreviewLightbox({
  open, onClose, title = "Preview", imgSrc,
  onDownload, onCustomize, customizeDisabled
}: Props) {
  if (!open) return null;

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-label={title}
      className="fixed inset-0 z-50 flex items-center justify-center"
      onKeyDown={(e) => e.key === "Escape" && onClose()}
    >
      {/* backdrop */}
      <div className="absolute inset-0 bg-black/70" onClick={onClose} />

      {/* modal */}
      <div className="relative z-10 w-[92vw] max-w-4xl max-h-[90vh] bg-white rounded-xl shadow-xl overflow-hidden flex flex-col">
        {/* header */}
        <div className="flex items-center justify-between px-4 py-3 border-b">
          <h3 className="text-base font-semibold">{title}</h3>
          <button
            className="text-gray-500 hover:text-gray-700"
            aria-label="Close preview"
            onClick={onClose}
          >✕</button>
        </div>

        {/* content (scroll/pan) */}
        <div className="flex-1 overflow-auto bg-white p-4">
          <div className="min-h-[60vh] flex items-center justify-center">
            <img
              src={imgSrc}
              alt={title}
              className="max-w-full h-auto object-contain select-none"
              draggable={false}
            />
          </div>
        </div>

        {/* footer actions */}
        <div className="flex items-center justify-between gap-2 px-4 py-3 border-t">
          <button className="btn btn-outline" onClick={onDownload}>Download SVG</button>
          <button
            className="btn btn-primary"
            onClick={onCustomize}
            disabled={!!customizeDisabled}
          >
            Customize in Composer
          </button>
        </div>
      </div>
    </div>
  );
}


Notes: Keeps it simple (native scroll = pan). We can add pinch-to-zoom later if you want.

2) Wire it into the Logo Template card

File: client/src/components/logo/LogoTemplateCard.tsx

--- a/client/src/components/logo/LogoTemplateCard.tsx
+++ b/client/src/components/logo/LogoTemplateCard.tsx
@@ -1,6 +1,9 @@
 import { useLocation } from "wouter";
 import { useUserPlan } from "@/hooks/useUserPlan";
 import PremiumModal from "@/components/paywalls/PremiumModal";
+import PreviewLightbox from "@/components/common/PreviewLightbox";
 
 export function LogoTemplateCard({ template }) {
   const [, navigate] = useLocation();
   const { isPremium } = useUserPlan() ?? { isPremium: false };
   const [showPaywall, setShowPaywall] = React.useState(false);
+  const [showPreview, setShowPreview] = React.useState(false);
 
   const downloadSvg = async () => {
     // (existing download logic)
   };
   const goCustomize = () => {
     if (!isPremium) return setShowPaywall(true);
     navigate(`/brand-development/ai-logo-creator/logo-composer-${template.id}`);
   };
 
   return (
-    <div className="relative rounded-xl border overflow-hidden group">
+    <div className="relative rounded-xl border overflow-hidden group">
       {/* Thumbnail */}
-      <img src={template.previewUrl} alt={template.name} className="w-full object-contain" />
+      <div className="relative aspect-[4/3] bg-white cursor-zoom-in" onClick={()=>setShowPreview(true)}>
+        <img
+          src={template.previewUrl}
+          alt={template.name}
+          className="w-full h-full object-contain transition-transform duration-300 ease-out group-hover:scale-[1.06] motion-reduce:transition-none motion-reduce:transform-none"
+          draggable={false}
+        />
+      </div>
 
       {/* title/tags ... */}
 
       {/* hover overlay buttons (desktop) */}
       {/* (keep your existing overlay actions if you like) */}
 
+      {/* paywall modal */}
       <PremiumModal
         open={showPaywall}
         onClose={() => setShowPaywall(false)}
         onUpgrade={() => navigate("/pricing")}
       />
+
+      {/* preview lightbox */}
+      <PreviewLightbox
+        open={showPreview}
+        onClose={()=>setShowPreview(false)}
+        title={template.name}
+        imgSrc={template.previewUrl}
+        onDownload={downloadSvg}
+        onCustomize={goCustomize}
+        customizeDisabled={false}
+      />
     </div>
   );
 }

QA (quick)

Click thumbnail → modal opens with large preview.

ESC / backdrop click / ✕ → closes and returns focus.

Download SVG works inside modal.

Customize in Composer:

Premium → routes to composer with template ID.

Non-premium → paywall modal appears (from the card logic); after upgrade, try again → routes.

Mobile: modal is full-screen; footer buttons visible; scrollable image.