feat: add Remotion animated demos to landing page, show all pricing plans

Replace static mockups with Remotion Player animations:
- SchemaDemo: 3x3 grid with spring animations, highlight, part info card
- DashboardDemo: 5-scene product flow (VIN input, vehicle info, categories, schema, OEM codes)
- Both support dark/light theme via isDark prop, autoplay + loop

Expand pricing section from single teaser to all 4 plans with horizontal scroll on mobile.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sase Dev
2026-02-15 07:35:41 +00:00
parent 25278d386a
commit bcc614a0c8
5 changed files with 1020 additions and 98 deletions

View File

@@ -0,0 +1,673 @@
import {
AbsoluteFill,
useCurrentFrame,
useVideoConfig,
interpolate,
spring,
Sequence,
} from "remotion";
function getColors(isDark: boolean) {
return {
bg: isDark ? "#1a1a1a" : "#f5f5f5",
fg: isDark ? "#fafafa" : "#0a0a0a",
muted: isDark ? "#262626" : "#f5f5f5",
mutedFg: isDark ? "#a3a3a3" : "#737373",
border: isDark ? "#262626" : "#e5e5e5",
emerald: "#10b981",
surface: isDark ? "#1f1f1f" : "#ffffff",
surfaceAlt: isDark ? "#171717" : "#fafafa",
};
}
const VIN_TEXT = "WVWZZZ1JZ3W...";
const SIDEBAR_ITEMS = [
"Dashboard",
"Şase Arama",
"Katalog",
"Şemalar",
"Ayarlar",
];
const CATEGORIES = ["Motor", "Şasi", "Elektrik", "Karoseri", "Fren"];
const PARTS = [
{ code: "1J0 820 803F", name: "Klima Kompresörü" },
{ code: "1J0 698 151G", name: "Fren Balatası" },
{ code: "1J0 819 031A", name: "Kalorifer Motoru" },
];
// ─── Scene 1: VIN Input ─────────────────────────────────────────────────────
const VinInputScene: React.FC<{ isDark: boolean }> = ({ isDark }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const c = getColors(isDark);
// Sidebar slides in
const sidebarSlide = spring({
frame,
fps,
config: { damping: 14, stiffness: 180 },
});
const sidebarX = interpolate(sidebarSlide, [0, 1], [-140, 0]);
// Typewriter effect starts at frame 30
const typeStart = 30;
const charsToShow = Math.min(
Math.max(0, Math.floor((frame - typeStart) / 3)),
VIN_TEXT.length,
);
const typedText = VIN_TEXT.slice(0, charsToShow);
return (
<AbsoluteFill style={{ flexDirection: "row" }}>
{/* Sidebar */}
<div
style={{
width: 140,
backgroundColor: c.surfaceAlt,
borderRight: `1px solid ${c.border}`,
padding: 12,
transform: `translateX(${sidebarX}px)`,
}}
>
{SIDEBAR_ITEMS.map((item, i) => (
<div
key={item}
style={{
padding: "6px 10px",
borderRadius: 6,
fontSize: 10,
fontFamily: "system-ui, sans-serif",
color: i === 1 ? c.fg : c.mutedFg,
backgroundColor: i === 1 ? c.muted : "transparent",
marginBottom: 2,
}}
>
{item}
</div>
))}
</div>
{/* Main content */}
<div style={{ flex: 1, padding: 20 }}>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
backgroundColor: c.muted,
borderRadius: 8,
padding: "8px 12px",
border: `1px solid ${c.border}`,
}}
>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke={c.mutedFg}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
<span
style={{
fontFamily: "monospace",
fontSize: 11,
color: typedText ? c.fg : c.mutedFg,
}}
>
{typedText || "Şase numarası girin..."}
</span>
{/* Blinking cursor */}
{frame > typeStart && charsToShow < VIN_TEXT.length && (
<span
style={{
width: 1,
height: 14,
backgroundColor: c.fg,
opacity: Math.sin(frame * 0.3) > 0 ? 1 : 0,
}}
/>
)}
</div>
</div>
</AbsoluteFill>
);
};
// ─── Scene 2: Vehicle Info ──────────────────────────────────────────────────
const VehicleInfoScene: React.FC<{ isDark: boolean }> = ({ isDark }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const c = getColors(isDark);
const cardSpring = spring({
frame: frame - 10,
fps,
config: { damping: 12, stiffness: 160 },
});
const translateY = interpolate(cardSpring, [0, 1], [50, 0]);
const opacity = interpolate(cardSpring, [0, 1], [0, 1]);
return (
<AbsoluteFill style={{ flexDirection: "row" }}>
{/* Sidebar (static) */}
<div
style={{
width: 140,
backgroundColor: c.surfaceAlt,
borderRight: `1px solid ${c.border}`,
padding: 12,
}}
>
{SIDEBAR_ITEMS.map((item, i) => (
<div
key={item}
style={{
padding: "6px 10px",
borderRadius: 6,
fontSize: 10,
fontFamily: "system-ui, sans-serif",
color: i === 1 ? c.fg : c.mutedFg,
backgroundColor: i === 1 ? c.muted : "transparent",
marginBottom: 2,
}}
>
{item}
</div>
))}
</div>
{/* Main content */}
<div style={{ flex: 1, padding: 20 }}>
{/* VIN bar (filled) */}
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
backgroundColor: c.muted,
borderRadius: 8,
padding: "8px 12px",
border: `1px solid ${c.border}`,
marginBottom: 16,
}}
>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke={c.mutedFg}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
<span
style={{ fontFamily: "monospace", fontSize: 11, color: c.fg }}
>
{VIN_TEXT}
</span>
</div>
{/* Vehicle card */}
<div
style={{
backgroundColor: c.surface,
border: `1px solid ${c.emerald}40`,
borderRadius: 10,
padding: 16,
transform: `translateY(${translateY}px)`,
opacity,
}}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginBottom: 8,
}}
>
<div
style={{
width: 8,
height: 8,
borderRadius: "50%",
backgroundColor: c.emerald,
}}
/>
<span
style={{
fontSize: 13,
fontWeight: 600,
fontFamily: "system-ui, sans-serif",
color: c.fg,
}}
>
Volkswagen Golf
</span>
</div>
<div
style={{
display: "flex",
gap: 12,
fontSize: 10,
color: c.mutedFg,
fontFamily: "system-ui, sans-serif",
}}
>
<span>2003</span>
<span>1.6 FSI</span>
<span>Hatchback</span>
</div>
</div>
</div>
</AbsoluteFill>
);
};
// ─── Scene 3: Category Selection ────────────────────────────────────────────
const CategoryScene: React.FC<{ isDark: boolean }> = ({ isDark }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const c = getColors(isDark);
return (
<AbsoluteFill style={{ flexDirection: "row" }}>
{/* Sidebar */}
<div
style={{
width: 140,
backgroundColor: c.surfaceAlt,
borderRight: `1px solid ${c.border}`,
padding: 12,
}}
>
{SIDEBAR_ITEMS.map((item, i) => (
<div
key={item}
style={{
padding: "6px 10px",
borderRadius: 6,
fontSize: 10,
fontFamily: "system-ui, sans-serif",
color: i === 2 ? c.fg : c.mutedFg,
backgroundColor: i === 2 ? c.muted : "transparent",
marginBottom: 2,
}}
>
{item}
</div>
))}
</div>
{/* Main content */}
<div style={{ flex: 1, padding: 20 }}>
<div
style={{
fontSize: 12,
fontWeight: 600,
color: c.fg,
marginBottom: 12,
fontFamily: "system-ui, sans-serif",
}}
>
Kategori Seçin
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
{CATEGORIES.map((cat, i) => {
const delay = i * 5;
const itemSpring = spring({
frame: frame - delay,
fps,
config: { damping: 14, stiffness: 200 },
});
const itemOpacity = interpolate(itemSpring, [0, 1], [0, 1]);
const translateX = interpolate(itemSpring, [0, 1], [20, 0]);
// Highlight "Motor" after all items appear
const isMotor = i === 0;
const highlightProgress = isMotor
? interpolate(frame, [50, 65], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
})
: 0;
return (
<div
key={cat}
style={{
padding: "8px 12px",
borderRadius: 8,
fontSize: 11,
fontFamily: "system-ui, sans-serif",
color:
highlightProgress > 0.5 ? c.fg : c.mutedFg,
backgroundColor:
highlightProgress > 0.5
? isDark
? "#064e3b"
: "#d1fae5"
: c.muted,
border: `1px solid ${highlightProgress > 0.5 ? c.emerald : c.border}`,
opacity: itemOpacity,
transform: `translateX(${translateX}px)`,
}}
>
{cat}
</div>
);
})}
</div>
</div>
</AbsoluteFill>
);
};
// ─── Scene 4: Schema View ───────────────────────────────────────────────────
const SchemaViewScene: React.FC<{ isDark: boolean }> = ({ isDark }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const c = getColors(isDark);
const HOTSPOTS = [
{ x: "30%", y: "35%" },
{ x: "60%", y: "25%" },
{ x: "45%", y: "60%" },
{ x: "70%", y: "55%" },
];
return (
<AbsoluteFill style={{ flexDirection: "row" }}>
{/* Sidebar */}
<div
style={{
width: 140,
backgroundColor: c.surfaceAlt,
borderRight: `1px solid ${c.border}`,
padding: 12,
}}
>
{SIDEBAR_ITEMS.map((item, i) => (
<div
key={item}
style={{
padding: "6px 10px",
borderRadius: 6,
fontSize: 10,
fontFamily: "system-ui, sans-serif",
color: i === 3 ? c.fg : c.mutedFg,
backgroundColor: i === 3 ? c.muted : "transparent",
marginBottom: 2,
}}
>
{item}
</div>
))}
</div>
{/* Main content — schema placeholder */}
<div style={{ flex: 1, padding: 20, position: "relative" }}>
<div
style={{
fontSize: 12,
fontWeight: 600,
color: c.fg,
marginBottom: 12,
fontFamily: "system-ui, sans-serif",
}}
>
Motor Şema Görünümü
</div>
<div
style={{
position: "relative",
backgroundColor: c.muted,
borderRadius: 10,
border: `1px solid ${c.border}`,
height: "75%",
}}
>
{/* Faint grid lines for schema feel */}
{[0.25, 0.5, 0.75].map((pct) => (
<div
key={`h-${pct}`}
style={{
position: "absolute",
top: `${pct * 100}%`,
left: 0,
right: 0,
height: 1,
backgroundColor: c.border,
opacity: 0.5,
}}
/>
))}
{[0.25, 0.5, 0.75].map((pct) => (
<div
key={`v-${pct}`}
style={{
position: "absolute",
left: `${pct * 100}%`,
top: 0,
bottom: 0,
width: 1,
backgroundColor: c.border,
opacity: 0.5,
}}
/>
))}
{/* Hotspots */}
{HOTSPOTS.map((spot, i) => {
const delay = i * 15;
const pulseSpring = spring({
frame: frame - delay,
fps,
config: { damping: 10, stiffness: 120 },
});
const scale = interpolate(pulseSpring, [0, 1], [0, 1]);
// Pulsing ring
const ringScale = 1 + Math.sin((frame - delay) * 0.15) * 0.3;
const ringOpacity = scale > 0.5 ? 0.3 + Math.sin((frame - delay) * 0.15) * 0.2 : 0;
return (
<div
key={i}
style={{
position: "absolute",
left: spot.x,
top: spot.y,
transform: `translate(-50%, -50%) scale(${scale})`,
}}
>
{/* Pulse ring */}
<div
style={{
position: "absolute",
width: 24,
height: 24,
borderRadius: "50%",
border: `2px solid ${c.emerald}`,
opacity: ringOpacity,
transform: `translate(-50%, -50%) scale(${ringScale})`,
left: "50%",
top: "50%",
}}
/>
{/* Dot */}
<div
style={{
width: 10,
height: 10,
borderRadius: "50%",
backgroundColor: c.emerald,
border: `2px solid ${c.surface}`,
}}
/>
</div>
);
})}
</div>
</div>
</AbsoluteFill>
);
};
// ─── Scene 5: OEM Codes ─────────────────────────────────────────────────────
const OemCodesScene: React.FC<{ isDark: boolean }> = ({ isDark }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const c = getColors(isDark);
// Fade out at the end
const fadeOut = interpolate(frame, [70, 90], [1, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<AbsoluteFill style={{ flexDirection: "row", opacity: fadeOut }}>
{/* Sidebar */}
<div
style={{
width: 140,
backgroundColor: c.surfaceAlt,
borderRight: `1px solid ${c.border}`,
padding: 12,
}}
>
{SIDEBAR_ITEMS.map((item, i) => (
<div
key={item}
style={{
padding: "6px 10px",
borderRadius: 6,
fontSize: 10,
fontFamily: "system-ui, sans-serif",
color: i === 2 ? c.fg : c.mutedFg,
backgroundColor: i === 2 ? c.muted : "transparent",
marginBottom: 2,
}}
>
{item}
</div>
))}
</div>
{/* Main content */}
<div style={{ flex: 1, padding: 20 }}>
<div
style={{
fontSize: 12,
fontWeight: 600,
color: c.fg,
marginBottom: 12,
fontFamily: "system-ui, sans-serif",
}}
>
OEM Parça Listesi
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
{PARTS.map((part, i) => {
const delay = i * 8;
const slideSpring = spring({
frame: frame - delay,
fps,
config: { damping: 14, stiffness: 180 },
});
const translateX = interpolate(slideSpring, [0, 1], [40, 0]);
const opacity = interpolate(slideSpring, [0, 1], [0, 1]);
return (
<div
key={part.code}
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "8px 12px",
borderRadius: 8,
backgroundColor: c.muted,
border: `1px solid ${c.border}`,
transform: `translateX(${translateX}px)`,
opacity,
}}
>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<span
style={{
fontFamily: "monospace",
fontSize: 11,
fontWeight: 600,
color: c.fg,
}}
>
{part.code}
</span>
<span
style={{
fontSize: 10,
color: c.mutedFg,
fontFamily: "system-ui, sans-serif",
}}
>
{part.name}
</span>
</div>
<div
style={{
width: 6,
height: 6,
borderRadius: "50%",
backgroundColor: c.emerald,
}}
/>
</div>
);
})}
</div>
</div>
</AbsoluteFill>
);
};
// ─── Main Composition ───────────────────────────────────────────────────────
export const DashboardDemo: React.FC<{ isDark: boolean }> = ({ isDark }) => {
const c = getColors(isDark);
return (
<AbsoluteFill style={{ backgroundColor: c.bg }}>
<Sequence from={0} durationInFrames={90}>
<VinInputScene isDark={isDark} />
</Sequence>
<Sequence from={90} durationInFrames={90}>
<VehicleInfoScene isDark={isDark} />
</Sequence>
<Sequence from={180} durationInFrames={90}>
<CategoryScene isDark={isDark} />
</Sequence>
<Sequence from={270} durationInFrames={90}>
<SchemaViewScene isDark={isDark} />
</Sequence>
<Sequence from={360} durationInFrames={90}>
<OemCodesScene isDark={isDark} />
</Sequence>
</AbsoluteFill>
);
};

