Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import Hero from "@/features/landing-page/hero";
import { ValuePropositionSection } from "@/features/landing-page/value-proposition";
import { ThreeStepExplainerSection } from "@/features/landing-page/three-step-explainer";
import { FeaturesGridSection } from "@/features/landing-page/features-grid";
import { StatsSection } from "@/features/landing-page/stats";
import { CreateSurvey } from "@/features/landing-page/create-survey";
import { TrustedBySection } from "@/features/landing-page/trusted-by";
import { TestimonialsSection } from "@/features/landing-page/testimonials";
import { PricingSection } from "@/features/landing-page/pricing";
import { FaqSection } from "@/features/landing-page/faq-section";
import { CtaBanner } from "@/features/landing-page/cta-banner";
import Footer from "@/features/landing-page/footer";
import Navbar from "@/features/landing-page/navbar";
import SmoothScrollWrapper from "@/components/smooth-scroll-wrapper";
Expand All @@ -12,9 +19,16 @@ export default function Home() {
<div className="min-h-screen bg-bg-dark">
<Navbar />
<Hero />
<TrustedBySection />
<ValuePropositionSection />
<ThreeStepExplainerSection />
<FeaturesGridSection />
<StatsSection />
<CreateSurvey />
<TestimonialsSection />
<PricingSection />
<FaqSection />
<CtaBanner />
<Footer />
</div>
</SmoothScrollWrapper>
Expand Down
83 changes: 83 additions & 0 deletions src/features/landing-page/cta-banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use client";

import { motion, type Variants } from "framer-motion";
import Button from "@/components/button";
import Link from "next/link";

const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.1,
},
},
};

const itemVariants: Variants = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.7, ease: "easeOut" },
},
};

export const CtaBanner = () => {
return (
<motion.section
className="relative py-16 sm:py-24 md:py-32 px-4 sm:px-6 lg:px-8 overflow-hidden"
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.3 }}
variants={containerVariants}
>
<div
className="absolute inset-0 pointer-events-none"
style={{
backgroundImage: "url('/bg-gradient.svg')",
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
opacity: 0.4,
}}
/>

<motion.div
className="relative z-10 max-w-[48rem] mx-auto text-center"
variants={containerVariants}
>
<motion.h2
className="font-semibold md:font-bold lg:font-extrabold text-2xl sm:text-3xl md:text-4xl lg:text-[3rem] xl:text-[4rem] text-white leading-tight mb-4 sm:mb-6"
variants={itemVariants}
>
Ready to Start{" "}
<span className="text-primary-purple">Earning?</span>
</motion.h2>

<motion.p
className="font-normal text-sm sm:text-base md:text-lg lg:text-[1.25rem] leading-6 sm:leading-7 md:leading-8 text-text-gray max-w-[39.9375rem] mx-auto mb-8 sm:mb-10"
variants={itemVariants}
>
Join thousands of participants and researchers on the leading
decentralized survey platform. Start earning STRK tokens today.
</motion.p>

<motion.div
className="flex flex-col sm:flex-row gap-4 justify-center"
variants={itemVariants}
>
<Link href="/">
<Button text="Start Earning" className="w-full sm:w-auto" />
</Link>
<Link href="/">
<button className="px-8 py-4 border-2 border-border-light rounded-xl font-bold text-base text-white hover:bg-border-light hover:text-bg-dark transition-colors duration-300 w-full sm:w-auto">
Create a Survey
</button>
</Link>
</motion.div>
</motion.div>
</motion.section>
);
};
124 changes: 124 additions & 0 deletions src/features/landing-page/faq-section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"use client";

import { useState } from "react";
import { motion, AnimatePresence, type Variants } from "framer-motion";
import { ChevronDown } from "lucide-react";

const faqs = [
{
question: "What is Survexa?",
answer:
"Survexa is a decentralized survey platform built on Starknet. It connects researchers with participants, rewarding respondents with STRK tokens for sharing their opinions and insights.",
},
{
question: "How do I earn STRK tokens?",
answer:
"Simply browse available surveys, complete them with thoughtful responses, and receive STRK tokens directly to your wallet. The reward amount varies based on survey length and complexity.",
},
{
question: "How do I create a survey?",
answer:
"Sign up as a researcher, use our intuitive survey builder to create your questions, set your reward budget, and publish. Our platform handles participant matching and token distribution automatically.",
},
{
question: "Is my data secure?",
answer:
"Yes. Survexa leverages blockchain technology for transparency while keeping individual responses anonymous. Your data is encrypted and you maintain full control over your information.",
},
{
question: "What types of surveys can I create?",
answer:
"You can create multiple-choice, open-ended, rating scale, and matrix surveys. Our custom templates make it easy to get started quickly for any research need.",
},
];

const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2,
},
},
};

const itemVariants: Variants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: "easeOut" },
},
};

