press releases

This commit is contained in:
Timo Knuth
2026-01-27 12:29:44 +01:00
parent be5db36b7f
commit 4dc7c29134
19 changed files with 606 additions and 16 deletions

View File

@@ -50,7 +50,7 @@ export default function AdBanner({
session.user.plan === 'LIFETIME'
);
if (shouldExclude) return null;
useEffect(() => {
// Don't load if loading session or if user is paid
@@ -92,6 +92,8 @@ export default function AdBanner({
// Don't render anything while session is loading
if (status === 'loading') return null;
if (shouldExclude) return null;
return (
<div
className={`ad-container flex justify-center items-center overflow-hidden transition-opacity duration-300 ${adFilled ? 'opacity-100' : 'opacity-0 h-0'} ${className}`}

View File

@@ -105,7 +105,7 @@ const AIComingSoonBanner = () => {
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-blue-100 mb-4 animate-pulse">
<Sparkles className="w-4 h-4 text-blue-600" />
<span className="text-sm font-medium text-blue-700">
Coming Soon
The Future is Here
</span>
</div>

View File

@@ -14,11 +14,14 @@ import { Button } from '@/components/ui/Button';
import { ReprintCalculatorTeaser } from '@/components/marketing/ReprintCalculatorTeaser';
import { ScrollToTop } from '@/components/ui/ScrollToTop';
import { FreeToolsGrid } from '@/components/marketing/FreeToolsGrid';
import { Testimonials } from '@/components/marketing/Testimonials';
import { getFeaturedTestimonials } from '@/lib/testimonial-data';
import en from '@/i18n/en.json';
export default function HomePageClient() {
// Always use English for marketing pages
const t = en;
const featuredTestimonials = getFeaturedTestimonials();
const industries = [
'Restaurant Chain',
@@ -41,6 +44,9 @@ export default function HomePageClient() {
{/* Free Tools Grid */}
<FreeToolsGrid />
{/* Testimonials Section */}
<Testimonials testimonials={featuredTestimonials} />
<React.Fragment>
<StaticVsDynamic t={t} />
<ReprintCalculatorTeaser />

View File

@@ -29,6 +29,15 @@ export const StatsStrip: React.FC<StatsStripProps> = ({ t }) => {
</div>
))}
</div>
{/* Market Context - Citation / AEO Trust Signal */}
<div className="mt-12 pt-8 border-t border-gray-100 text-center">
<p className="text-sm text-gray-500 max-w-3xl mx-auto leading-relaxed">
Market Context: The global QR code payment market is projected to grow from <span className="font-semibold text-gray-700">$15.9B (2025)</span> to <span className="font-semibold text-gray-700">$38B (2030)</span> <a href="https://finance.yahoo.com/news/analysis-global-qr-code-payments-155300360.html" target="_blank" rel="nofollow external" className="text-blue-600 hover:underline decoration-blue-200 underline-offset-2">(Yahoo Finance)</a>.
<span className="mx-2"></span>
94% of marketers increased their QR code usage in 2025 <a href="https://bitly.com/blog/qr-code-statistics/" target="_blank" rel="nofollow external" className="text-blue-600 hover:underline decoration-blue-200 underline-offset-2">(Bitly)</a>.
</p>
</div>
</div>
</section>
);

View File

@@ -0,0 +1,132 @@
'use client';
import React from 'react';
import { motion } from 'framer-motion';
import { Star, CheckCircle } from 'lucide-react';
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
import type { Testimonial } from '@/lib/types';
interface TestimonialsProps {
testimonials: Testimonial[];
title?: string;
subtitle?: string;
showAll?: boolean;
}
export const Testimonials: React.FC<TestimonialsProps> = ({
testimonials,
title = "What Our Customers Say",
subtitle = "Real experiences from businesses using QR Master",
showAll = false
}) => {
const displayTestimonials = showAll ? testimonials : testimonials.slice(0, 3);
const renderStars = (rating: number) => {
return (
<div className="flex gap-1" aria-label={`${rating} out of 5 stars`}>
{[...Array(5)].map((_, index) => (
<Star
key={index}
className={`w-5 h-5 ${index < rating
? 'fill-yellow-400 text-yellow-400'
: 'fill-gray-200 text-gray-200'
}`}
/>
))}
</div>
);
};
return (
<section className="py-16 bg-white">
<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 }}
transition={{ duration: 0.5 }}
className="text-center mb-12"
>
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
{title}
</h2>
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
{subtitle}
</p>
</motion.div>
<div className={`grid gap-8 ${displayTestimonials.length === 1
? 'grid-cols-1 max-w-2xl mx-auto'
: displayTestimonials.length === 2
? 'grid-cols-1 md:grid-cols-2 max-w-4xl mx-auto'
: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3'
}`}>
{displayTestimonials.map((testimonial, index) => (
<motion.div
key={testimonial.id}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<Card hover className="h-full flex flex-col">
<CardHeader>
<div className="flex items-center justify-between mb-3">
{renderStars(testimonial.rating)}
{testimonial.verified && (
<span className="inline-flex items-center gap-1 px-2 py-1 bg-green-100 text-green-700 text-xs font-medium rounded-full">
<CheckCircle className="w-3 h-3" />
Verified
</span>
)}
</div>
<h3 className="text-xl font-semibold text-gray-900 mb-2">
{testimonial.title}
</h3>
</CardHeader>
<CardContent className="flex-grow">
<p className="text-gray-700 leading-relaxed mb-6">
{testimonial.content}
</p>
<div className="border-t border-gray-200 pt-4 mt-auto">
<div className="flex flex-col">
<span className="font-semibold text-gray-900">
{testimonial.author.name}
</span>
<div className="text-sm text-gray-600">
{testimonial.author.company && (
<span>{testimonial.author.company}</span>
)}
{testimonial.author.company && testimonial.author.location && (
<span> </span>
)}
{testimonial.author.location && (
<span>{testimonial.author.location}</span>
)}
</div>
<span className="text-xs text-gray-500 mt-1">
{testimonial.date}
</span>
</div>
</div>
</CardContent>
</Card>
</motion.div>
))}
</div>
{!showAll && (
<div className="mt-12 text-center">
<a href="/testimonials" className="inline-flex items-center text-blue-600 font-semibold hover:text-blue-700 transition-colors">
See all reviews
<svg className="w-4 h-4 ml-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</a>
</div>
)}
</div>
</section >
);
};

View File

@@ -44,6 +44,8 @@ export function Footer({ variant = 'marketing', t }: FooterProps) {
<ul className={`space-y-2 ${isDashboard ? 'text-gray-500' : 'text-gray-400'}`}>
<li><Link href="/features" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.features}</Link></li>
<li><Link href="/about" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>About</Link></li>
<li><Link href="/press" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Press</Link></li>
<li><Link href="/testimonials" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Testimonials</Link></li>
<li><Link href="/authors/timo" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Timo Knuth (Author)</Link></li>
<li><Link href="/#pricing" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.pricing}</Link></li>
<li><Link href="/qr-code-tracking" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>QR Analytics</Link></li>