View File

@@ -0,0 +1,188 @@
import {
AbsoluteFill,
useCurrentFrame,
useVideoConfig,
interpolate,
spring,
Sequence,
} from "remotion";
const PARTS = ["P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9"];
function getColors(isDark: boolean) {
return {
bg: isDark ? "#1a1a1a" : "#f5f5f5",
fg: isDark ? "#fafafa" : "#0a0a0a",
muted: isDark ? "#262626" : "#f5f5f5",
mutedFg: isDark ? "#a3a3a3" : "#737373",
border: isDark ? "#262626" : "#e5e5e5",
emerald: "#10b981",
surface: isDark ? "#1f1f1f" : "#ffffff",
};
}
export const SchemaDemo: React.FC<{ isDark: boolean }> = ({ isDark }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const c = getColors(isDark);
return (
<AbsoluteFill style={{ backgroundColor: c.bg, padding: 24 }}>
{/* Scene 1: Grid appears (0-60) */}
<Sequence from={0} durationInFrames={240}>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gap: 8,
width: "100%",
maxWidth: 280,
margin: "0 auto",
}}
>
{PARTS.map((label, i) => {
const delay = i * 3;
const scale = spring({
frame: frame - delay,
fps,
config: { damping: 12, stiffness: 200 },
});
// Scene 2: Highlight center box (60-120)
const isCenter = i === 4;
const highlightProgress = isCenter
? interpolate(frame, [60, 80], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
})
: 0;
const boxScale = scale * (1 + highlightProgress * 0.08);
const borderColor = isCenter
? interpolate(highlightProgress, [0, 1], [0, 1]) > 0.5
? c.emerald
: c.border
: c.border;
const boxBg = isCenter
? interpolate(highlightProgress, [0, 1], [0, 1]) > 0.5
? isDark
? "#064e3b"
: "#d1fae5"
: c.muted
: c.muted;
const textColor = isCenter && highlightProgress > 0.5 ? c.fg : c.mutedFg;
// Scene 4: Fade out (180-240)
const fadeOut = interpolate(frame, [200, 240], [1, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<div
key={label}
style={{
aspectRatio: "1",
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: 8,
border: `1.5px solid ${borderColor}`,
backgroundColor: boxBg,
fontSize: 12,
fontFamily: "system-ui, sans-serif",
color: textColor,
transform: `scale(${boxScale})`,
opacity: fadeOut,
}}
>
{isCenter && highlightProgress > 0.5 ? "▶" : label}
</div>
);
})}
</div>
</Sequence>
{/* Scene 3: Part info card (120-180) */}
<Sequence from={0} durationInFrames={240}>
<AbsoluteFill
style={{
top: "auto",
bottom: 20,
left: 24,
right: 24,
height: "auto",
display: "flex",
justifyContent: "center",
}}
>
{(() => {
const slideUp = spring({
frame: frame - 120,
fps,
config: { damping: 14, stiffness: 180 },
});
const translateY = interpolate(slideUp, [0, 1], [40, 0]);
const cardOpacity = interpolate(frame, [120, 135], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const fadeOut = interpolate(frame, [200, 240], [1, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<div
style={{
backgroundColor: c.surface,
border: `1px solid ${c.border}`,
borderRadius: 10,
padding: "10px 16px",
display: "flex",
alignItems: "center",
gap: 12,
opacity: cardOpacity * fadeOut,
transform: `translateY(${translateY}px)`,
maxWidth: 260,
width: "100%",
}}
>
<div
style={{
width: 8,
height: 8,
borderRadius: "50%",
backgroundColor: c.emerald,
flexShrink: 0,
}}
/>
<div>
<div
style={{
fontSize: 11,
fontFamily: "monospace",
color: c.fg,
fontWeight: 600,
}}
>
1J0 820 803F
</div>
<div
style={{
fontSize: 10,
color: c.mutedFg,
marginTop: 2,
}}
>
Klima Kompresörü
</div>
</div>
</div>
);
})()}
</AbsoluteFill>
</Sequence>
</AbsoluteFill>
);
};