export const FaqSection = () => {
const [openIndex, setOpenIndex] = useState<number | null>(null);

return (
<motion.section
className="relative py-16 sm:py-24 md:py-32 px-4 sm:px-6 lg:px-8"
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
variants={containerVariants}
>
<div className="max-w-[48rem] mx-auto">
<motion.div className="text-center mb-12 sm:mb-16" variants={itemVariants}>
<h2 className="font-semibold md:font-bold lg:font-extrabold text-2xl sm:text-3xl md:text-4xl lg:text-[3rem] xl:text-[4rem] text-white leading-tight mb-4 sm:mb-6">
Frequently Asked{" "}
<span className="text-primary-purple">Questions</span>
</h2>
<p className="font-normal text-sm sm:text-base md:text-lg lg:text-[1.25rem] leading-6 sm:leading-7 md:leading-8 text-text-gray">
Everything you need to know about Survexa.
</p>
</motion.div>

<motion.div className="space-y-3 sm:space-y-4" variants={containerVariants}>
{faqs.map((faq, index) => (
<motion.div
key={index}
className="rounded-xl border border-border-gray bg-bg-faq-main overflow-hidden hover:border-primary-purple/20 transition-colors duration-300"
variants={itemVariants}
>
<button
className="w-full flex items-center justify-between p-4 sm:p-5 text-left cursor-pointer"
onClick={() =>
setOpenIndex(openIndex === index ? null : index)
}
aria-expanded={openIndex === index}
>
<span className="text-white font-medium text-sm sm:text-base pr-4">
{faq.question}
</span>
<motion.span
animate={{ rotate: openIndex === index ? 180 : 0 }}
transition={{ duration: 0.3 }}
className="flex-shrink-0"
>
<ChevronDown className="w-5 h-5 text-text-gray" />
</motion.span>
</button>
<AnimatePresence>
{openIndex === index && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
>
<div className="px-4 sm:px-5 pb-4 sm:pb-5">
<p className="text-text-gray text-sm sm:text-base leading-relaxed">
{faq.answer}
</p>
</div>
</motion.div>
)}
</AnimatePresence>
</motion.div>
))}
</motion.div>
</div>
</motion.section>
);
};
135 changes: 135 additions & 0 deletions src/features/landing-page/features-grid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"use client";

import { motion, type Variants } from "framer-motion";
import {
BarChart3Icon,
ShieldCheckIcon,
ZapIcon,
UsersIcon,
GlobeIcon,
BrainCircuitIcon,
} from "lucide-react";

const features = [
{
icon: ZapIcon,
title: "Lightning Fast Setup",
description:
"Create and launch surveys in minutes with our intuitive builder. No coding required.",
},
{
icon: BarChart3Icon,
title: "Real-Time Analytics",
description:
"Track responses as they come in with live dashboards, charts, and exportable reports.",
},
{
icon: ShieldCheckIcon,
title: "Blockchain Verified",
description:
"Every response is verified on-chain, ensuring data integrity and preventing fraud.",
},
{
icon: UsersIcon,
title: "Targeted Audiences",
description:
"Reach the right respondents with smart demographic and interest-based targeting.",
},
{
icon: GlobeIcon,
title: "Global Reach",
description:
"Access participants worldwide with multi-language support and localized surveys.",
},
{
icon: BrainCircuitIcon,
title: "AI-Powered Insights",
description:
"Get intelligent summaries and trend analysis powered by advanced machine learning.",
},
];

const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.15,
delayChildren: 0.1,
},
},
};

const cardVariants: Variants = {
hidden: { opacity: 0, y: 30, scale: 0.95 },
visible: {
opacity: 1,
y: 0,
scale: 1,
transition: { duration: 0.5, ease: "easeOut" },
},
};

const headerVariants: Variants = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.8, ease: "easeOut" },
},
};

export const FeaturesGridSection = () => {
return (
<motion.section
className="relative py-16 sm:py-24 md:py-32 px-4 sm:px-6 lg:px-8 overflow-hidden"
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
variants={containerVariants}
>
<div className="relative z-10 max-w-7xl mx-auto">
<motion.div
className="text-center mb-12 sm:mb-16"
variants={headerVariants}
>
<h2 className="font-semibold md:font-bold lg:font-extrabold text-2xl sm:text-3xl md:text-4xl lg:text-[3rem] xl:text-[4rem] text-white leading-tight mb-4 sm:mb-6">
Everything You Need to{" "}
<span className="text-primary-purple">Succeed</span>
</h2>
<p className="font-normal text-sm sm:text-base md:text-lg lg:text-[1.25rem] leading-6 sm:leading-7 md:leading-8 text-text-gray max-w-[39.9375rem] mx-auto">
Powerful features designed to help you create better surveys and
gather meaningful insights.
</p>
</motion.div>

<motion.div
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8"
variants={containerVariants}
>
{features.map((feature, index) => {
const Icon = feature.icon;
return (
<motion.div
key={index}
className="group bg-bg-card/50 backdrop-blur-sm rounded-2xl p-6 sm:p-8 border border-border-gray hover:border-primary-purple/30 transition-all duration-300"
variants={cardVariants}
whileHover={{ y: -4, transition: { duration: 0.2 } }}
>
<div className="w-12 h-12 rounded-xl bg-primary-purple/10 flex items-center justify-center mb-5 group-hover:bg-primary-purple/20 transition-colors duration-300">
<Icon className="w-6 h-6 text-primary-purple" />
</div>
<h3 className="text-white font-bold text-lg sm:text-xl mb-3">
{feature.title}
</h3>
<p className="text-text-gray text-sm sm:text-base leading-relaxed">
{feature.description}
</p>
</motion.div>
);
})}
</motion.div>
</div>
</motion.section>
);
};
Loading