diff --git a/package.json b/package.json
index ed209bf..9b00bf1 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
"astro": "^6.4.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "embla-carousel-react": "^8.6.0",
"lucide-react": "^1.17.0",
"radix-ui": "^1.5.0",
"react": "^19.2.7",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e50089d..b085433 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,6 +29,9 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
+ embla-carousel-react:
+ specifier: ^8.6.0
+ version: 8.6.0(react@19.2.7)
lucide-react:
specifier: ^1.17.0
version: 1.17.0(react@19.2.7)
@@ -2078,6 +2081,19 @@ packages:
electron-to-chromium@1.5.368:
resolution: {integrity: sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==}
+ embla-carousel-react@8.6.0:
+ resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
+ embla-carousel-reactive-utils@8.6.0:
+ resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==}
+ peerDependencies:
+ embla-carousel: 8.6.0
+
+ embla-carousel@8.6.0:
+ resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==}
+
emoji-regex@10.6.0:
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
@@ -5800,6 +5816,18 @@ snapshots:
electron-to-chromium@1.5.368: {}
+ embla-carousel-react@8.6.0(react@19.2.7):
+ dependencies:
+ embla-carousel: 8.6.0
+ embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0)
+ react: 19.2.7
+
+ embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0):
+ dependencies:
+ embla-carousel: 8.6.0
+
+ embla-carousel@8.6.0: {}
+
emoji-regex@10.6.0: {}
emoji-regex@8.0.0: {}
diff --git a/src/assets/cta-bg.jpg b/src/assets/cta-bg.jpg
new file mode 100644
index 0000000..b58f5fd
Binary files /dev/null and b/src/assets/cta-bg.jpg differ
diff --git a/src/assets/image.png b/src/assets/image.png
new file mode 100644
index 0000000..74b9d73
Binary files /dev/null and b/src/assets/image.png differ
diff --git a/src/components/contact/ContactForm.astro b/src/components/contact/ContactForm.astro
new file mode 100644
index 0000000..57802b6
--- /dev/null
+++ b/src/components/contact/ContactForm.astro
@@ -0,0 +1,67 @@
+---
+import { buttonVariants } from "@/components/ui/button";
+import { LucideIcon } from "@/components/ui/lucide-icon";
+import { cn } from "@/lib/utils";
+---
+
+
diff --git a/src/components/contact/SupportBanner.astro b/src/components/contact/SupportBanner.astro
new file mode 100644
index 0000000..26cef33
--- /dev/null
+++ b/src/components/contact/SupportBanner.astro
@@ -0,0 +1,30 @@
+---
+import ActionLink from "@/components/landing/ActionLink.astro";
+import { LucideIcon } from "@/components/ui/lucide-icon";
+---
+
+
+
+
+
+
+
+
نیاز به راهنمایی سریع دارید؟
+
+ تیم پشتیبانی ما آماده کمک به شماست.
+
+
+
+
+
+ ارسال پیام به پشتیبانی
+
+
+
diff --git a/src/components/landing/ActionLink.astro b/src/components/landing/ActionLink.astro
new file mode 100644
index 0000000..6ffdce0
--- /dev/null
+++ b/src/components/landing/ActionLink.astro
@@ -0,0 +1,22 @@
+---
+import { buttonVariants } from "@/components/ui/button";
+import { cn } from "@/lib/utils";
+
+interface Props {
+ href: string;
+ variant?: "default" | "outline" | "secondary" | "ghost";
+ size?: "default" | "lg" | "sm";
+ class?: string;
+}
+
+const {
+ href,
+ variant = "default",
+ size = "default",
+ class: className,
+} = Astro.props;
+---
+
+
+
+
diff --git a/src/components/landing/CtaSection.astro b/src/components/landing/CtaSection.astro
new file mode 100644
index 0000000..af2866c
--- /dev/null
+++ b/src/components/landing/CtaSection.astro
@@ -0,0 +1,69 @@
+---
+import ActionLink from "@/components/landing/ActionLink.astro";
+import { LucideIcon } from "@/components/ui/lucide-icon";
+import ctaBg from "@/assets/cta-bg.jpg";
+---
+
+
+
+

+
+
+
+
+
+
+