View File

@@ -24,6 +24,9 @@ import {
Gift,
} from "lucide-react";
import { useState, useEffect } from "react";
import { Player } from "@remotion/player";
import { SchemaDemo } from "@/remotion/SchemaDemo";
import { DashboardDemo } from "@/remotion/DashboardDemo";
import { getUserSettings, setUserSetting } from "@/lib/user-settings";
// ─── DATA ────────────────────────────────────────────────────────────────────
@@ -107,22 +110,18 @@ const FEATURES = [
"Alt kategori derinliği",
],
mockupTitle: "İnteraktif Şema — Motor Bölgesi",
mockupContent: (
<div className="p-4">
<div className="grid grid-cols-3 gap-2">
{Array.from({ length: 9 }).map((_, i) => (
<div
key={i}
className={`flex aspect-square items-center justify-center rounded-lg border border-border text-xs text-muted-foreground ${i === 4 ? "border-foreground/30 bg-muted text-foreground" : "bg-muted/50"}`}
>
{i === 4 ? "▶" : `P${i + 1}`}
</div>
))}
</div>
<div className="mt-3 text-center text-xs text-muted-foreground">
Parça seçmek için tıklayın
</div>
</div>
mockupContent: (isDark: boolean) => (
<Player
component={SchemaDemo}
compositionWidth={400}
compositionHeight={300}
durationInFrames={240}
fps={30}
loop
autoPlay
style={{ width: "100%" }}
inputProps={{ isDark }}
/>
),
},
{
@@ -705,7 +704,9 @@ function HomePage() {
{/* Browser mockup */}
<div className="flex-1">
<BrowserFrame title={feature.mockupTitle}>
{feature.mockupContent}
{typeof feature.mockupContent === "function"
? feature.mockupContent(isDark)
: feature.mockupContent}
</BrowserFrame>
</div>
</div>
@@ -929,35 +930,17 @@ function HomePage() {
<div className="relative mt-12">
<BrowserFrame title="sase.tr/dashboard">
<div className="flex min-h-[300px] sm:min-h-[400px]">
{/* Sidebar mock */}
<div className="hidden w-48 border-r border-border p-4 sm:block">
<div className="space-y-2">
{["Dashboard", "Şase Arama", "Katalog", "Şemalar", "Ayarlar"].map(
(item) => (
<div
key={item}
className={`rounded-md px-3 py-1.5 text-xs ${item === "Şase Arama" ? "bg-muted text-foreground" : "text-muted-foreground"}`}
>
{item}
</div>
)
)}
</div>
</div>
{/* Content grid mock */}
<div className="flex-1 p-4">
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
{Array.from({ length: 6 }).map((_, i) => (
<div
key={i}
className="aspect-[4/3] rounded-lg bg-border"
/>
))}
</div>
</div>
</div>
<Player
component={DashboardDemo}
compositionWidth={960}
compositionHeight={540}
durationInFrames={450}
fps={30}
loop
autoPlay
style={{ width: "100%" }}
inputProps={{ isDark }}
/>
</BrowserFrame>
{/* Floating labels */}
@@ -980,61 +963,108 @@ function HomePage() {
</div>
</section>
{/* ─── 10. PRICING TEASER (YENİ) ────────────────────────────────── */}
{/* ─── 10. PRICING ─────────────────────────────────────────────── */}
<section className="px-4 py-24 sm:px-6">
<div className="mx-auto max-w-4xl text-center">
<SectionBadge>Fiyatlandırma</SectionBadge>
<h2 className="mt-4 font-[family-name:var(--font-display)] text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl">
Günde 7 TL'den Başlayan Fiyatlar
</h2>
<p className="mx-auto mt-4 max-w-xl text-muted-foreground">
Tek bir yanlış parça iadesinin maliyetinden daha az.
</p>
{/* Popular plan card */}
<div className="mx-auto mt-12 max-w-sm overflow-hidden rounded-2xl border-2 border-foreground/20 bg-surface p-8">
<div className="mb-2 inline-flex rounded-full bg-foreground px-3 py-1 text-xs font-medium text-background">
En Popüler
</div>
<h3 className="font-[family-name:var(--font-display)] text-xl font-bold">
2 Marka
</h3>
<div className="mt-3">
<span className="font-[family-name:var(--font-display)] text-4xl font-bold">
350 TL
</span>
<span className="text-muted-foreground">/ay</span>
</div>
<ul className="mt-6 space-y-2 text-left text-sm">
{[
"2 marka seçimi",
"Sınırsız şase arama",
"Parça kataloğu & şema",
"Öncelikli destek",
].map((f) => (
<li
key={f}
className="flex items-center gap-2 text-muted-foreground"
>
<CheckCircle2 className="size-4 shrink-0 text-emerald-500" />
{f}
</li>
))}
</ul>
<Link to="/register">
<Button className="mt-6 w-full rounded-full bg-foreground text-background hover:bg-foreground/90">
7 Gün Ücretsiz Deneyin
</Button>
</Link>
<div className="mx-auto max-w-6xl">
<div className="text-center">
<SectionBadge>Fiyatlandırma</SectionBadge>
<h2 className="mt-4 font-[family-name:var(--font-display)] text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl">
Günde 7 TL'den Başlayan Fiyatlar
</h2>
<p className="mx-auto mt-4 max-w-xl text-muted-foreground">
Tek bir yanlış parça iadesinin maliyetinden daha az. Tüm fiyatlar KDV dahildir.
</p>
</div>
<Link
to="/pricing"
className="mt-6 inline-flex items-center gap-1 text-sm text-muted-foreground transition hover:text-foreground"
>
Tüm planları gör
<ArrowRight className="size-3.5" />
</Link>
{/* Horizontally scrollable on mobile, grid on desktop */}
<div className="mt-12 flex gap-4 overflow-x-auto pb-4 sm:pb-0 lg:grid lg:grid-cols-4 lg:overflow-visible">
{[
{
name: "1 Marka",
description: "Tek marka için yedek parça erişimi",
price: "200",
yearly: "2.000",
features: ["1 marka seçimi", "Sınırsız VIN arama", "Parça kataloğu", "Şema görüntüleyici"],
},
{
name: "2 Marka",
description: "İki farklı marka için erişim",
price: "350",
yearly: "3.500",
popular: true,
features: ["2 marka seçimi", "Sınırsız VIN arama", "Parça kataloğu", "Şema görüntüleyici", "Öncelikli destek"],
},
{
name: "3 Marka",
description: "Üç marka için kapsamlı erişim",
price: "500",
yearly: "5.000",
features: ["3 marka seçimi", "Sınırsız VIN arama", "Parça kataloğu", "Şema görüntüleyici", "Öncelikli destek"],
},
{
name: "Full Paket",
description: "Tüm markalara sınırsız erişim",
price: "999",
yearly: "9.990",
features: ["Tüm markalar", "Sınırsız VIN arama", "Parça kataloğu", "Şema görüntüleyici", "Öncelikli destek", "OEM parça arama"],
},
].map((plan) => (
<div
key={plan.name}
className={`flex min-w-[260px] flex-1 flex-col rounded-2xl border bg-surface p-6 sm:p-8 ${plan.popular ? "border-2 border-foreground/20" : "border-border"}`}
>
{plan.popular && (
<div className="mb-3 inline-flex self-start rounded-full bg-foreground px-3 py-1 text-xs font-medium text-background">
En Popüler
</div>
)}
<h3 className="font-[family-name:var(--font-display)] text-xl font-bold">
{plan.name}
</h3>
<p className="mt-1 text-sm text-muted-foreground">
{plan.description}
</p>
<div className="mt-4">
<span className="font-[family-name:var(--font-display)] text-4xl font-bold">
{plan.price} TL
</span>
<span className="text-muted-foreground">/ay</span>
</div>
<p className="mt-1 text-xs text-muted-foreground">
veya {plan.yearly} TL/yıl
</p>
<ul className="mt-6 flex-1 space-y-2 text-sm">
{plan.features.map((f) => (
<li
key={f}
className="flex items-center gap-2 text-muted-foreground"
>
<CheckCircle2 className="size-4 shrink-0 text-emerald-500" />
{f}
</li>
))}
</ul>
<Link to="/register" className="mt-6">
<Button
className={`w-full rounded-full ${plan.popular ? "bg-foreground text-background hover:bg-foreground/90" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"}`}
variant={plan.popular ? "default" : "outline"}
>
7 Gün Ücretsiz Deneyin
</Button>
</Link>
</div>
))}
</div>
<div className="mt-8 text-center">
<Link
to="/pricing"
className="inline-flex items-center gap-1 text-sm text-muted-foreground transition hover:text-foreground"
>
Detaylı karşılaştırma
<ArrowRight className="size-3.5" />
</Link>
</div>
</div>
</section>