Skip to content

Commit 63f080c

Browse files
authored
Merge pull request #53 from will-lp1/staging
feat: new landing page feature cards
2 parents 17b0d3e + b622474 commit 63f080c

File tree

1 file changed

+137
-16
lines changed

1 file changed

+137
-16
lines changed

apps/snow-leopard/app/page.tsx

Lines changed: 137 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,49 @@ import Image from 'next/image';
1212
import { motion, useInView } from "framer-motion";
1313
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip';
1414
import Script from 'next/script';
15+
import { Switch } from "@/components/ui/switch";
1516

1617
const crimson = Crimson_Text({
1718
weight: ["400", "700"],
1819
subsets: ["latin"],
1920
display: "swap",
2021
});
2122

23+
const StyleToggleDemo = ({ inView }: { inView: boolean }) => {
24+
const [isEnabled, setIsEnabled] = useState(false);
25+
useEffect(() => {
26+
if (inView) {
27+
const timer = setTimeout(() => setIsEnabled(true), 1500);
28+
return () => clearTimeout(timer);
29+
}
30+
}, [inView]);
31+
32+
return (
33+
<div className="rounded-md border p-4 w-full">
34+
<div className="flex items-center justify-between">
35+
<span className="text-xs font-medium">Apply Writer Style</span>
36+
<Switch checked={isEnabled} onCheckedChange={setIsEnabled} className="scale-110" />
37+
</div>
38+
</div>
39+
);
40+
};
41+
2242
export default function Home() {
2343
const router = useRouter();
2444
const featuresRef = useRef<HTMLElement>(null);
2545
const isFeaturesInView = useInView(featuresRef, { once: true, amount: 0.3 });
26-
// Individual card in-view refs for staggered, per-card animations
2746
const card1Ref = useRef<HTMLDivElement>(null);
2847
const card2Ref = useRef<HTMLDivElement>(null);
2948
const card3Ref = useRef<HTMLDivElement>(null);
49+
const card4Ref = useRef<HTMLDivElement>(null);
50+
const card5Ref = useRef<HTMLDivElement>(null);
51+
const card6Ref = useRef<HTMLDivElement>(null);
3052
const card1InView = useInView(card1Ref, { once: true, amount: 0.5 });
3153
const card2InView = useInView(card2Ref, { once: true, amount: 0.5 });
3254
const card3InView = useInView(card3Ref, { once: true, amount: 0.5 });
33-
34-
const socialProofRef = useRef<HTMLElement>(null);
35-
const isSocialProofInView = useInView(socialProofRef, { once: true, amount: 0.2 });
36-
const proof1Ref = useRef<HTMLDivElement>(null);
37-
const proof2Ref = useRef<HTMLDivElement>(null);
38-
const proof3Ref = useRef<HTMLDivElement>(null);
39-
const proof1InView = useInView(proof1Ref, { once: true, amount: 0.5 });
40-
const proof2InView = useInView(proof2Ref, { once: true, amount: 0.5 });
41-
const proof3InView = useInView(proof3Ref, { once: true, amount: 0.5 });
55+
const card4InView = useInView(card4Ref, { once: true, amount: 0.5 });
56+
const card5InView = useInView(card5Ref, { once: true, amount: 0.5 });
57+
const card6InView = useInView(card6Ref, { once: true, amount: 0.5 });
4258

4359
const [hasSession, setHasSession] = useState<boolean>(false);
4460

@@ -70,6 +86,9 @@ export default function Home() {
7086
}
7187
};
7288