+
+
+
+
+ از امروز باشگاه خود را حرفهایتر مدیریت کنید
+
+
+ به ۵۴ باشگاه دیگر در نکستا ملحق شوید.
+
+
+
+
+
+
+ شروع کنید
+
+
+
+ تماس با ما
+
+
+
+
+
diff --git a/src/components/landing/FeaturesSection.astro b/src/components/landing/FeaturesSection.astro
new file mode 100644
index 0000000..84b5950
--- /dev/null
+++ b/src/components/landing/FeaturesSection.astro
@@ -0,0 +1,34 @@
+---
+import SectionShell from "@/components/landing/SectionShell.astro";
+import SectionHeading from "@/components/landing/SectionHeading.astro";
+import { LucideIcon } from "@/components/ui/lucide-icon";
+import { features } from "@/lib/landing-data";
+---
+
+
+
+
+
+ {
+ features.map((feature) => (
+
+
+
+
+
+
+
+ {feature.title}
+
+
+ {feature.description}
+
+
+
+ ))
+ }
+
+
diff --git a/src/components/landing/HeroSection.astro b/src/components/landing/HeroSection.astro
new file mode 100644
index 0000000..15e5b43
--- /dev/null
+++ b/src/components/landing/HeroSection.astro
@@ -0,0 +1,68 @@
+---
+import ActionLink from "@/components/landing/ActionLink.astro";
+import heroImage from "@/assets/image.png";
+---
+
+
+
+
+
+
+
+
+ مدیریت هوشمند باشگاه،
+ سادهتر از همیشه
+
+
+
+ یک پلتفرم کامل برای مدیریت اعضا، مربیان، برنامههای تمرینی و تمام
+ فعالیتهای باشگاه.
+
+
+
+
+ همین الان امتحان کن!
+
+
+ مشاهده پلنها
+
+
+
+
+
+
+
+

