Impeccable
This commit is contained in:
@@ -1004,49 +1004,51 @@ export default function CreatePage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Foreground Color
|
||||
</label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="color"
|
||||
value={foregroundColor}
|
||||
onChange={(e) => setForegroundColor(e.target.value)}
|
||||
className="w-12 h-10 rounded border border-gray-300"
|
||||
disabled={!canCustomizeColors}
|
||||
/>
|
||||
<Input
|
||||
value={foregroundColor}
|
||||
onChange={(e) => setForegroundColor(e.target.value)}
|
||||
className="flex-1"
|
||||
disabled={!canCustomizeColors}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Background Color
|
||||
</label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="color"
|
||||
value={backgroundColor}
|
||||
onChange={(e) => setBackgroundColor(e.target.value)}
|
||||
className="w-12 h-10 rounded border border-gray-300"
|
||||
disabled={!canCustomizeColors}
|
||||
/>
|
||||
<Input
|
||||
value={backgroundColor}
|
||||
onChange={(e) => setBackgroundColor(e.target.value)}
|
||||
className="flex-1"
|
||||
disabled={!canCustomizeColors}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Foreground Color
|
||||
</label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="color"
|
||||
value={foregroundColor}
|
||||
onChange={(e) => setForegroundColor(e.target.value)}
|
||||
className="w-12 h-10 rounded border border-gray-300"
|
||||
disabled={!canCustomizeColors}
|
||||
/>
|
||||
<Input
|
||||
value={foregroundColor}
|
||||
onChange={(e) => setForegroundColor(e.target.value)}
|
||||
className="flex-1"
|
||||
disabled={!canCustomizeColors}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Background Color
|
||||
</label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="color"
|
||||
value={backgroundColor}
|
||||
onChange={(e) => setBackgroundColor(e.target.value)}
|
||||
className="w-12 h-10 rounded border border-gray-300"
|
||||
disabled={!canCustomizeColors}
|
||||
/>
|
||||
<Input
|
||||
value={backgroundColor}
|
||||
onChange={(e) => setBackgroundColor(e.target.value)}
|
||||
className="flex-1"
|
||||
disabled={!canCustomizeColors}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Select
|
||||
|
||||
@@ -65,7 +65,10 @@ export default function RootLayout({
|
||||
{children}
|
||||
</Providers>
|
||||
</Suspense>
|
||||
</body>
|
||||
{/* impeccable-live-start */}
|
||||
<script src="http://localhost:8400/live.js"></script>
|
||||
{/* impeccable-live-end */}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,280 +1,124 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { motion, Variants } from 'framer-motion';
|
||||
import {
|
||||
Link as LinkIcon,
|
||||
User,
|
||||
Mail,
|
||||
Calendar,
|
||||
Facebook,
|
||||
Instagram,
|
||||
Phone,
|
||||
MessageSquare,
|
||||
Type,
|
||||
Music,
|
||||
Twitter,
|
||||
MessageCircle,
|
||||
Wifi,
|
||||
Youtube,
|
||||
Bitcoin,
|
||||
MapPin,
|
||||
CreditCard,
|
||||
Video,
|
||||
Users,
|
||||
Barcode,
|
||||
Star
|
||||
} from 'lucide-react';
|
||||
|
||||
const TOOLS = [
|
||||
{
|
||||
icon: LinkIcon,
|
||||
name: 'URL',
|
||||
description: 'Open any website',
|
||||
href: '/tools/url-qr-code',
|
||||
color: 'text-blue-500',
|
||||
bg: 'bg-blue-50'
|
||||
},
|
||||
{
|
||||
icon: User,
|
||||
name: 'vCard',
|
||||
description: 'Share contact details',
|
||||
href: '/tools/vcard-qr-code',
|
||||
color: 'text-rose-500',
|
||||
bg: 'bg-rose-50'
|
||||
},
|
||||
{
|
||||
icon: Type,
|
||||
name: 'Text',
|
||||
description: 'Display plain text',
|
||||
href: '/tools/text-qr-code',
|
||||
color: 'text-slate-600',
|
||||
bg: 'bg-slate-50'
|
||||
},
|
||||
{
|
||||
icon: Mail,
|
||||
name: 'Email',
|
||||
description: 'Send an email',
|
||||
href: '/tools/email-qr-code',
|
||||
color: 'text-red-500',
|
||||
bg: 'bg-red-50'
|
||||
},
|
||||
{
|
||||
icon: MessageSquare,
|
||||
name: 'SMS',
|
||||
description: 'Send a text message',
|
||||
href: '/tools/sms-qr-code',
|
||||
color: 'text-green-500',
|
||||
bg: 'bg-green-50'
|
||||
},
|
||||
{
|
||||
icon: Wifi,
|
||||
name: 'WiFi',
|
||||
description: 'Connect to WiFi',
|
||||
href: '/tools/wifi-qr-code',
|
||||
color: 'text-indigo-500',
|
||||
bg: 'bg-indigo-50'
|
||||
},
|
||||
{
|
||||
icon: Bitcoin,
|
||||
name: 'Crypto',
|
||||
description: 'Receive payments',
|
||||
href: '/tools/crypto-qr-code',
|
||||
color: 'text-orange-500',
|
||||
bg: 'bg-orange-50'
|
||||
},
|
||||
{
|
||||
icon: Calendar,
|
||||
name: 'Event',
|
||||
description: 'Save calendar event',
|
||||
href: '/tools/event-qr-code',
|
||||
color: 'text-violet-500',
|
||||
bg: 'bg-violet-50'
|
||||
},
|
||||
{
|
||||
icon: Facebook,
|
||||
name: 'Facebook',
|
||||
description: 'Open Facebook page',
|
||||
href: '/tools/facebook-qr-code',
|
||||
color: 'text-blue-600',
|
||||
bg: 'bg-blue-50'
|
||||
},
|
||||
{
|
||||
icon: Instagram,
|
||||
name: 'Instagram',
|
||||
description: 'Open Instagram profile',
|
||||
href: '/tools/instagram-qr-code',
|
||||
color: 'text-pink-500',
|
||||
bg: 'bg-pink-50'
|
||||
},
|
||||
{
|
||||
icon: Twitter,
|
||||
name: 'Twitter',
|
||||
description: 'Open Twitter profile',
|
||||
href: '/tools/twitter-qr-code',
|
||||
color: 'text-sky-500',
|
||||
bg: 'bg-sky-50'
|
||||
},
|
||||
{
|
||||
icon: Youtube,
|
||||
name: 'YouTube',
|
||||
description: 'Open YouTube video',
|
||||
href: '/tools/youtube-qr-code',
|
||||
color: 'text-red-600',
|
||||
bg: 'bg-red-50'
|
||||
},
|
||||
{
|
||||
icon: MessageCircle,
|
||||
name: 'WhatsApp',
|
||||
description: 'Send WhatsApp message',
|
||||
href: '/tools/whatsapp-qr-code',
|
||||
color: 'text-green-600',
|
||||
bg: 'bg-green-50'
|
||||
},
|
||||
{
|
||||
icon: Music,
|
||||
name: 'TikTok',
|
||||
description: 'Open TikTok profile',
|
||||
href: '/tools/tiktok-qr-code',
|
||||
color: 'text-pink-600',
|
||||
bg: 'bg-pink-50'
|
||||
},
|
||||
{
|
||||
icon: MapPin,
|
||||
name: 'Location',
|
||||
description: 'Share GPS coordinates',
|
||||
href: '/tools/geolocation-qr-code',
|
||||
color: 'text-emerald-500',
|
||||
bg: 'bg-emerald-50'
|
||||
},
|
||||
{
|
||||
icon: Phone,
|
||||
name: 'Call',
|
||||
description: 'Start a phone call',
|
||||
href: '/tools/call-qr-code-generator',
|
||||
color: 'text-violet-500',
|
||||
bg: 'bg-violet-50'
|
||||
},
|
||||
{
|
||||
icon: CreditCard,
|
||||
name: 'PayPal',
|
||||
description: 'Receive PayPal payments',
|
||||
href: '/tools/paypal-qr-code',
|
||||
color: 'text-blue-700',
|
||||
bg: 'bg-blue-50'
|
||||
},
|
||||
{
|
||||
icon: Video,
|
||||
name: 'Zoom',
|
||||
description: 'Join Zoom meeting',
|
||||
href: '/tools/zoom-qr-code',
|
||||
color: 'text-sky-500',
|
||||
bg: 'bg-sky-50'
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
name: 'Teams',
|
||||
description: 'Join Teams meeting',
|
||||
href: '/tools/teams-qr-code',
|
||||
color: 'text-violet-500',
|
||||
bg: 'bg-violet-50'
|
||||
},
|
||||
{
|
||||
icon: Barcode,
|
||||
name: 'Barcode',
|
||||
description: 'Create standard barcodes',
|
||||
href: '/tools/barcode-generator',
|
||||
color: 'text-slate-900',
|
||||
bg: 'bg-slate-100'
|
||||
},
|
||||
{
|
||||
icon: Star,
|
||||
name: 'Google Review',
|
||||
description: 'Get more Google reviews',
|
||||
href: '/tools/google-review-qr-code',
|
||||
color: 'text-yellow-500',
|
||||
bg: 'bg-yellow-50'
|
||||
}
|
||||
];
|
||||
|
||||
// Animation variants
|
||||
const containerVariants: Variants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.05
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const itemVariants: Variants = {
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.4
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function FreeToolsGrid() {
|
||||
return (
|
||||
<section id="tools" className="py-24 bg-slate-50/50 border-t border-slate-100">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<div className="flex flex-col md:flex-row items-center justify-center gap-3 mb-4">
|
||||
<h2 className="text-3xl lg:text-4xl font-bold text-slate-900">
|
||||
More Free QR Code Tools
|
||||
</h2>
|
||||
<div className="bg-gradient-to-r from-emerald-500 to-green-500 text-white px-3 py-1 rounded-full text-xs md:text-sm font-semibold shadow-lg shadow-emerald-500/20 flex items-center gap-2">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-white opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-white"></span>
|
||||
</span>
|
||||
Free Forever
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Create specialized QR codes for every need. Completely free and no signup required.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-4 gap-4 md:gap-6"
|
||||
>
|
||||
{TOOLS.map((tool) => (
|
||||
<motion.div key={tool.name} variants={itemVariants}>
|
||||
<Link
|
||||
href={tool.href}
|
||||
className="group flex flex-col items-center p-5 md:p-6 rounded-2xl border border-slate-200/80 bg-white hover:border-primary-200 hover:shadow-xl hover:shadow-primary-500/10 transition-all duration-300"
|
||||
>
|
||||
<div className={`w-12 h-12 md:w-14 md:h-14 rounded-xl ${tool.bg} flex items-center justify-center mb-3 md:mb-4 group-hover:scale-110 transition-transform duration-300`}>
|
||||
<tool.icon className={`w-6 h-6 md:w-7 md:h-7 ${tool.color}`} />
|
||||
</div>
|
||||
<h3 className="text-base md:text-lg font-semibold text-slate-900 mb-0.5">
|
||||
{tool.name}
|
||||
</h3>
|
||||
<p className="text-xs md:text-sm text-slate-600 text-center">
|
||||
{tool.description}
|
||||
</p>
|
||||
</Link>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { motion, Variants } from 'framer-motion';
|
||||
import {
|
||||
Link as LinkIcon,
|
||||
User,
|
||||
Mail,
|
||||
Calendar,
|
||||
Facebook,
|
||||
Instagram,
|
||||
Phone,
|
||||
MessageSquare,
|
||||
Type,
|
||||
Music,
|
||||
Twitter,
|
||||
MessageCircle,
|
||||
Wifi,
|
||||
Youtube,
|
||||
Bitcoin,
|
||||
MapPin,
|
||||
CreditCard,
|
||||
Video,
|
||||
Users,
|
||||
Barcode,
|
||||
Star
|
||||
} from 'lucide-react';
|
||||
|
||||
const TOOLS = [
|
||||
{ icon: LinkIcon, name: 'URL', description: 'Open any website', href: '/tools/url-qr-code', color: 'text-blue-500', bg: 'bg-blue-50' },
|
||||
{ icon: User, name: 'vCard', description: 'Share contact details', href: '/tools/vcard-qr-code', color: 'text-rose-500', bg: 'bg-rose-50' },
|
||||
{ icon: Type, name: 'Text', description: 'Display plain text', href: '/tools/text-qr-code', color: 'text-slate-600', bg: 'bg-slate-50' },
|
||||
{ icon: Mail, name: 'Email', description: 'Send an email', href: '/tools/email-qr-code', color: 'text-red-500', bg: 'bg-red-50' },
|
||||
{ icon: MessageSquare, name: 'SMS', description: 'Send a text message', href: '/tools/sms-qr-code', color: 'text-green-500', bg: 'bg-green-50' },
|
||||
{ icon: Wifi, name: 'WiFi', description: 'Connect to WiFi', href: '/tools/wifi-qr-code', color: 'text-indigo-500', bg: 'bg-indigo-50' },
|
||||
{ icon: Bitcoin, name: 'Crypto', description: 'Receive payments', href: '/tools/crypto-qr-code', color: 'text-orange-500', bg: 'bg-orange-50' },
|
||||
{ icon: Calendar, name: 'Event', description: 'Save calendar event', href: '/tools/event-qr-code', color: 'text-violet-500', bg: 'bg-violet-50' },
|
||||
{ icon: Facebook, name: 'Facebook', description: 'Open Facebook page', href: '/tools/facebook-qr-code', color: 'text-blue-600', bg: 'bg-blue-50' },
|
||||
{ icon: Instagram, name: 'Instagram', description: 'Open Instagram profile', href: '/tools/instagram-qr-code', color: 'text-pink-500', bg: 'bg-pink-50' },
|
||||
{ icon: Twitter, name: 'Twitter', description: 'Open Twitter profile', href: '/tools/twitter-qr-code', color: 'text-sky-500', bg: 'bg-sky-50' },
|
||||
{ icon: Youtube, name: 'YouTube', description: 'Open YouTube video', href: '/tools/youtube-qr-code', color: 'text-red-600', bg: 'bg-red-50' },
|
||||
{ icon: MessageCircle, name: 'WhatsApp', description: 'Send WhatsApp message', href: '/tools/whatsapp-qr-code', color: 'text-green-600', bg: 'bg-green-50' },
|
||||
{ icon: Music, name: 'TikTok', description: 'Open TikTok profile', href: '/tools/tiktok-qr-code', color: 'text-slate-900', bg: 'bg-slate-100' },
|
||||
{ icon: MapPin, name: 'Location', description: 'Share GPS coordinates', href: '/tools/location-qr-code', color: 'text-emerald-500', bg: 'bg-emerald-50' },
|
||||
{ icon: Phone, name: 'Call', description: 'Start a phone call', href: '/tools/phone-qr-code', color: 'text-teal-500', bg: 'bg-teal-50' },
|
||||
];
|
||||
|
||||
const containerVariants: Variants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: { staggerChildren: 0.05 }
|
||||
}
|
||||
};
|
||||
|
||||
const itemVariants: Variants = {
|
||||
hidden: { opacity: 0, y: 16 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.4 }
|
||||
}
|
||||
};
|
||||
|
||||
export function FreeToolsGrid() {
|
||||
return (
|
||||
<section id="tools" className="py-24 bg-slate-50/50 border-t border-slate-100">
|
||||
<style dangerouslySetInnerHTML={{__html:`
|
||||
.ftg-card { transition: transform 0.22s ease-out, box-shadow 0.22s ease-out, border-color 0.22s ease-out; }
|
||||
.ftg-card:hover { transform: translateY(-8px); box-shadow: rgba(83,58,253,0.2) 0px 20px 40px -12px, rgba(0,0,0,0.08) 0px 8px 16px -8px; border-color: #533afd; }
|
||||
.ftg-icon { transition: transform 0.25s ease-out; }
|
||||
.ftg-card:hover .ftg-icon { transform: rotate(12deg) scale(1.1); }
|
||||
@media (prefers-reduced-motion: reduce) { .ftg-card, .ftg-icon { transition: none !important; } }
|
||||
`}}/>
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<div className="flex flex-col md:flex-row items-center justify-center gap-3 mb-4">
|
||||
<h2 className="text-3xl lg:text-4xl font-bold text-slate-900">More Free QR Code Tools</h2>
|
||||
<div className="bg-gradient-to-r from-emerald-500 to-green-500 text-white px-3 py-1 rounded-full text-xs md:text-sm font-semibold shadow-lg shadow-emerald-500/20 flex items-center gap-2">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-white opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-white"></span>
|
||||
</span>
|
||||
Free Forever
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Create specialized QR codes for every need. Completely free and no signup required.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
className="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6"
|
||||
>
|
||||
{TOOLS.map((tool) => (
|
||||
<motion.div key={tool.name} variants={itemVariants}>
|
||||
<Link
|
||||
href={tool.href}
|
||||
className="ftg-card group flex flex-col items-center p-5 md:p-6 rounded-2xl border border-[#e5edf5] bg-white"
|
||||
>
|
||||
<div className={`ftg-icon w-12 h-12 md:w-14 md:h-14 rounded-xl ${tool.bg} flex items-center justify-center mb-3 md:mb-4`}>
|
||||
<tool.icon className={`w-6 h-6 md:w-7 md:h-7 ${tool.color}`} aria-hidden="true" />
|
||||
</div>
|
||||
<h3 className="text-base md:text-lg font-semibold text-[#061b31] mb-0.5">{tool.name}</h3>
|
||||
<p className="text-xs md:text-sm text-[#64748d] text-center">{tool.description}</p>
|
||||
</Link>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,212 +1,456 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Globe, User, MapPin, Phone, CheckCircle2, ArrowRight, FileText, Ticket, Smartphone, Star } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
// Sub-component for the flipping effect
|
||||
const FlippingCard = ({ front, back, delay }: { front: any, back: any, delay: number }) => {
|
||||
const [isFlipped, setIsFlipped] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Initial delay
|
||||
const initialTimeout = setTimeout(() => {
|
||||
setIsFlipped(true); // First flip
|
||||
|
||||
// Setup interval for subsequent flips
|
||||
const interval = setInterval(() => {
|
||||
setIsFlipped(prev => !prev);
|
||||
}, 8000); // Toggle every 8 seconds to prevent overlap (4 cards * 2s gap)
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, delay * 1000);
|
||||
|
||||
return () => clearTimeout(initialTimeout);
|
||||
}, [delay]);
|
||||
|
||||
return (
|
||||
<div className="relative h-32 w-full perspective-[1000px] group cursor-pointer">
|
||||
<motion.div
|
||||
animate={{ rotateY: isFlipped ? 180 : 0 }}
|
||||
transition={{ duration: 0.6, type: "spring", stiffness: 260, damping: 20 }}
|
||||
className="relative w-full h-full preserve-3d"
|
||||
style={{ transformStyle: 'preserve-3d' }}
|
||||
>
|
||||
{/* Front Face */}
|
||||
<div
|
||||
className="absolute inset-0 backface-hidden"
|
||||
style={{ backfaceVisibility: 'hidden', WebkitBackfaceVisibility: 'hidden' }}
|
||||
>
|
||||
<Card className="w-full h-full backdrop-blur-xl bg-white/70 border-white/50 shadow-xl shadow-gray-200/50 p-4 flex flex-col items-center justify-center hover:scale-105 transition-all duration-300">
|
||||
<div className={`w-10 h-10 mb-3 rounded-xl ${front.color} flex items-center justify-center`}>
|
||||
<front.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<p className="font-semibold text-gray-800 text-sm">{front.title}</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Back Face */}
|
||||
<div
|
||||
className="absolute inset-0 backface-hidden"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden',
|
||||
WebkitBackfaceVisibility: 'hidden',
|
||||
transform: 'rotateY(180deg)'
|
||||
}}
|
||||
>
|
||||
<Card className="w-full h-full backdrop-blur-xl bg-white/80 border-white/60 shadow-xl shadow-blue-200/50 p-4 flex flex-col items-center justify-center hover:scale-105 transition-all duration-300">
|
||||
<div className={`w-10 h-10 mb-3 rounded-xl ${back.color} flex items-center justify-center`}>
|
||||
<back.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<p className="font-semibold text-gray-900 text-sm">{back.title}</p>
|
||||
</Card>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Globe, User, MapPin, Phone, FileText, Ticket, Smartphone, Star } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const FlippingCard = ({ front, back, delay }: { front: any, back: any, delay: number }) => {
|
||||
const [isFlipped, setIsFlipped] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const initialTimeout = setTimeout(() => {
|
||||
setIsFlipped(true);
|
||||
const interval = setInterval(() => {
|
||||
setIsFlipped(prev => !prev);
|
||||
}, 8000);
|
||||
return () => clearInterval(interval);
|
||||
}, delay * 1000);
|
||||
return () => clearTimeout(initialTimeout);
|
||||
}, [delay]);
|
||||
|
||||
return (
|
||||
<div className="relative h-32 w-full perspective-[1000px] group cursor-pointer">
|
||||
<motion.div
|
||||
animate={{ rotateY: isFlipped ? 180 : 0 }}
|
||||
transition={{ duration: 0.6, type: "spring", stiffness: 260, damping: 20 }}
|
||||
className="relative w-full h-full preserve-3d"
|
||||
style={{ transformStyle: 'preserve-3d' }}
|
||||
>
|
||||
<div
|
||||
className="absolute inset-0 backface-hidden"
|
||||
style={{ backfaceVisibility: 'hidden', WebkitBackfaceVisibility: 'hidden' }}
|
||||
>
|
||||
<Card className="w-full h-full backdrop-blur-xl bg-white/70 border-white/50 shadow-xl shadow-gray-200/50 p-4 flex flex-col items-center justify-center hover:scale-105 transition-all duration-300">
|
||||
<div className={`w-10 h-10 mb-3 rounded-xl ${front.color} flex items-center justify-center`}>
|
||||
<front.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<p className="font-semibold text-gray-800 text-sm">{front.title}</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="absolute inset-0 backface-hidden"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden',
|
||||
WebkitBackfaceVisibility: 'hidden',
|
||||
transform: 'rotateY(180deg)'
|
||||
}}
|
||||
>
|
||||
<Card className="w-full h-full backdrop-blur-xl bg-white/80 border-white/60 shadow-xl shadow-blue-200/50 p-4 flex flex-col items-center justify-center hover:scale-105 transition-all duration-300">
|
||||
<div className={`w-10 h-10 mb-3 rounded-xl ${back.color} flex items-center justify-center`}>
|
||||
<back.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<p className="font-semibold text-gray-900 text-sm">{back.title}</p>
|
||||
</Card>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface HeroProps {
|
||||
t: any; // i18n translation function
|
||||
t: any;
|
||||
headingAs?: 'h1' | 'div';
|
||||
}
|
||||
|
||||
export const Hero: React.FC<HeroProps> = ({ t, headingAs = 'h1' }) => {
|
||||
const HeadingTag = headingAs;
|
||||
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-gradient-to-br from-blue-50 via-white to-purple-50 pt-12 pb-20">
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<style dangerouslySetInnerHTML={{__html:`
|
||||
@keyframes dotFloat{0%{transform:translateY(0) translateX(0);opacity:0}20%{opacity:1}80%{opacity:1}100%{transform:translateY(calc(var(--dy)*1px)) translateX(calc(var(--dx)*1px));opacity:0}}
|
||||
.hero-dot{position:absolute;border-radius:50%;animation:dotFloat linear infinite;}
|
||||
@media(prefers-reduced-motion:reduce){.hero-dot{animation:none}}
|
||||
`}}/>
|
||||
{([
|
||||
{size:4,x:15,y:80,dx:-30,dy:-120,dur:12,delay:0,color:'rgba(96,165,250,0.5)'},
|
||||
{size:3,x:35,y:90,dx:20,dy:-100,dur:15,delay:-3,color:'rgba(167,139,250,0.4)'},
|
||||
{size:5,x:55,y:85,dx:-10,dy:-130,dur:10,delay:-6,color:'rgba(96,165,250,0.3)'},
|
||||
{size:2,x:70,y:95,dx:30,dy:-110,dur:18,delay:-2,color:'rgba(192,132,252,0.5)'},
|
||||
{size:4,x:85,y:80,dx:-20,dy:-90,dur:14,delay:-8,color:'rgba(147,197,253,0.4)'},
|
||||
{size:3,x:25,y:70,dx:15,dy:-140,dur:11,delay:-5,color:'rgba(216,180,254,0.4)'},
|
||||
{size:6,x:60,y:75,dx:-25,dy:-100,dur:16,delay:-1,color:'rgba(96,165,250,0.25)'},
|
||||
{size:2,x:45,y:88,dx:10,dy:-120,dur:13,delay:-9,color:'rgba(167,139,250,0.5)'},
|
||||
{size:5,x:80,y:60,dx:-15,dy:-80,dur:20,delay:-4,color:'rgba(99,102,241,0.3)'},
|
||||
{size:3,x:10,y:50,dx:25,dy:-110,dur:17,delay:-7,color:'rgba(192,132,252,0.35)'},
|
||||
] as {size:number,x:number,y:number,dx:number,dy:number,dur:number,delay:number,color:string}[]).map((p,i)=>(
|
||||
<div key={i} className="hero-dot" style={{
|
||||
width:p.size,height:p.size,
|
||||
left:`${p.x}%`,top:`${p.y}%`,
|
||||
background:p.color,
|
||||
'--dx':p.dx,'--dy':p.dy,
|
||||
animationDuration:`${p.dur}s`,
|
||||
animationDelay:`${p.delay}s`,
|
||||
} as React.CSSProperties}/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
const containerjs = {
|
||||
hidden: { opacity: 0 },
|
||||
show: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const itemjs = {
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
show: { opacity: 1, y: 0 }
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-gradient-to-br from-blue-50 via-white to-purple-50 pt-12 pb-20">
|
||||
{/* Animated Background Orbs */}
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
{/* Orb 1 - Blue (top-left) */}
|
||||
<div className="absolute -top-24 -left-24 w-96 h-96 bg-blue-400/30 rounded-full blur-3xl animate-blob" />
|
||||
|
||||
{/* Orb 2 - Purple (top-right) */}
|
||||
<div className="absolute -top-12 -right-12 w-96 h-96 bg-purple-400/30 rounded-full blur-3xl animate-blob animation-delay-2000" />
|
||||
|
||||
{/* Orb 3 - Pink (bottom-left) */}
|
||||
<div className="absolute -bottom-24 -left-12 w-96 h-96 bg-pink-400/20 rounded-full blur-3xl animate-blob animation-delay-4000" />
|
||||
|
||||
{/* Orb 4 - Cyan (center-right) */}
|
||||
<div className="absolute top-1/2 -right-24 w-80 h-80 bg-cyan-400/20 rounded-full blur-3xl animate-blob animation-delay-6000" />
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl relative z-10">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left Content */}
|
||||
<div className="space-y-8">
|
||||
<Badge variant="info" className="inline-flex items-center space-x-2">
|
||||
<span>{t.hero.badge}</span>
|
||||
</Badge>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="space-y-6"
|
||||
>
|
||||
<HeadingTag className="text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl relative z-10">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left Content */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="w-6 h-0.5 bg-[#533afd] shrink-0" />
|
||||
<span className="text-[10px] font-semibold tracking-[0.1em] uppercase text-[#533afd]">{t.hero.badge}</span>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="space-y-4"
|
||||
>
|
||||
<HeadingTag
|
||||
className="font-extrabold text-[#061b31] leading-[0.92]"
|
||||
style={{ fontSize: 'clamp(2.75rem, 6vw, 5rem)' }}
|
||||
>
|
||||
{t.hero.title}
|
||||
</HeadingTag>
|
||||
|
||||
<p className="text-xl text-gray-600 leading-relaxed max-w-2xl">
|
||||
<p className="text-[0.9375rem] text-[#64748d] leading-[1.55] max-w-[44ch]">
|
||||
{t.hero.subtitle}
|
||||
</p>
|
||||
|
||||
<div className="space-y-3 pt-2">
|
||||
{t.hero.features.map((feature: string, index: number) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.2 + (index * 0.1) }}
|
||||
className="flex items-center space-x-3"
|
||||
>
|
||||
<div className="flex-shrink-0 w-6 h-6 bg-emerald-100 rounded-full flex items-center justify-center">
|
||||
<CheckCircle2 className="w-4 h-4 text-emerald-600" />
|
||||
</div>
|
||||
<span className="text-gray-700 font-medium">{feature}</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="flex flex-col sm:flex-row gap-4 pt-4"
|
||||
>
|
||||
<Link href="/signup">
|
||||
<Button size="lg" className="text-lg px-8 py-6 w-full sm:w-auto shadow-lg shadow-blue-500/25 hover:shadow-blue-500/40 transition-all duration-300">
|
||||
{t.hero.cta_primary}
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/#pricing">
|
||||
<Button variant="outline" size="lg" className="text-lg px-8 py-6 w-full sm:w-auto backdrop-blur-sm bg-white/50 border-gray-200 hover:bg-white/80 transition-all duration-300">
|
||||
{t.hero.cta_secondary}
|
||||
</Button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Right Preview Widget */}
|
||||
<div className="relative">
|
||||
<div className="relative perspective-[1000px]">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{[
|
||||
{
|
||||
front: { title: 'URL/Website', color: 'bg-blue-500/10 text-blue-600', icon: Globe },
|
||||
back: { title: 'PDF / Menu', color: 'bg-orange-500/10 text-orange-600', icon: FileText },
|
||||
delay: 3 // Starts at 3s
|
||||
},
|
||||
{
|
||||
front: { title: 'Contact Card', color: 'bg-purple-500/10 text-purple-600', icon: User },
|
||||
back: { title: 'Coupon / Deals', color: 'bg-red-500/10 text-red-600', icon: Ticket },
|
||||
delay: 5 // +2s
|
||||
},
|
||||
{
|
||||
front: { title: 'Location', color: 'bg-green-500/10 text-green-600', icon: MapPin },
|
||||
back: { title: 'App Store', color: 'bg-sky-500/10 text-sky-600', icon: Smartphone },
|
||||
delay: 7 // +2s
|
||||
},
|
||||
{
|
||||
front: { title: 'Phone Number', color: 'bg-pink-500/10 text-pink-600', icon: Phone },
|
||||
back: { title: 'Feedback', color: 'bg-yellow-500/10 text-yellow-600', icon: Star },
|
||||
delay: 9 // +2s
|
||||
},
|
||||
].map((card, index) => (
|
||||
<FlippingCard key={index} {...card} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Smooth Gradient Fade Transition */}
|
||||
<div className="absolute bottom-0 left-0 w-full h-32 bg-gradient-to-b from-transparent to-gray-50 pointer-events-none" />
|
||||
</section >
|
||||
);
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col gap-1.5 pt-1">
|
||||
{t.hero.features.map((feature: string, index: number) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.2 + (index * 0.1) }}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<div className="w-1 h-1 rounded-full bg-[#533afd] shrink-0" />
|
||||
<span className="text-[13px] text-[#273951] font-medium">{feature}</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="flex gap-4 items-center flex-wrap pt-1"
|
||||
>
|
||||
<Link href="/signup">
|
||||
<Button size="lg" style={{ padding: '14px 36px', fontSize: '1.0625rem', boxShadow: 'rgba(83,58,253,0.3) 0px 10px 28px -8px' }}>
|
||||
{t.hero.cta_primary}
|
||||
</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href="/#pricing"
|
||||
style={{ fontSize: '0.875rem', fontWeight: 500, color: '#533afd', textDecoration: 'underline', textUnderlineOffset: '3px' }}
|
||||
>
|
||||
{t.hero.cta_secondary}
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Right Preview Widget */}
|
||||
<div className="relative">
|
||||
<div className="relative perspective-[1000px]">
|
||||
{/* impeccable-variants-start eb49f55c */}
|
||||
<div data-impeccable-variants="eb49f55c" data-impeccable-variant-count="2" style={{ display: "contents" }}>
|
||||
{/* Original */}
|
||||
<div data-impeccable-variant="original">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{[
|
||||
{
|
||||
front: { title: 'URL/Website', color: 'bg-blue-500/10 text-blue-600', icon: Globe },
|
||||
back: { title: 'PDF / Menu', color: 'bg-orange-500/10 text-orange-600', icon: FileText },
|
||||
delay: 3
|
||||
},
|
||||
{
|
||||
front: { title: 'Contact Card', color: 'bg-purple-500/10 text-purple-600', icon: User },
|
||||
back: { title: 'Coupon / Deals', color: 'bg-red-500/10 text-red-600', icon: Ticket },
|
||||
delay: 5
|
||||
},
|
||||
{
|
||||
front: { title: 'Location', color: 'bg-green-500/10 text-green-600', icon: MapPin },
|
||||
back: { title: 'App Store', color: 'bg-sky-500/10 text-sky-600', icon: Smartphone },
|
||||
delay: 7
|
||||
},
|
||||
{
|
||||
front: { title: 'Phone Number', color: 'bg-pink-500/10 text-pink-600', icon: Phone },
|
||||
back: { title: 'Feedback', color: 'bg-yellow-500/10 text-yellow-600', icon: Star },
|
||||
delay: 9
|
||||
},
|
||||
].map((card, index) => (
|
||||
<FlippingCard key={index} {...card} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* Variants: insert below this line */}
|
||||
<style data-impeccable-css="eb49f55c">{`
|
||||
@scope ([data-impeccable-variant="1"]) {
|
||||
.qr-instrument-grid {
|
||||
--accent-strength: var(--p-accent-strength, 0.58);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid oklch(91% 0.018 260);
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(135deg, oklch(99% 0.006 260), oklch(96% 0.018 278)),
|
||||
repeating-linear-gradient(90deg, oklch(93% 0.012 260) 0 1px, transparent 1px 44px);
|
||||
box-shadow:
|
||||
rgba(50, 50, 93, 0.24) 0 30px 45px -30px,
|
||||
rgba(3, 3, 39, 0.10) 0 18px 36px -18px;
|
||||
}
|
||||
.qr-instrument-tile {
|
||||
position: relative;
|
||||
min-height: 126px;
|
||||
overflow: hidden;
|
||||
border: 1px solid oklch(88% 0.018 260);
|
||||
border-radius: 6px;
|
||||
background: oklch(99% 0.004 260 / 0.92);
|
||||
color: oklch(22% 0.07 260);
|
||||
box-shadow: rgba(23, 23, 23, 0.06) 0 3px 6px;
|
||||
transition: transform 220ms cubic-bezier(0.25, 1, 0.5, 1), border-color 220ms cubic-bezier(0.25, 1, 0.5, 1), box-shadow 220ms cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
.qr-instrument-tile:hover {
|
||||
transform: translateY(-3px);
|
||||
border-color: color-mix(in oklch, oklch(55% 0.25 280) calc(var(--accent-strength) * 100%), oklch(88% 0.018 260));
|
||||
box-shadow:
|
||||
rgba(50, 50, 93, 0.26) 0 24px 40px -26px,
|
||||
rgba(83, 58, 253, calc(var(--accent-strength) * 0.16)) 0 10px 24px -16px;
|
||||
}
|
||||
.qr-instrument-face {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
align-content: space-between;
|
||||
padding: 14px;
|
||||
}
|
||||
.qr-instrument-code {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
color: oklch(55% 0.04 260);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.qr-instrument-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 6px;
|
||||
color: oklch(52% 0.24 280);
|
||||
background: color-mix(in oklch, oklch(62% 0.25 280) calc(var(--accent-strength) * 16%), oklch(97% 0.01 260));
|
||||
}
|
||||
.qr-instrument-label {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
font-size: 13px;
|
||||
line-height: 1.15;
|
||||
font-weight: 600;
|
||||
color: oklch(20% 0.065 260);
|
||||
}
|
||||
.qr-instrument-spark {
|
||||
width: 34px;
|
||||
height: 12px;
|
||||
background: linear-gradient(90deg, transparent 0 12%, oklch(58% 0.24 280 / calc(var(--accent-strength) * 0.55)) 12% 18%, transparent 18% 34%, oklch(58% 0.24 280 / calc(var(--accent-strength) * 0.75)) 34% 44%, transparent 44% 100%);
|
||||
transform: skewX(-18deg);
|
||||
}
|
||||
.qr-instrument-tile:hover .qr-instrument-spark {
|
||||
animation: qrInstrumentPulse 720ms cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
@keyframes qrInstrumentPulse {
|
||||
from { opacity: 0.25; transform: translateX(-4px) skewX(-18deg); }
|
||||
to { opacity: 1; transform: translateX(0) skewX(-18deg); }
|
||||
}
|
||||
:scope[data-p-density="compact"] .qr-instrument-grid { gap: 8px; padding: 8px; }
|
||||
:scope[data-p-density="compact"] .qr-instrument-tile { min-height: 114px; }
|
||||
:scope[data-p-density="open"] .qr-instrument-grid { gap: 14px; padding: 12px; }
|
||||
:scope[data-p-density="open"] .qr-instrument-tile { min-height: 136px; }
|
||||
}
|
||||
@scope ([data-impeccable-variant="2"]) {
|
||||
.qr-rhythm-grid {
|
||||
--motion-depth: var(--p-motion-depth, 0.45);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.qr-rhythm-tile {
|
||||
position: relative;
|
||||
min-height: 126px;
|
||||
overflow: hidden;
|
||||
border: 1px solid oklch(90% 0.018 260);
|
||||
border-radius: 8px;
|
||||
background: oklch(99% 0.004 260 / 0.86);
|
||||
box-shadow:
|
||||
rgba(50, 50, 93, 0.18) 0 24px 34px -28px,
|
||||
rgba(0, 0, 0, 0.08) 0 14px 24px -18px;
|
||||
transition: transform 260ms cubic-bezier(0.25, 1, 0.5, 1), box-shadow 260ms cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
.qr-rhythm-tile:hover {
|
||||
transform: translateY(calc(var(--motion-depth) * -8px));
|
||||
box-shadow:
|
||||
rgba(50, 50, 93, 0.26) 0 30px 45px -30px,
|
||||
rgba(83, 58, 253, 0.14) 0 16px 30px -22px;
|
||||
}
|
||||
.qr-rhythm-tile::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 10px;
|
||||
border-radius: 6px;
|
||||
background:
|
||||
linear-gradient(90deg, oklch(55% 0.25 280 / 0.10), transparent 42%),
|
||||
repeating-linear-gradient(90deg, oklch(68% 0.16 280 / 0.32) 0 3px, transparent 3px 8px);
|
||||
clip-path: inset(72% 0 10% 0);
|
||||
opacity: calc(0.36 + var(--motion-depth) * 0.5);
|
||||
}
|
||||
.qr-rhythm-content {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.qr-rhythm-icon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 8px;
|
||||
color: oklch(48% 0.24 280);
|
||||
background: oklch(96% 0.025 280);
|
||||
box-shadow: inset oklch(100% 0.004 260 / 0.82) 0 1px 0;
|
||||
transition: transform 260ms cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
.qr-rhythm-tile:hover .qr-rhythm-icon {
|
||||
transform: translateY(calc(var(--motion-depth) * -5px)) scale(calc(1 + var(--motion-depth) * 0.05));
|
||||
}
|
||||
.qr-rhythm-label {
|
||||
color: oklch(20% 0.065 260);
|
||||
font-size: 13px;
|
||||
line-height: 1.12;
|
||||
font-weight: 600;
|
||||
}
|
||||
.qr-rhythm-meta {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 10px;
|
||||
color: oklch(58% 0.04 260);
|
||||
font-size: 9px;
|
||||
line-height: 1;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
:scope[data-p-signal="quiet"] .qr-rhythm-tile::before { opacity: 0.18; }
|
||||
:scope[data-p-signal="bright"] .qr-rhythm-tile::before { opacity: 0.78; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.qr-rhythm-tile,
|
||||
.qr-rhythm-icon,
|
||||
.qr-instrument-tile,
|
||||
.qr-instrument-spark {
|
||||
animation: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
<div
|
||||
data-impeccable-variant="1"
|
||||
data-impeccable-params='[
|
||||
{"id":"accent-strength","kind":"range","min":0.25,"max":0.9,"step":0.05,"default":0.58,"label":"Accent strength"},
|
||||
{"id":"density","kind":"steps","default":"balanced","label":"Density","options":[
|
||||
{"value":"compact","label":"Compact"},
|
||||
{"value":"balanced","label":"Balanced"},
|
||||
{"value":"open","label":"Open"}
|
||||
]}
|
||||
]'
|
||||
>
|
||||
<div className="qr-instrument-grid">
|
||||
{[
|
||||
{ code: '01', title: 'URL/Website', icon: Globe },
|
||||
{ code: '02', title: 'PDF / Menu', icon: FileText },
|
||||
{ code: '03', title: 'Contact Card', icon: User },
|
||||
{ code: '04', title: 'Coupon / Deals', icon: Ticket },
|
||||
].map((card) => (
|
||||
<div className="qr-instrument-tile" key={card.title}>
|
||||
<div className="qr-instrument-face">
|
||||
<div className="qr-instrument-code">
|
||||
<span>QR-{card.code}</span>
|
||||
<span>READY</span>
|
||||
</div>
|
||||
<div className="qr-instrument-icon">
|
||||
<card.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<div className="qr-instrument-label">
|
||||
<span>{card.title}</span>
|
||||
<span className="qr-instrument-spark" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-impeccable-variant="2"
|
||||
style={{ display: 'none' }}
|
||||
data-impeccable-params='[
|
||||
{"id":"motion-depth","kind":"range","min":0,"max":1,"step":0.05,"default":0.45,"label":"Motion depth"},
|
||||
{"id":"signal","kind":"steps","default":"calm","label":"Signal","options":[
|
||||
{"value":"quiet","label":"Quiet"},
|
||||
{"value":"calm","label":"Calm"},
|
||||
{"value":"bright","label":"Bright"}
|
||||
]}
|
||||
]'
|
||||
>
|
||||
<div className="qr-rhythm-grid">
|
||||
{[
|
||||
{ code: 'URL', title: 'URL/Website', icon: Globe },
|
||||
{ code: 'PDF', title: 'PDF / Menu', icon: FileText },
|
||||
{ code: 'LOC', title: 'Location', icon: MapPin },
|
||||
{ code: 'APP', title: 'App Store', icon: Smartphone },
|
||||
].map((card) => (
|
||||
<div className="qr-rhythm-tile" key={card.title}>
|
||||
<span className="qr-rhythm-meta">{card.code}</span>
|
||||
<div className="qr-rhythm-content">
|
||||
<div className="qr-rhythm-icon">
|
||||
<card.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<span className="qr-rhythm-label">{card.title}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end eb49f55c */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-0 left-0 w-full h-32 bg-gradient-to-b from-transparent to-gray-50 pointer-events-none" />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,48 +9,41 @@ export const ReprintCalculatorTeaser: React.FC = () => {
|
||||
return (
|
||||
<section className="py-24 bg-white relative overflow-hidden">
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
<div className="bg-gradient-to-br from-indigo-50 to-white border border-indigo-100 rounded-3xl p-8 md:p-12 lg:p-16 flex flex-col md:flex-row items-center justify-between gap-12 shadow-sm hover:shadow-md transition-shadow duration-500">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-12 bg-white border border-[#e5edf5] rounded-lg p-8 md:p-12 lg:p-16" style={{boxShadow:'rgba(50,50,93,0.12) 0px 8px 20px -8px'}}>
|
||||
|
||||
<div className="flex-1 text-center md:text-left">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-indigo-100 text-indigo-700 text-sm font-medium mb-6">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
<div className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded border border-[rgba(83,58,253,0.15)] mb-5" style={{background:'oklch(0.94 0.02 270)',color:'#533afd',fontSize:11,fontWeight:500,letterSpacing:'0.06em',textTransform:'uppercase'}}>
|
||||
<TrendingUp style={{width:12,height:12}} aria-hidden="true" />
|
||||
<span>ROI Calculator</span>
|
||||
</div>
|
||||
|
||||
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold text-slate-900 mb-6 leading-tight">
|
||||
Are you burning budget on <br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-indigo-600 to-blue-600">Static Reprints?</span>
|
||||
<h2 style={{fontSize:'clamp(1.875rem,4vw,3rem)',fontWeight:700,lineHeight:1.1,color:'#061b31',marginBottom:'1.25rem',letterSpacing:'-0.02em'}}>
|
||||
Are you burning budget on{' '}
|
||||
<span style={{color:'#533afd'}}>Static Reprints?</span>
|
||||
</h2>
|
||||
|
||||
<p className="text-slate-600 text-lg md:text-xl max-w-xl mx-auto md:mx-0 leading-relaxed mb-8">
|
||||
<p style={{color:'#64748d',fontSize:'1.0625rem',lineHeight:1.6,maxWidth:'52ch',margin:'0 auto 2rem'}}>
|
||||
Find out exactly how much you can save by switching to dynamic QR codes. Our calculator reveals your savings potential in seconds.
|
||||
</p>
|
||||
|
||||
<Link href="/reprint-calculator">
|
||||
<Button
|
||||
size="lg"
|
||||
variant="primary"
|
||||
className="h-14 px-8 text-lg hover:translate-x-1 transition-transform"
|
||||
>
|
||||
Calculate Savings <ArrowRight className="w-5 h-5 ml-2" />
|
||||
<Button size="lg" className="h-14 px-8">
|
||||
Calculate Savings <ArrowRight className="w-4 h-4 ml-2" aria-hidden="true" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0 w-full md:w-auto flex justify-center md:justify-end">
|
||||
<div className="relative group">
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-blue-500 rounded-2xl blur opacity-20 group-hover:opacity-40 transition duration-1000 group-hover:duration-200"></div>
|
||||
<div className="relative bg-white rounded-xl p-8 border border-slate-100 w-full max-w-xs text-center shadow-lg">
|
||||
<div className="w-16 h-16 bg-indigo-50 rounded-2xl flex items-center justify-center mx-auto mb-6 text-indigo-600">
|
||||
<Calculator className="w-8 h-8" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-900 mb-2">Cost Analysis</h3>
|
||||
<p className="text-slate-500 text-sm mb-6">
|
||||
Enter your print volume and update frequency to see your hidden costs.
|
||||
</p>
|
||||
<div className="h-1.5 w-full bg-slate-100 rounded-full overflow-hidden">
|
||||
<div className="h-full w-2/3 bg-indigo-500 rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
<div className="bg-white border border-[#e5edf5] rounded-lg p-7 w-full max-w-[260px] text-center" style={{boxShadow:'rgba(50,50,93,0.25) 0px 20px 40px -20px,rgba(0,0,0,0.08) 0px 10px 20px -10px'}}>
|
||||
<div className="flex items-center justify-center mx-auto mb-4 text-[#533afd]" style={{width:44,height:44,background:'oklch(0.94 0.02 270)',borderRadius:6}}>
|
||||
<Calculator style={{width:22,height:22}} aria-hidden="true" />
|
||||
</div>
|
||||
<h3 className="font-semibold text-[#061b31] mb-2" style={{fontSize:'0.9375rem'}}>Cost Analysis</h3>
|
||||
<p className="text-[#64748d] leading-relaxed" style={{fontSize:'0.8125rem'}}>
|
||||
Enter your print volume and update frequency to see your hidden costs.
|
||||
</p>
|
||||
<div className="mt-5 h-0.5 w-full bg-[#e5edf5] rounded-sm overflow-hidden">
|
||||
<div className="h-full w-2/3 bg-[#533afd] rounded-sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user