89+
const modelNames = ["Llama", "Kimi", "Deepseek", "Claude"] as const;
90+
const proIndex = 3;
91+
7392
return (
7493
<div className="relative min-h-screen overflow-hidden bg-background text-foreground">
7594
{/* Header */}
@@ -183,9 +202,9 @@ export default function Home() {
183202
initial={{ opacity: 0, y: 20 }}
184203
animate={card1InView ? { opacity: 1, y: 0 } : {}}
185204
transition={{ duration: 0.6, delay: 0.1 }}
186-
className="w-full"
205+
className="w-full h-full"
187206
>
188-
<Card className="flex flex-col min-h-[320px] rounded-xl overflow-visible">
207+
<Card className="h-full flex flex-col min-h-[320px] rounded-xl overflow-visible">
189208
<CardHeader className="p-6 text-base font-medium">
190209
Real-time Inline Suggestions
191210
</CardHeader>
@@ -207,9 +226,9 @@ export default function Home() {
207226
initial={{ opacity: 0, y: 20 }}
208227
animate={card2InView ? { opacity: 1, y: 0 } : {}}
209228
transition={{ duration: 0.6, delay: 0.2 }}
210-
className="w-full"
229+
className="w-full h-full"
211230
>
212-
<Card className="flex flex-col min-h-[320px] rounded-xl overflow-visible">
231+
<Card className="h-full flex flex-col min-h-[320px] rounded-xl overflow-visible">
213232
<CardHeader className="p-6 text-base font-medium">
214233
Powerful Selection Edits
215234
</CardHeader>
@@ -247,9 +266,9 @@ export default function Home() {
247266
initial={{ opacity: 0, y: 20 }}
248267
animate={card3InView ? { opacity: 1, y: 0 } : {}}
249268
transition={{ duration: 0.6, delay: 0.3 }}
250-
className="w-full"
269+
className="w-full h-full"
251270
>
252-
<Card className="flex flex-col min-h-[320px] rounded-xl overflow-visible">
271+
<Card className="h-full flex flex-col min-h-[320px] rounded-xl overflow-visible">
253272
<CardHeader className="p-6 text-base font-medium">
254273
Instant Synonym Finder
255274
</CardHeader>
@@ -269,6 +288,108 @@ export default function Home() {
269288
</CardContent>
270289
</Card>
271290
</motion.div>
291+
292+
{/* Card 4: AI Writing That Sounds Like You */}
293+
<motion.div
294+
ref={card4Ref}
295+
initial={{ opacity: 0, y: 20 }}
296+
animate={card4InView ? { opacity: 1, y: 0 } : {}}
297+
transition={{ duration: 0.6, delay: 0.2 }}
298+
className="w-full h-full"
299+
>
300+
<Card className="h-full flex flex-col min-h-[320px] rounded-xl overflow-visible">
301+
<CardHeader className="p-6 text-base font-medium">
302+
AI Writing That Sounds Like You
303+
</CardHeader>
304+
<CardContent className="p-6 text-sm text-muted-foreground flex-grow flex flex-col justify-between items-center">
305+
<div className="w-full flex flex-col items-center flex-grow justify-center">
306+
<StyleToggleDemo inView={card4InView} />
307+
</div>
308+
<p className="text-center w-full mt-8">Trained on your writing to apply your unique style.</p>
309+
</CardContent>
310+
</Card>
311+
</motion.div>
312+
313+
{/* Card 5: Access Premium Models */}
314+
<motion.div
315+
ref={card5Ref}
316+
initial={{ opacity: 0, y: 20 }}
317+
animate={card5InView ? { opacity: 1, y: 0 } : {}}
318+
transition={{ duration: 0.6, delay: 0.25 }}
319+
className="w-full h-full"
320+
>
321+
<Card className="h-full flex flex-col min-h-[320px] rounded-xl">
322+
<CardHeader className="p-6 text-base font-medium">
323+
Access Premium Models
324+
</CardHeader>
325+
<CardContent className="p-6 text-sm text-muted-foreground flex-grow flex flex-col justify-between items-center">
326+
<div className="w-full flex items-center justify-center gap-0" style={{ height: '112px' }}>
327+
{modelNames.map((name, i) => {
328+
const mid = (modelNames.length - 1) / 2;
329+
const offset = i - mid;
330+
const rot = offset * 8;
331+
const y = Math.abs(offset) * 8;
332+
return (
333+
<motion.div
334+
key={name}
335+
initial={{ opacity: 0, rotate: 0, y: 0 }}
336+
animate={card5InView ? { opacity: 1, rotate: rot, y, transition: { delay: 0.2 + i * 0.1, type: 'spring', stiffness: 140, damping: 15 } } : {}}
337+
className="w-20 h-28 bg-background border border-border rounded-lg flex items-center justify-center mx-[-4px] shadow-sm relative"
338+
style={{ zIndex: 10 - Math.abs(offset) }}
339+
>
340+
{i === proIndex && (
341+
<span className="absolute top-1 right-1 bg-accent text-accent-foreground text-[10px] px-1 rounded">Pro</span>
342+
)}
343+
<span className="text-xs font-medium">{name}</span>
344+
</motion.div>
345+
);
346+
})}
347+
</div>
348+
<p className="text-sm text-muted-foreground text-center w-full mt-8">
349+
Unlimited, free access to the best AI models.
350+
</p>
351+
</CardContent>
352+
</Card>
353+
</motion.div>
354+
355+
{/* Card 6: One-Click Publish & Share */}
356+
<motion.div
357+
ref={card6Ref}
358+
initial={{ opacity: 0, y: 20 }}
359+
animate={card6InView ? { opacity: 1, y: 0 } : {}}
360+
transition={{ duration: 0.6, delay: 0.3 }}
361+
className="w-full h-full"
362+
>
363+
<Card className="h-full flex flex-col min-h-[320px] rounded-xl">
364+
<CardHeader className="p-6 text-base font-medium">
365+
One-Click Publish & Share
366+
</CardHeader>
367+
<CardContent className="p-6 text-sm text-muted-foreground flex-grow flex flex-col justify-between items-center">
368+
<div className="w-full flex flex-col items-center">
369+
{/* Mini page preview */}
370+
<div className="relative w-44 h-32 rounded-lg border border-border bg-background shadow-sm overflow-hidden">
371+
{/* URL bar */}
372+
<div className="h-5 bg-muted flex items-center px-2 text-[9px] text-muted-foreground/90 font-mono gap-1">
373+
<span className="truncate">you/your-post</span>
374+
</div>
375+
{/* Content preview */}
376+
<div className="p-3 space-y-1">
377+
<div className="h-2.5 bg-muted rounded w-2/3" />
378+
<div className="h-2.5 bg-muted rounded w-full" />
379+
<div className="h-2.5 bg-muted rounded w-5/6" />
380+
</div>
381+
{/* Chat bubble */}
382+
<div className="absolute bottom-2 right-2 w-8 h-4 rounded-full bg-primary flex items-center justify-center text-[6px] text-primary-foreground shadow">
383+
Ask Leo
384+
</div>
385+
</div>
386+
</div>
387+
<p className="text-sm text-muted-foreground text-center w-full mt-8">
388+
Publish in one click & share with AI chat support.
389+
</p>
390+
</CardContent>
391+
</Card>
392+
</motion.div>
272393
</div>
273394
</div>
274395
</section>

0 commit comments

Comments
 (0)