+
+
+
diff --git a/src/components/landing/PricingSection.astro b/src/components/landing/PricingSection.astro
new file mode 100644
index 0000000..80a3fda
--- /dev/null
+++ b/src/components/landing/PricingSection.astro
@@ -0,0 +1,73 @@
+---
+import SectionShell from "@/components/landing/SectionShell.astro";
+import SectionHeading from "@/components/landing/SectionHeading.astro";
+import { LucideIcon } from "@/components/ui/lucide-icon";
+import { plans } from "@/lib/landing-data";
+---
+
+
+
+
+
+ {
+ plans.map((plan) => (
+
+ {plan.popular && (
+
+ محبوبترین
+
+ )}
+
+
+
{plan.name}
+
+ {plan.price}
+ /
+ {plan.period}
+
+
+
+
+ -
+
+ {plan.memberLimit}
+
+
+ {plan.extras.map((feature) => (
+ -
+
+
+
+ {feature}
+
+ ))}
+
+
+
+ {plan.description}
+
+
+ ))
+ }
+
+
+
+
diff --git a/src/components/landing/SectionHeading.astro b/src/components/landing/SectionHeading.astro
new file mode 100644
index 0000000..fbd26e7
--- /dev/null
+++ b/src/components/landing/SectionHeading.astro
@@ -0,0 +1,27 @@
+---
+interface Props {
+ title: string;
+ subtitle?: string;
+ centered?: boolean;
+}
+
+const { title, subtitle, centered = true } = Astro.props;
+---
+
+
+
+ {title}
+
+ {
+ subtitle && (
+
+ {subtitle}
+
+ )
+ }
+
diff --git a/src/components/landing/SectionShell.astro b/src/components/landing/SectionShell.astro
new file mode 100644
index 0000000..1ba4776
--- /dev/null
+++ b/src/components/landing/SectionShell.astro
@@ -0,0 +1,30 @@
+---
+interface Props {
+ variant?: "default" | "elevated";
+ id?: string;
+ class?: string;
+ containerClass?: string;
+}
+
+const {
+ variant = "default",
+ id,
+ class: className,
+ containerClass,
+} = Astro.props;
+---
+
+
diff --git a/src/components/landing/StatsSection.astro b/src/components/landing/StatsSection.astro
new file mode 100644
index 0000000..48f9833
--- /dev/null
+++ b/src/components/landing/StatsSection.astro
@@ -0,0 +1,34 @@
+---
+import SectionShell from "@/components/landing/SectionShell.astro";
+import { HeroSocialProof } from "@/components/landing/hero-social-proof";
+import { LucideIcon } from "@/components/ui/lucide-icon";
+import { stats } from "@/lib/landing-data";
+---
+
+
+
+
+
+
+
+ {
+ stats.map((stat) => (
+
+
+
+
+
+
{stat.value}
+
{stat.label}
+
+
+ ))
+ }
+
+
diff --git a/src/components/landing/StepsSection.astro b/src/components/landing/StepsSection.astro
new file mode 100644
index 0000000..286f261
--- /dev/null
+++ b/src/components/landing/StepsSection.astro
@@ -0,0 +1,36 @@
+---
+import SectionShell from "@/components/landing/SectionShell.astro";
+import SectionHeading from "@/components/landing/SectionHeading.astro";
+import { steps } from "@/lib/landing-data";
+import { cn } from "@/lib/utils";
+---
+
+
+
+
+
+ {
+ steps.map((step, i) => (
+
+
+ {step.number}
+
+
+
+
{step.title}
+
+ {step.description}
+
+
+
+ ))
+ }
+
+
diff --git a/src/components/landing/TestimonialsSection.astro b/src/components/landing/TestimonialsSection.astro
new file mode 100644
index 0000000..f5eabcf
--- /dev/null
+++ b/src/components/landing/TestimonialsSection.astro
@@ -0,0 +1,49 @@
+---
+import SectionShell from "@/components/landing/SectionShell.astro";
+import SectionHeading from "@/components/landing/SectionHeading.astro";
+import { TestimonialsCarousel } from "@/components/landing/testimonials-carousel";
+import { LucideIcon } from "@/components/ui/lucide-icon";
+import { testimonials } from "@/lib/landing-data";
+---
+
+
+
+
+
+
+
+ {
+ testimonials.map((item) => (
+
+
+
+
+ {item.name.charAt(0)}
+
+
+
{item.name}
+
{item.role}
+
+
+
+
+ {Array.from({ length: item.rating }).map(() => (
+
+ ))}
+
+
+
+
+ «{item.quote}»
+
+
+ ))
+ }
+
+
diff --git a/src/components/landing/hero-social-proof.tsx b/src/components/landing/hero-social-proof.tsx
new file mode 100644
index 0000000..7a32221
--- /dev/null
+++ b/src/components/landing/hero-social-proof.tsx
@@ -0,0 +1,53 @@
+import { Star } from "lucide-react";
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarGroup,
+ AvatarImage,
+} from "@/components/ui/avatar";
+
+const avatars = [
+ { src: "https://i.pravatar.cc/80?img=11", fallback: "ا" },
+ { src: "https://i.pravatar.cc/80?img=12", fallback: "س" },
+ { src: "https://i.pravatar.cc/80?img=13", fallback: "ر" },
+ { src: "https://i.pravatar.cc/80?img=14", fallback: "ک" },
+] as const;
+
+export function HeroSocialProof() {
+ return (
+
+
+ {avatars.map((avatar) => (
+
+
+
+ {avatar.fallback}
+
+
+ ))}
+
+
+
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+ ))}
+
+
4.9/5
+
+
+ بیش از 40 باشگاه به ما اعتماد کردهاند
+
+
+
+ );
+}
diff --git a/src/components/landing/testimonials-carousel.tsx b/src/components/landing/testimonials-carousel.tsx
new file mode 100644
index 0000000..7484eed
--- /dev/null
+++ b/src/components/landing/testimonials-carousel.tsx
@@ -0,0 +1,116 @@
+import * as React from "react";
+import useEmblaCarousel from "embla-carousel-react";
+import { Star } from "lucide-react";
+
+import { testimonials } from "@/lib/landing-data";
+import { cn } from "@/lib/utils";
+
+type Testimonial = (typeof testimonials)[number];
+
+function TestimonialCard({ item }: { item: Testimonial }) {
+ return (
+
+
+
+
+ {item.name.charAt(0)}
+
+
+
{item.name}
+
{item.role}
+
+
+
+
+ {Array.from({ length: item.rating }).map((_, index) => (
+
+ ))}
+
+
+
+
+ «{item.quote}»
+
+
+ );
+}
+
+function CarouselDots({
+ count,
+ selected,
+ onSelect,
+}: {
+ count: number;
+ selected: number;
+ onSelect: (index: number) => void;
+}) {
+ return (
+
+ {Array.from({ length: count }).map((_, index) => (
+
+ );
+}
+
+export function TestimonialsCarousel() {
+ const [selected, setSelected] = React.useState(0);
+ const [emblaRef, emblaApi] = useEmblaCarousel({
+ align: "start",
+ loop: false,
+ direction: "rtl",
+ containScroll: "trimSnaps",
+ });
+
+ React.useEffect(() => {
+ if (!emblaApi) return;
+
+ const onSelect = () => setSelected(emblaApi.selectedScrollSnap());
+
+ onSelect();
+ emblaApi.on("select", onSelect);
+ emblaApi.on("reInit", onSelect);
+
+ return () => {
+ emblaApi.off("select", onSelect);
+ emblaApi.off("reInit", onSelect);
+ };
+ }, [emblaApi]);
+
+ return (
+
+
+
+ {testimonials.map((item) => (
+
+
+
+ ))}
+
+
+
+
emblaApi?.scrollTo(index)}
+ />
+
+ );
+}
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 75b8c3d..b70830a 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -11,7 +11,7 @@ const buttonVariants = cva(
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/80",
outline:
- "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
+ "border-primary bg-transparent text-primary hover:border-primary hover:bg-primary/80 hover:text-primary-foreground aria-expanded:border-primary aria-expanded:bg-primary aria-expanded:text-primary-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost:
diff --git a/src/components/ui/carousel.tsx b/src/components/ui/carousel.tsx
new file mode 100644
index 0000000..f8202e4
--- /dev/null
+++ b/src/components/ui/carousel.tsx
@@ -0,0 +1,240 @@
+import * as React from "react"
+import useEmblaCarousel, {
+ type UseEmblaCarouselType,
+} from "embla-carousel-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
+
+type CarouselApi = UseEmblaCarouselType[1]
+type UseCarouselParameters = Parameters
+type CarouselOptions = UseCarouselParameters[0]
+type CarouselPlugin = UseCarouselParameters[1]
+
+type CarouselProps = {
+ opts?: CarouselOptions
+ plugins?: CarouselPlugin
+ orientation?: "horizontal" | "vertical"
+ setApi?: (api: CarouselApi) => void
+}
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0]
+ api: ReturnType[1]
+ scrollPrev: () => void
+ scrollNext: () => void
+ canScrollPrev: boolean
+ canScrollNext: boolean
+} & CarouselProps
+
+const CarouselContext = React.createContext(null)
+
+function useCarousel() {
+ const context = React.useContext(CarouselContext)
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ")
+ }
+
+ return context
+}
+
+function Carousel({
+ orientation = "horizontal",
+ opts,
+ setApi,
+ plugins,
+ className,
+ children,
+ ...props
+}: React.ComponentProps<"div"> & CarouselProps) {
+ const [carouselRef, api] = useEmblaCarousel(
+ {
+ ...opts,
+ axis: orientation === "horizontal" ? "x" : "y",
+ },
+ plugins
+ )
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
+
+ const onSelect = React.useCallback((api: CarouselApi) => {
+ if (!api) return
+ setCanScrollPrev(api.canScrollPrev())
+ setCanScrollNext(api.canScrollNext())
+ }, [])
+
+ const scrollPrev = React.useCallback(() => {
+ api?.scrollPrev()
+ }, [api])
+
+ const scrollNext = React.useCallback(() => {
+ api?.scrollNext()
+ }, [api])
+
+ const handleKeyDown = React.useCallback(
+ (event: React.KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault()
+ scrollPrev()
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault()
+ scrollNext()
+ }
+ },
+ [scrollPrev, scrollNext]
+ )
+
+ React.useEffect(() => {
+ if (!api || !setApi) return
+ setApi(api)
+ }, [api, setApi])
+
+ React.useEffect(() => {
+ if (!api) return
+ onSelect(api)
+ api.on("reInit", onSelect)
+ api.on("select", onSelect)
+
+ return () => {
+ api?.off("select", onSelect)
+ }
+ }, [api, onSelect])
+
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
+ const { carouselRef, orientation } = useCarousel()
+
+ return (
+
+ )
+}
+
+function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
+ const { orientation } = useCarousel()
+
+ return (
+
+ )
+}
+
+function CarouselPrevious({
+ className,
+ variant = "outline",
+ size = "icon-sm",
+ ...props
+}: React.ComponentProps) {
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
+
+ return (
+
+ )
+}
+
+function CarouselNext({
+ className,
+ variant = "outline",
+ size = "icon-sm",
+ ...props
+}: React.ComponentProps) {
+ const { orientation, scrollNext, canScrollNext } = useCarousel()
+
+ return (
+
+ )
+}
+
+export {
+ type CarouselApi,
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselPrevious,
+ CarouselNext,
+ useCarousel,
+}
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
new file mode 100644
index 0000000..d763cd9
--- /dev/null
+++ b/src/components/ui/input.tsx
@@ -0,0 +1,19 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Input({ className, type, ...props }: React.ComponentProps<"input">) {
+ return (
+
+ )
+}
+
+export { Input }
diff --git a/src/components/ui/lucide-icon.tsx b/src/components/ui/lucide-icon.tsx
new file mode 100644
index 0000000..2ccd3f8
--- /dev/null
+++ b/src/components/ui/lucide-icon.tsx
@@ -0,0 +1,64 @@
+import {
+ ArrowLeft,
+ BarChart3,
+ Bell,
+ BicepsFlexed,
+ Building2,
+ Check,
+ Cloud,
+ Dumbbell,
+ FileText,
+ Headset,
+ Leaf,
+ Mail,
+ MapPin,
+ Phone,
+ SportShoe,
+ Star,
+ ThumbsUp,
+ TrendingUp,
+ UserCircle,
+ Users,
+ Wallet,
+ type LucideIcon as LucideIconComponent,
+} from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+export const landingIcons = {
+ arrowLeft: ArrowLeft,
+ mail: Mail,
+ phone: Phone,
+ mapPin: MapPin,
+ headset: Headset,
+ users: Users,
+ userCircle: UserCircle,
+ coach: Dumbbell,
+ biceps: BicepsFlexed,
+ sportShoe: SportShoe,
+ dumbbell: Dumbbell,
+ trendingUp: TrendingUp,
+ chart: BarChart3,
+ fileText: FileText,
+ leaf: Leaf,
+ wallet: Wallet,
+ bell: Bell,
+ cloud: Cloud,
+ building: Building2,
+ thumbsUp: ThumbsUp,
+ star: Star,
+ check: Check,
+} as const satisfies Record
+
+export type LandingIconName = keyof typeof landingIcons
+
+type LucideIconProps = {
+ name: LandingIconName
+ className?: string
+}
+
+export function LucideIcon({ name, className }: LucideIconProps) {
+ const Icon = landingIcons[name] ?? Users
+
+ return
+}
diff --git a/src/components/views/Header.astro b/src/components/views/Header.astro
index 76bd15b..f19c20a 100644
--- a/src/components/views/Header.astro
+++ b/src/components/views/Header.astro
@@ -32,7 +32,7 @@ const pathname = Astro.url.pathname;
شروع کنید
diff --git a/src/components/views/header-nav.tsx b/src/components/views/header-nav.tsx
index f9d1b80..873697d 100644
--- a/src/components/views/header-nav.tsx
+++ b/src/components/views/header-nav.tsx
@@ -11,7 +11,7 @@ export function HeaderNav({ pathname = "/", className }: HeaderNavProps) {
const activeTab = getActiveNavTab(pathname)
return (
-
+
-
+
diff --git a/src/lib/contact-data.ts b/src/lib/contact-data.ts
new file mode 100644
index 0000000..50d2c42
--- /dev/null
+++ b/src/lib/contact-data.ts
@@ -0,0 +1,19 @@
+export const contactDetails = [
+ {
+ icon: "mail" as const,
+ label: "ایمیل ما",
+ value: "hello@Nxta.com",
+ href: "mailto:hello@Nxta.com",
+ },
+ {
+ icon: "phone" as const,
+ label: "شماره تماس ما",
+ value: "09123456789",
+ href: "tel:09123456789",
+ },
+ {
+ icon: "mapPin" as const,
+ label: "آدرس ما",
+ value: "استان، شهر، کوچه، پلاک، واحد",
+ },
+] as const
diff --git a/src/lib/landing-data.ts b/src/lib/landing-data.ts
new file mode 100644
index 0000000..9a2e79b
--- /dev/null
+++ b/src/lib/landing-data.ts
@@ -0,0 +1,112 @@
+export const stats = [
+ { value: "+۵۰", label: "مربی", icon: "biceps" },
+ { value: "+۱۰۰", label: "ورزشکار", icon: "sportShoe" },
+ { value: "+۵۰", label: "باشگاه فعال", icon: "dumbbell" },
+ { value: "۹۸٪", label: "رضایت کاربران", icon: "star" },
+] as const;
+
+export const features = [
+ {
+ title: "مدیریت اعضا",
+ description: "مدیریت اطلاعات اعضا، پروفایلها و روند پیشرفت به سادگی.",
+ icon: "users",
+ },
+ {
+ title: "پنل مربیان",
+ description: "اختصاص برنامه تمرینی و غذایی تنها در چند ثانیه.",
+ icon: "userCircle",
+ },
+ {
+ title: "پیگیری پیشرفت",
+ description: "بررسی فعالیتها، اهداف و عملکرد اعضا به صورت کامل.",
+ icon: "trendingUp",
+ },
+ {
+ title: "مدیریت اشتراکها",
+ description: "مدیریت پلنها، تمدیدها و پرداختها بدون دردسر.",
+ icon: "fileText",
+ },
+ {
+ title: "برنامههای غذایی",
+ description: "ساخت و اختصاص برنامههای تغذیه حرفهای و شخصیسازی شده.",
+ icon: "leaf",
+ },
+ {
+ title: "اعلانهای هوشمند",
+ description: "اطلاعرسانی خودکار به مربیان و اعضا.",
+ icon: "bell",
+ },
+] as const;
+
+export const steps = [
+ {
+ number: "۱",
+ title: "باشگاه خود را ایجاد کنید",
+ description: "پروفایل باشگاه را راهاندازی کرده و مربیان را دعوت کنید.",
+ },
+ {
+ number: "۲",
+ title: "اعضا را مدیریت کنید",
+ description: "اعضا را اضافه کرده و برای آنها برنامه اختصاصی تعریف کنید.",
+ },
+ {
+ number: "۳",
+ title: "باشگاه خود را توسعه دهید",
+ description: "فعالیتها، پیشرفت اعضا و عملکرد باشگاه را بررسی کنید.",
+ },
+] as const;
+
+export const plans = [
+ {
+ name: "پلن حرفهای",
+ price: "2 میلیون تومان",
+ period: "ماهیانه",
+ popular: false,
+ memberLimit: "تا 5 مربی و 50 ورزشکار",
+ extras: ["دسترسی به حرکات عمومی", "دسترسی به وعدههای عمومی"],
+ description:
+ "مناسب برای باشگاههای بزرگ که به دسترسی کامل و انعطافپذیری بالا نیاز دارند.",
+ },
+ {
+ name: "پلن استاندارد",
+ price: "2 میلیون تومان",
+ period: "ماهیانه",
+ popular: true,
+ memberLimit: "تا 5 مربی و 50 ورزشکار",
+ extras: ["دسترسی به حرکات عمومی"],
+ description: "ایدهآل برای باشگاههای در حال رشد با اعضای فعال.",
+ },
+ {
+ name: "پلن ساده",
+ price: "2 میلیون تومان",
+ period: "ماهیانه",
+ popular: false,
+ memberLimit: "تا 5 مربی و 50 ورزشکار",
+ extras: [],
+ description: "بهترین انتخاب برای باشگاههای تازهبهراهافتاده.",
+ },
+] as const;
+
+export const testimonials = [
+ {
+ name: "امیر حسینی",
+ role: "مدیر باشگاه فیتپلاس",
+ quote:
+ "از وقتی نکستا را استفاده میکنیم، مدیریت اعضا و مالی یکسوم زمان قبلی طول میکشد. واقعاً ساده و کاربردی است.",
+ rating: 5,
+ },
+ {
+ name: "سارا محمدی",
+ role: "مربی بدنسازی",
+ quote:
+ "پنل مربی عالیه. برنامه تمرینی و پیشرفت شاگردها را یکجا میبینم و دیگر نیازی به دفتر و اکسل نیست.",
+ rating: 5,
+ },
+ {
+ name: "رضا کریمی",
+ role: "مالک باشگاه آیرون",
+ quote:
+ "گزارشهای مالی و آماری دقیقاً همان چیزی بود که نیاز داشتم. تیم پشتیبانی هم سریع و حرفهای پاسخ میدهند.",
+ rating: 5,
+ },
+] as const;
diff --git a/src/lib/nav-links.ts b/src/lib/nav-links.ts
index 7ea7784..efe15f0 100644
--- a/src/lib/nav-links.ts
+++ b/src/lib/nav-links.ts
@@ -18,10 +18,12 @@ export const socialLinks = [
export type HeaderNavValue = (typeof headerNavLinks)[number]["value"]
-export function getActiveNavTab(pathname: string): HeaderNavValue {
+export function getActiveNavTab(pathname: string): HeaderNavValue | undefined {
const path = pathname.replace(/\/$/, "") || "/"
+ if (path === "/" || path === "") return undefined
if (path === "/blog" || path.startsWith("/blog/")) return "blog"
if (path === "/contact" || path.startsWith("/contact/")) return "contact"
- return "plans"
+ if (path === "/plans" || path.startsWith("/plans/")) return "plans"
+ return undefined
}
diff --git a/src/pages/contact.astro b/src/pages/contact.astro
index 463cbf0..06b9a74 100644
--- a/src/pages/contact.astro
+++ b/src/pages/contact.astro
@@ -1,9 +1,85 @@
---
-import Layout from "../layouts/Layout.astro";
+import Layout from "@/layouts/Layout.astro";
+import ContactForm from "@/components/contact/ContactForm.astro";
+import SupportBanner from "@/components/contact/SupportBanner.astro";
+import { LucideIcon } from "@/components/ui/lucide-icon";
+import { contactDetails } from "@/lib/contact-data";
---
-
- تماس با ما
+
+
+
+
+
+
+
+
+ با ما در ارتباط باشید
+
+
+
+
+ در کنار شما هستیم
+
+
+ هر سوال یا درخواستی دارید، تیم ما آماده پاسخگویی است. فرم را
+ پر کنید یا از راههای ارتباطی زیر با ما در تماس باشید.
+
+
+
+
+ {
+ contactDetails.map((item) => (
+
+
+
+
+
+
{item.label}
+ {item.href ? (
+
+ {item.value}
+
+ ) : (
+
+ {item.value}
+
+ )}
+
+
+ ))
+ }
+
+
+
+
+ در کنار شما هستیم
+
+
+ برای دریافت راهنمایی، پشتیبانی یا اطلاعات بیشتر، فرم را تکمیل
+ کنید تا با شما در ارتباط باشیم.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 6ce0937..df54a27 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1,3 +1,20 @@
---
-return Astro.redirect("/plans");
+import Layout from "../layouts/Layout.astro";
+import HeroSection from "@/components/landing/HeroSection.astro";
+import StatsSection from "@/components/landing/StatsSection.astro";
+import FeaturesSection from "@/components/landing/FeaturesSection.astro";
+import StepsSection from "@/components/landing/StepsSection.astro";
+import PricingSection from "@/components/landing/PricingSection.astro";
+import TestimonialsSection from "@/components/landing/TestimonialsSection.astro";
+import CtaSection from "@/components/landing/CtaSection.astro";
---
+
+
+
+
+
+
+
+
+
+
diff --git a/src/styles/global.css b/src/styles/global.css
index d151149..092135a 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -151,6 +151,29 @@
display: grid;
grid-template-rows: auto 1fr auto;
font-family: "iransansxv", sans-serif;
+ overflow-x: clip;
+ }
+
+ main {
+ min-width: 0;
+ overflow-x: clip;
+ }
+}
+
+@layer components {
+ .glass-surface {
+ @apply rounded-2xl border border-white/5 bg-card/15 backdrop-blur-xl;
+ }
+
+ .glass-input {
+ @apply w-full border-0 text-sm text-muted-foreground outline-none transition-colors placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-primary/20;
+ background: rgb(239 238 234 / 0.06);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ }
+
+ .contact-icon {
+ @apply flex shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary;
}
}