some edit

This commit is contained in:
2026-08-13 20:38:45 +03:30
parent 1cea5b78cb
commit 031e0cc413
24 changed files with 462 additions and 330 deletions
+34 -29
View File
@@ -220,56 +220,61 @@ const Feature199 = ({ className }: Feature199Props) => {
const handleScroll = () => {
const container = containerRef.current;
if (!container) return;
const items = container.getElementsByClassName("feature-item");
const containerMiddle = window.innerHeight * 0.6;
let closestItem: number | null = null;
let closestDistance = Infinity;
const rect = container.getBoundingClientRect();
const listHeight = container.offsetHeight;
const viewportHeight = window.innerHeight;
const scrollRange = Math.max(listHeight - viewportHeight * 0.45, 1);
const passed = Math.min(Math.max(-rect.top + viewportHeight * 0.2, 0), scrollRange);
const progress = passed / scrollRange;
const steppedIndex = Math.round(progress * (FEATURES.length - 1));
const nextIndex = Math.min(Math.max(steppedIndex, 0), FEATURES.length - 1);
Array.from(items).forEach((item, index) => {
const rect = item.getBoundingClientRect();
const distance = Math.abs(rect.top + rect.height / 2 - containerMiddle);
if (distance < closestDistance) {
closestDistance = distance;
closestItem = index;
}
});
if (closestItem !== null) {
setActiveFeature(closestItem);
}
setActiveFeature((prev) => (prev === nextIndex ? prev : nextIndex));
};
window.addEventListener("scroll", handleScroll);
handleScroll();
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, [isMobile]);
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<h2 className="mb-10 text-xl font-bold text-balance md:text-2xll lg:mb-20 lg:text-2xll">
<section
dir="rtl"
lang="fa"
className={cn("min-w-0 overflow-x-hidden py-16 sm:py-24 lg:py-32", className)}
>
<div className="container min-w-0">
<h2 className="mb-8 text-xl font-bold text-balance sm:mb-10 sm:text-2xl lg:mb-20 lg:text-3xl">
هر سرنخ را تا قرارداد دنبال کنید
</h2>
<div className="relative gap-6 pb-72 md:grid md:grid-cols-5 md:pb-0">
<div className="col-span-3 pb-4 text-start" ref={containerRef}>
<div className="relative min-w-0 gap-6 pb-64 sm:pb-60 lg:grid lg:grid-cols-5 lg:pb-0">
<div
className="col-span-3 min-w-0 pb-4 text-start"
ref={containerRef}
>
<Separator />
{FEATURES.map((feature, index) => (
<React.Fragment key={feature.id}>
<div
className="feature-item py-3"
className="feature-item py-2.5 sm:py-3"
onMouseEnter={() => !isMobile && setActiveFeature(index)}
>
<div className="flex items-center gap-7 md:gap-16 lg:gap-28">
<div
className={cn(
"flex min-w-0 items-center gap-3 rounded-lg px-2 py-2 transition-colors sm:gap-5 sm:px-0 sm:py-0 lg:gap-16 xl:gap-28",
activeFeature === index && "bg-muted/40 sm:bg-transparent",
)}
>
<span
className={cn(
"invisible size-2.5 shrink-0 rounded-full bg-primary md:size-3",
"invisible size-2 shrink-0 rounded-full bg-primary sm:size-2.5 lg:size-3",
activeFeature === index && "visible",
)}
/>
<p
className={cn(
"text-[clamp(1.65rem,3vw,2.15rem)] font-bold text-muted-foreground",
"min-w-0 wrap-break-word text-base font-bold text-muted-foreground sm:text-lg md:text-xl lg:text-2xl xl:text-3xl",
activeFeature === index && "text-primary",
)}
>
@@ -281,11 +286,11 @@ const Feature199 = ({ className }: Feature199Props) => {
</React.Fragment>
))}
</div>
<div className="sticky bottom-3 start-3 col-span-2 h-72 w-fit border md:top-20 md:h-fit">
<div className="fixed inset-x-3 bottom-3 z-20 mx-auto max-w-md border bg-background shadow-md md:sticky md:top-20 md:bottom-auto md:inset-x-auto md:col-span-2 md:mx-0 md:max-w-none md:h-fit md:w-fit md:shadow-none">
<img
src={FEATURES[activeFeature].image}
alt={FEATURES[activeFeature].imageAlt}
className="h-72 md:h-auto"
className="h-44 w-full object-cover sm:h-52 md:h-72 md:w-auto md:object-fill"
/>
</div>
</div>
+7 -14
View File
@@ -153,7 +153,6 @@ const Feature216 = ({ className }: Feature216Props) => {
const [buttonPosition, setButtonPosition] = useState<number>(0);
// Rightmost tab in RTL (`justify-start`) is the first item.
const [tabValue, setTabValue] = useState<TabValue>(TAB_ITEMS[0].value);
const triggerRefs = useRef<HTMLButtonElement[]>([]);
const imageRef = useRef<HTMLDivElement | null>(null);
const updateBottom = () => {
@@ -178,10 +177,6 @@ const Feature216 = ({ className }: Feature216Props) => {
};
const handleTabNavigation = (direction: "next" | "prev") => {
triggerRefs.current.forEach((ref) => {
ref.classList.add("grayscale-[1]", "opacity-[0.2]");
});
const currentIndex = getCurrentTabIndex();
if (currentIndex === -1) return;
@@ -207,21 +202,19 @@ const Feature216 = ({ className }: Feature216Props) => {
<h2 className="mb-10 text-center text-3xl font-light text-balance md:text-xl">
شاخص‌های کلیدی را یکجا ببینید
</h2>
<Tabs value={tabValue} onValueChange={onValueChange} className="w-full">
<Tabs
dir="rtl"
value={tabValue}
onValueChange={onValueChange}
className="w-full"
>
<TabsList className="hidden !h-auto w-full !justify-start items-end gap-12 bg-transparent pb-10 xl:flex">
{TAB_ITEMS.map((trigger, index) => {
const Icon = trigger.icon;
return (
<TabsTrigger
ref={(el) => {
if (el) {
triggerRefs.current[index] = el;
} else {
delete triggerRefs.current[index];
}
}}
className="flex !h-auto flex-col items-center gap-5 !border-none !bg-transparent p-0 opacity-[0.2] !shadow-none grayscale-[1] hover:!bg-transparent hover:opacity-[0.5] hover:grayscale-[0] data-[state=active]:!bg-transparent data-[state=active]:opacity-100 data-[state=active]:!shadow-none data-[state=active]:grayscale-0 dark:!bg-transparent dark:data-[state=active]:!bg-transparent"
className="flex !h-auto !flex-none shrink-0 flex-col items-center gap-5 !border-none !bg-transparent p-0 opacity-[0.2] !shadow-none grayscale hover:!bg-transparent hover:opacity-50 hover:grayscale-0 data-[state=active]:!bg-transparent data-[state=active]:opacity-100 data-[state=active]:!shadow-none data-[state=active]:grayscale-0 dark:!bg-transparent dark:data-[state=active]:!bg-transparent"
key={trigger.id}
value={trigger.value}
data-index={index}
+11 -13
View File
@@ -1,6 +1,5 @@
import { ArrowRight } from "lucide-react";
import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
@@ -32,13 +31,13 @@ const Feature220 = ({ className }: Feature220Props) => {
dir="rtl"
lang="fa"
className={cn(
"bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-[length:3.125rem_3.125rem] bg-repeat py-20",
"flex min-h-full w-full min-w-0 flex-col justify-center bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-[length:3.125rem_3.125rem] bg-repeat py-12 sm:py-16 lg:py-20",
className,
)}
>
<div className="container flex flex-wrap-reverse gap-12 lg:grid lg:grid-cols-[1fr_1.5fr] lg:gap-[7.5rem]">
<div className="flex flex-col justify-between gap-20">
<div className="mt-10 flex flex-col gap-5 text-start">
<div className="container flex min-w-0 flex-col gap-8 px-4 sm:gap-10 sm:px-6 md:gap-12 lg:grid lg:grid-cols-[1fr_1.5fr] lg:gap-[7.5rem] lg:px-0">
<div className="order-2 flex min-w-0 flex-col justify-between gap-10 sm:gap-14 md:gap-16 lg:order-1 lg:gap-20 lg:ps-12">
<div className="flex flex-col gap-5 text-start md:mt-8 lg:mt-10">
<h2 className="text-xl leading-none font-semibold tracking-tight text-balance md:text-2xll">
از خرید تا فروش در یک سیستم
</h2>
@@ -53,7 +52,7 @@ const Feature220 = ({ className }: Feature220Props) => {
</div>
</div>
<div>
<div className="group flex w-full max-w-[30rem] flex-col gap-10 rounded-[.5rem] bg-background p-5 text-start transition-shadow hover:shadow-[0_0_2px_theme('colors.gray.300')]">
<div className="group flex w-full max-w-full flex-col gap-10 rounded-[.5rem] bg-background p-5 text-start transition-shadow hover:shadow-[0_0_2px_theme('colors.gray.300')] sm:max-w-[30rem]">
<div className="flex w-full flex-col gap-8 sm:flex-row">
{STATS.map((item) => (
<div className="flex flex-col gap-1" key={item.id}>
@@ -67,21 +66,20 @@ const Feature220 = ({ className }: Feature220Props) => {
</div>
</div>
</div>
<div className="w-full max-w-[46.5rem]">
<AspectRatio
ratio={0.873239437}
className="overflow-hidden rounded-2xl"
>
<div className="order-1 w-full min-w-0 max-w-full pt-6 sm:pt-8 md:pt-8 lg:order-2 lg:max-w-[46.5rem] lg:pe-12 lg:pt-10">
<div className="aspect-video max-h-80 overflow-hidden rounded-2xl sm:max-h-96 md:aspect-[4/3] md:max-h-none lg:aspect-[712/815]">
<video
src="/block-media/shadcnblocks/block/video-8.mp4"
src="https://deifkwefumgah.cloudfront.net/shadcnblocks/block/video-8.mp4"
className="block size-full object-cover object-center"
loop
autoPlay
muted
playsInline
preload="metadata"
controls={false}
aria-label="عملیات سازمان را یکپارچه کنید"
/>
</AspectRatio>
</div>
</div>
</div>
</section>
+22 -14
View File
@@ -33,44 +33,52 @@ const Feature220a = ({ className }: Feature220aProps) => {
dir="rtl"
lang="fa"
className={cn(
"bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-[length:3.125rem_3.125rem] bg-repeat py-24",
"flex min-h-full w-full min-w-0 flex-col justify-center overflow-x-hidden bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-[length:3.125rem_3.125rem] bg-repeat py-12 sm:py-16 lg:py-24",
className,
)}
>
<div className="container max-w-5xl">
<div className="mb-16 text-center">
<div className="container min-w-0 max-w-5xl px-4 sm:px-6">
<div className="mb-10 min-w-0 text-center sm:mb-16">
<Badge variant="secondary" className="mb-6 px-4 py-2">
هر تیم داشبورد خودش را داشته باشد
</Badge>
<h2 className="leading-tighter mb-6 text-2xll font-semibold tracking-tight text-balance lg:text-2xll">
<h2 className="leading-tighter mb-6 text-balance text-xl font-semibold tracking-tight sm:text-2xll lg:text-2xll">
روندها را زودتر از رقبا ببینید
<span className="block text-primary">گزارش‌هایی که مدیران واقعاً استفاده می‌کنند</span>
<span className="block text-primary">
گزارش‌هایی که مدیران واقعاً استفاده می‌کنند
</span>
</h2>
<p className="mx-auto mb-8 max-w-3xl text-xl leading-relaxed font-medium text-pretty text-muted-foreground">
داشبوردهای تعاملی به تیم‌ها اجازه می‌دهد سؤال جدید بپرسند بدون اینکه هر بار به تیم داده وابسته باشند.
<p className="mx-auto mb-8 max-w-3xl text-base leading-relaxed font-medium text-pretty text-muted-foreground sm:text-xl">
داشبوردهای تعاملی به تیم‌ها اجازه می‌دهد سؤال جدید بپرسند بدون اینکه
هر بار به تیم داده وابسته باشند.
</p>
<Button size="lg" className="gap-2 px-8 py-6 text-lg">
<Button
size="lg"
className="h-auto max-w-full gap-2 px-6 py-4 text-base whitespace-normal sm:px-8 sm:py-6 sm:text-lg"
>
گزارش سفارشی را بدون SQL بسازید و سریع منتشر کنید.
<ArrowRight className="size-4 rtl:rotate-180" />
<ArrowRight className="size-4 shrink-0 rtl:rotate-180" />
</Button>
</div>
<div className="mb-16">
<AspectRatio ratio={16 / 9} className="overflow-hidden rounded-3xl">
<div className="mb-10 min-w-0 sm:mb-16">
<AspectRatio ratio={16 / 9} className="overflow-hidden rounded-2xl sm:rounded-3xl">
<video
src="/block-media/shadcnblocks/block/video-8.mp4"
src="https://deifkwefumgah.cloudfront.net/shadcnblocks/block/video-8.mp4"
className="block size-full object-cover object-center"
loop
autoPlay
muted
playsInline
preload="metadata"
controls={false}
aria-label="تصمیم استراتژیک را داده‌محور کنید"
/>
</AspectRatio>
</div>
<div className="rounded-2xl bg-card p-8">
<div className="grid grid-cols-1 gap-8 sm:grid-cols-3">
<div className="min-w-0 rounded-2xl bg-card p-4 sm:p-8">
<div className="grid min-w-0 grid-cols-1 gap-6 sm:grid-cols-3 sm:gap-8">
{STATS.map((item) => (
<div className="text-center" key={item.id}>
<div className="mb-2 text-xl font-bold text-primary">
+8 -4
View File
@@ -32,11 +32,11 @@ const Feature220b = ({ className }: Feature220bProps) => {
dir="rtl"
lang="fa"
className={cn(
"bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-[length:3.125rem_3.125rem] bg-repeat py-20",
"flex min-h-full w-full min-w-0 flex-col justify-center overflow-x-hidden bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-[length:3.125rem_3.125rem] bg-repeat py-12 sm:py-16 lg:py-20",
className,
)}
>
<div className="container">
<div className="container min-w-0 px-4 sm:px-6">
<div className="mb-20 flex flex-wrap justify-center gap-12">
{STATS.map((item) => (
<div key={item.id} className="min-w-48 text-center">
@@ -57,11 +57,13 @@ const Feature220b = ({ className }: Feature220bProps) => {
className="overflow-hidden rounded-2xl"
>
<video
src="/block-media/shadcnblocks/block/video-8.mp4"
src="https://deifkwefumgah.cloudfront.net/shadcnblocks/block/video-8.mp4"
className="block size-full object-cover object-center"
loop
autoPlay
muted
playsInline
preload="metadata"
controls={false}
aria-label="هر سرنخ را تا قرارداد دنبال کنید"
/>
@@ -73,7 +75,9 @@ const Feature220b = ({ className }: Feature220bProps) => {
فروش تیم‌تان را یکجا مدیریت کنید
</h2>
<p className="text-lg leading-[1.4] font-medium text-pretty text-muted-foreground md:text-xl">
تمام تعاملات، سرنخ‌ها و قراردادها در یک فضای کاری متمرکز جمع می‌شود. تیم فروش بدون جابجایی بین ابزارها، سریع‌تر پیش‌بینی می‌کند و معاملات بیشتری می‌بندد.
تمام تعاملات، سرنخ‌ها و قراردادها در یک فضای کاری متمرکز جمع
می‌شود. تیم فروش بدون جابجایی بین ابزارها، سریع‌تر پیش‌بینی می‌کند
و معاملات بیشتری می‌بندد.
</p>
<div className="pt-4">
<Button size="lg" className="gap-2 px-8 py-6 text-lg">
+16 -15
View File
@@ -64,41 +64,42 @@ const Feature221 = ({ className }: Feature221Props) => {
dir="rtl"
lang="fa"
className={cn(
"bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-[length:3.125rem_3.125rem] bg-repeat py-32",
"w-full min-w-0 bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-size-[3.125rem_3.125rem] bg-repeat py-32",
className,
)}
>
<div className="container">
<div className="container px-4 sm:px-6 lg:px-8">
<div className="flex w-full flex-col items-start justify-between gap-4 pb-16 lg:flex-row lg:items-end">
<div className="flex w-full max-w-lg flex-1 flex-col items-start gap-5 text-start">
<h2 className="text-[2rem] leading-none font-bold tracking-tight text-balance md:text-[2.75rem] lg:text-2xll">
<h2 className="text-[2rem] leading-none font-bold tracking-tight text-balance md:text-[2.75rem]">
پیش‌بینی درآمد را دقیق‌تر کنید
</h2>
<p className="w-full max-w-[30rem] text-[1.15rem] leading-normal font-medium text-pretty text-muted-foreground sm:text-xl">
با یادآورهای هوشمند و اولویت‌بندی سرنخ، زمان تیم روی معاملات با احتمال بیشتر صرف می‌شود و چرخه فروش کوتاه‌تر می‌شود.
با یادآورهای هوشمند و اولویت‌بندی سرنخ، زمان تیم روی معاملات با
احتمال بیشتر صرف می‌شود و چرخه فروش کوتاه‌تر می‌شود.
</p>
</div>
<div>
<Button className="gap-2 rounded-full">
چرخه فروش را از سرنخ تا قرارداد در یک مسیر مشخص پیش ببرید. مدیران می‌توانند گلوگاه‌ها را ببینند و تیم‌ها با اولویت درست روی معاملات ارزشمند تمرکز کنند.
<ArrowRight className="size-4 rtl:rotate-180" />
<div className="w-full lg:w-auto lg:shrink-0">
<Button className="h-10 w-full gap-2 rounded-full px-4 lg:w-auto">
شروع کنید
<ArrowRight className="size-4 shrink-0 rtl:rotate-180" />
</Button>
</div>
</div>
<div className="grid w-full grid-cols-1 grid-rows-[auto] gap-10 md:grid-cols-2">
<div className="grid w-full grid-cols-1 gap-10 md:grid-cols-2">
{FEATURES.map((feature) => (
<Card
key={feature.id}
className="flex w-full flex-col justify-between gap-10 rounded-lg border bg-background p-5"
className="flex w-full min-w-0 flex-col justify-between gap-10 rounded-lg border bg-background p-5"
>
<CardHeader className="flex w-full flex-col justify-between gap-4 p-0 lg:flex-row lg:items-center">
<CardTitle className="flex w-fit items-center justify-start gap-2.5">
<CardHeader className="flex w-full min-w-0 flex-row items-center justify-between gap-4 p-0">
<CardTitle className="min-w-0 flex-1">
<h3 className="text-[1.75rem] leading-none font-bold tracking-tight text-balance">
{feature.title}
</h3>
</CardTitle>
<Badge className="bg-muted">
<p className="text-sm text-foreground">الگوهای موفق فروش را در کل سازمان تکرار کنید. داده لحظه‌ای نشان می‌دهد کدام مرحله بیشترین افت تبدیل را دارد.</p>
<Badge className="shrink-0 bg-muted px-2 py-0.5 text-xs text-foreground">
ویژگی
</Badge>
</CardHeader>
<CardContent className="p-0 text-start">
@@ -106,7 +107,7 @@ const Feature221 = ({ className }: Feature221Props) => {
{feature.description}
</p>
</CardContent>
<CardFooter className="flex w-full items-end justify-between gap-5 p-0 lg:flex-row">
<CardFooter className="flex w-full items-end justify-between gap-5 border-0 bg-transparent p-0 pb-5">
<div className="flex-1">
<Button size="sm" className="rounded-full">
<a
+4 -4
View File
@@ -118,7 +118,7 @@ const Card = ({ link, background, title, imageAlt, stats }: CardData) => {
href={link}
aria-label={title}
style={{ backgroundImage: `url(${background})` }}
className="before:content-[] relative min-h-auto w-full overflow-hidden rounded-lg bg-black/80 bg-cover bg-center bg-no-repeat p-5 transition-all duration-300 before:absolute before:start-0 before:top-0 before:z-10 before:block before:size-full before:bg-black/50 before:transition-all before:duration-300 hover:before:bg-black/30 sm:aspect-square md:aspect-auto md:min-h-120 md:max-w-120"
className="before:content-[] relative min-h-auto w-full overflow-hidden rounded-lg bg-black/80 bg-cover bg-center bg-no-repeat p-5 transition-all duration-300 before:absolute before:start-0 before:top-0 before:z-10 before:block before:size-full before:bg-black/50 before:transition-all before:duration-300 hover:before:bg-black/30 sm:aspect-square md:aspect-auto md:min-h-120"
>
<span className="sr-only">{imageAlt}</span>
<div className="relative z-20 flex size-full flex-col justify-between gap-20 text-start md:gap-16">
@@ -150,9 +150,9 @@ interface Feature222Props {
const Feature222 = ({ className }: Feature222Props) => {
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<div className="grid gap-5 md:grid-cols-2 lg:grid-cols-3">
<section dir="rtl" lang="fa" className={cn("w-full min-w-0 py-32", className)}>
<div className="w-full px-4 sm:px-6 lg:px-8">
<div className="grid w-full grid-cols-1 gap-5 md:grid-cols-2 lg:grid-cols-3">
{LIST.map((item) => (
<Card key={item.id} {...item} />
))}
+24 -21
View File
@@ -120,10 +120,14 @@ const Feature224 = ({ className }: Feature224Props) => {
};
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<div className="flex w-full flex-col items-center justify-center gap-4">
<h2 className="mx-auto max-w-87.5 text-center text-xl leading-none font-medium text-balance md:max-w-112.5 md:text-2xll lg:max-w-140 lg:text-2xll">
<section
dir="rtl"
lang="fa"
className={cn("w-full min-w-0 overflow-x-hidden py-12 sm:py-20 lg:py-32", className)}
>
<div className="container min-w-0 px-4 sm:px-6">
<div className="flex w-full min-w-0 flex-col items-center justify-center gap-4">
<h2 className="mx-auto max-w-full text-center text-xl leading-none font-medium text-balance md:max-w-112.5 md:text-2xll lg:max-w-140 lg:text-2xll">
فرایندها را استاندارد و سریع کنید
</h2>
</div>
@@ -132,27 +136,27 @@ const Feature224 = ({ className }: Feature224Props) => {
align: "start",
direction: "rtl",
}}
className="w-full"
className="w-full min-w-0 max-w-full"
setApi={setApi}
>
<CarouselContent className="-ms-8 pt-20">
<CarouselContent className="pt-12 sm:pt-20">
{FEATURES.map((card) => (
<CarouselItem
key={card.id}
className="ps-8 md:basis-1/2 lg:basis-1/3"
className="min-w-0 basis-[85%] sm:basis-1/2 lg:basis-1/3"
>
<div className="p-1">
<Card className="border-0 py-0 shadow-none dark:bg-background">
<CardContent className="flex flex-col p-0">
<div className="min-w-0 p-1">
<Card className="min-w-0 border-0 py-0 shadow-none dark:bg-background">
<CardContent className="flex min-w-0 flex-col p-0">
<div
style={{ backgroundImage: `url("${card.background}")` }}
className="relative flex aspect-[0.935802469] w-full flex-col items-center justify-between overflow-hidden rounded-2xl bg-cover bg-center bg-no-repeat p-7"
className="relative flex aspect-[0.935802469] w-full min-w-0 flex-col items-center justify-between overflow-hidden rounded-2xl bg-cover bg-center bg-no-repeat p-4 sm:p-7"
role="img"
aria-label={card.imageAlt}
>
<div className="flex size-full flex-1" />
<div className="h-12 w-full">
<div className="mx-auto mb-8 flex w-full max-w-60 items-center gap-4 rounded-full bg-primary px-3 py-2.5 shadow-xl dark:bg-background">
<div className="h-12 w-full min-w-0">
<div className="mx-auto mb-6 flex w-full max-w-full min-w-0 items-center gap-3 rounded-full bg-primary px-3 py-2.5 shadow-xl sm:mb-8 sm:max-w-60 sm:gap-4 dark:bg-background">
<div className="shrink-0">
<div className="flex -space-x-2 rtl:space-x-reverse">
<div className="flex size-7 rounded-full border border-black bg-background dark:bg-primary">
@@ -163,14 +167,14 @@ const Feature224 = ({ className }: Feature224Props) => {
</div>
</div>
</div>
<div className="text-sm font-medium text-white">
<div className="min-w-0 text-sm font-medium text-white">
{card.label}
</div>
</div>
</div>
</div>
<div className="flex w-full flex-col gap-1 pt-6 text-start">
<h3 className="text-xl font-medium text-balance text-foreground">
<div className="flex w-full min-w-0 flex-col gap-1 pt-6 text-start">
<h3 className="text-lg font-medium text-balance text-foreground sm:text-xl">
{card.title}
</h3>
<p className="text-sm text-pretty text-muted-foreground">
@@ -182,12 +186,11 @@ const Feature224 = ({ className }: Feature224Props) => {
</div>
</CarouselItem>
))}
</CarouselContent>
<div className="mt-8 hidden items-center justify-center gap-4 md:flex">
<CarouselPrevious className="static size-12 translate-y-0" />
<CarouselNext className="static size-12 translate-y-0" />
</CarouselContent> <div className="mt-8 hidden items-center justify-center gap-4 md:flex">
<CarouselPrevious className="static size-12 translate-y-0 rtl:rotate-180" />
<CarouselNext className="static size-12 translate-y-0 rtl:rotate-180" />
</div>
<div className="mt-6 flex w-full items-center gap-2 md:hidden">
<div className="mt-6 flex w-full min-w-0 items-center gap-2 md:hidden">
{Array.from({ length: FEATURES.length }).map((_, i) => (
<button
type="button"
+12 -7
View File
@@ -114,7 +114,7 @@ const Feature225 = ({ className }: Feature225Props) => {
className={cn("w-full bg-background py-12 md:py-24 lg:py-32", className)}
>
<div className="container px-4 md:px-6">
<div className="flex flex-col items-center justify-center space-y-4 text-center">
<div className="flex w-full flex-col items-start space-y-4 text-right">
<h2 className="text-3xl font-bold tracking-tighter text-balance sm:text-xl md:text-2xll">
داده پراکنده را به تصمیم تبدیل کنید
</h2>
@@ -125,10 +125,11 @@ const Feature225 = ({ className }: Feature225Props) => {
<div className="mt-16">
<Tabs
dir="rtl"
defaultValue={FEATURES[0].id}
value={activeTab}
onValueChange={setActiveTab}
className="w-full"
className="w-full min-w-0"
>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
{FEATURES.map((feature) => (
@@ -141,8 +142,8 @@ const Feature225 = ({ className }: Feature225Props) => {
onClick={() => setActiveTab(feature.id)}
>
<CardContent className="p-6">
<div className="flex flex-col items-start space-y-2 text-start">
<div className="flex items-center gap-2">
<div className="flex w-full flex-col items-start space-y-2 text-right">
<div className="flex w-full items-center justify-start gap-2">
<div className="flex size-10 shrink-0 items-center justify-center rounded-full bg-primary/10">
{feature.icon}
</div>
@@ -150,7 +151,7 @@ const Feature225 = ({ className }: Feature225Props) => {
{feature.title}
</h3>
</div>
<p className="text-sm text-pretty text-muted-foreground">
<p className="w-full text-sm text-pretty text-muted-foreground">
{feature.description}
</p>
</div>
@@ -184,8 +185,12 @@ const Feature225 = ({ className }: Feature225Props) => {
className="size-full object-cover"
/>
) : (
<div className="flex size-full items-center justify-center bg-muted">
<p className="text-muted-foreground">داده فروش، بازاریابی و پشتیبانی در داشبوردهای یکپارچه جمع می‌شود. مدیران بدون انتظار گزارش دستی، وضعیت واقعی کسب‌وکار را می‌بینند.</p>
<div className="flex size-full items-center justify-center bg-muted p-6">
<p className="text-right text-pretty text-muted-foreground">
داده فروش، بازاریابی و پشتیبانی در داشبوردهای یکپارچه جمع
می‌شود. مدیران بدون انتظار گزارش دستی، وضعیت واقعی
کسب‌وکار را می‌بینند.
</p>
</div>
)}
</div>
+22 -15
View File
@@ -10,19 +10,26 @@ interface Feature228Props {
const Feature228 = ({ className }: Feature228Props) => {
return (
<section dir="rtl" lang="fa" className={cn("bg-muted/50 py-32", className)}>
<div className="container">
<div className="grid grid-cols-1 items-center gap-10 lg:grid-cols-2 lg:gap-0">
<div className="text-start lg:pe-16">
<h2 className="mb-5 text-2xll text-balance md:text-2xll md:leading-20">
<section
dir="rtl"
lang="fa"
className={cn(
"w-full min-w-0 overflow-x-hidden bg-muted/50 py-16 md:py-24 lg:py-32",
className,
)}
>
<div className="container px-4 sm:px-6 lg:px-8">
<div className="grid w-full min-w-0 grid-cols-1 items-center gap-10 lg:grid-cols-2 lg:gap-12 xl:gap-16">
<div className="min-w-0 text-start lg:pe-16">
<h2 className="mb-5 text-xl leading-tight font-semibold text-balance md:text-2xll md:leading-20">
با هوش مصنوعی سریع‌تر تصمیم بگیرید
<br />
کارهای تکراری را به AI بسپارید
</h2>
<p className="text-lg text-pretty text-muted-foreground">
<p className="text-base text-pretty text-muted-foreground md:text-lg">
مدل‌های هوشمند الگوهای پنهان در داده فروش و پشتیبانی را پیدا می‌کنند. تیم‌ها با پیشنهادهای عملی، سریع‌تر اقدام می‌کنند.
</p>
<div className="mt-12 grid gap-8 sm:grid-cols-2 sm:gap-10">
<div className="mt-8 grid gap-6 sm:mt-12 sm:grid-cols-2 sm:gap-8 md:gap-10">
<div>
<span className="mb-4 grid size-12 place-items-center rounded-lg bg-muted-foreground/10">
<Lightbulb />
@@ -81,27 +88,27 @@ const Feature228 = ({ className }: Feature228Props) => {
</div>
</div>
</div>
<div className="flex h-full flex-col gap-6 sm:max-h-[440px] sm:flex-row xl:max-h-[550px]">
<div className="flex flex-col gap-6">
<div className="flex w-full min-w-0 flex-col gap-6 md:max-h-[440px] md:flex-row xl:max-h-[550px]">
<div className="flex min-w-0 flex-1 flex-col gap-6">
<img
src="/block-media/shadcnblocks/block/placeholder-2.svg"
alt="تحلیل با AI"
className="aspect-video rounded-2xl border border-border object-cover"
className="aspect-video w-full rounded-2xl border border-border object-cover"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-3.svg"
alt="دستیار هوش مصنوعی"
className="ms-auto aspect-video flex-1 rounded-2xl border border-border object-cover sm:aspect-auto sm:w-2/3"
className="aspect-video w-full rounded-2xl border border-border object-cover md:ms-auto md:aspect-auto md:w-2/3"
/>
</div>
<div className="min-h-[580px] rounded-4xl border border-border p-2 sm:min-h-0 sm:max-w-56 sm:shrink-0 xl:max-w-72">
<div className="mx-auto aspect-[9/16] w-full max-w-72 rounded-4xl border border-border p-2 md:mx-0 md:aspect-auto md:max-w-56 md:shrink-0 xl:max-w-72">
<div className="flex h-full flex-col rounded-3xl border border-border bg-background px-1.5 pt-3 pb-1.5">
<div className="mx-auto mb-3 h-4 w-16 rounded-full bg-muted-foreground/15 xl:h-5 xl:w-20" />
<div className="flex-1">
<div className="mx-auto mb-3 h-4 w-16 shrink-0 rounded-full bg-muted-foreground/15 xl:h-5 xl:w-20" />
<div className="min-h-0 flex-1">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="پیشنهادهای هوشمند"
className="h-full rounded-2xl object-cover"
className="size-full rounded-2xl object-cover"
/>
</div>
</div>
+20 -13
View File
@@ -12,27 +12,34 @@ interface Feature234Props {
const Feature234 = ({ className }: Feature234Props) => {
return (
<section dir="rtl" lang="fa" className={cn("bg-background py-32", className)}>
<div className="container flex flex-col items-center">
<h1 className="font-inter mx-auto max-w-4xl text-center text-[70px] leading-[65px] font-semibold tracking-tighter text-balance lg:text-[85px] lg:leading-[86px]">
<section
dir="rtl"
lang="fa"
className={cn(
"w-full min-w-0 overflow-x-hidden bg-background py-12 sm:py-20 lg:py-32",
className,
)}
>
<div className="container flex min-w-0 flex-col items-center px-4 sm:px-6">
<h1 className="font-inter mx-auto max-w-full text-center text-3xl leading-tight font-semibold tracking-tighter text-balance sm:text-5xl md:text-6xl lg:max-w-4xl lg:text-[85px] lg:leading-[86px]">
عملیات سازمان را یکپارچه کنید
</h1>
<CardsContainer />
<p className="font-inter mx-auto max-w-xl text-center text-lg font-medium text-pretty text-black">
<p className="font-inter mx-auto max-w-xl text-center text-base font-medium text-pretty text-black sm:text-lg">
گزارش‌های یکپارچه نشان می‌دهد کدام بخش بیشترین تأثیر را روی سودآوری دارد.
</p>
<div className="mt-10 flex items-center gap-2">
<div className="mt-8 flex w-full max-w-full flex-wrap items-center justify-center gap-2 sm:mt-10">
<Button
asChild
variant="default"
className="block h-fit w-fit rounded-full px-6 py-2 text-sm leading-loose"
className="block h-auto max-w-full rounded-full px-5 py-2 text-sm leading-loose whitespace-normal"
>
<a href="#">از خرید تا فروش در یک سیستم</a>
</Button>
<Button
asChild
variant="secondary"
className="block h-fit w-fit rounded-full px-6 py-2 text-sm leading-loose"
className="block h-auto max-w-full rounded-full px-5 py-2 text-sm leading-loose whitespace-normal"
>
<a href="#">یکپارچگی</a>
</Button>
@@ -108,20 +115,20 @@ const CardsContainer = () => {
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
return (
<div className="my-16 ms-[50px] flex items-center justify-center lg:ms-0">
<div className="my-10 flex w-full max-w-full items-center justify-center overflow-hidden sm:my-14 lg:my-16">
{CARDS.map((card, index) => {
const yOffset = Math.pow(Math.abs(index - 3), 1.2) * 12;
const isHovered = hoveredIndex === index;
const visibilityClasses =
(index === 0 || index === 1 || index === 5 || index === 6
? " hidden sm:hidden "
? "hidden sm:hidden "
: "") +
(index === 0 || index === 6 ? "md:hidden " : "md:block ") +
"lg:block";
return (
<motion.div
className={visibilityClasses}
className={cn("shrink-0", visibilityClasses)}
key={card.id}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
@@ -134,14 +141,14 @@ const CardsContainer = () => {
>
<div
className={cn(
"relative h-[176px] w-[177px] overflow-hidden rounded-3xl border",
"relative h-28 w-28 overflow-hidden rounded-2xl border sm:h-36 sm:w-36 sm:rounded-3xl lg:h-[176px] lg:w-[177px]",
index !== 0 && "-ms-7 sm:-ms-9 lg:-ms-[50px]",
card.isDark
? "mb-14 border-[#8b8b8b] bg-black shadow-2xl"
? "mb-8 border-[#8b8b8b] bg-black shadow-2xl sm:mb-14"
: "border-[#cecece] bg-[#f5f5f5] shadow-lg",
)}
style={{
transform: `rotate(${card.rotate}deg)`,
marginInlineStart: index === 0 ? "0px" : "-50px",
}}
>
<img
+41 -22
View File
@@ -14,44 +14,63 @@ interface Feature236Props {
const Feature236 = ({ className }: Feature236Props) => {
return (
<section dir="rtl" lang="fa" className={cn("bg-background py-32", className)}>
<div className="container flex flex-col items-center gap-10 md:gap-0">
<section
dir="rtl"
lang="fa"
className={cn(
"w-full min-w-0 overflow-x-hidden bg-background py-12 sm:py-20 lg:py-32",
className,
)}
>
<div className="container flex min-w-0 flex-col items-center gap-8 px-4 sm:gap-10 sm:px-6 md:gap-0">
<Button
variant="outline"
className="flex items-center justify-center gap-5 rounded-full px-5! py-5 text-sm font-medium text-muted-foreground"
className="h-auto max-w-full gap-3 rounded-full px-4 py-4 text-sm font-medium whitespace-normal text-muted-foreground sm:gap-5 sm:px-5 sm:py-5"
>
<Group />
تمام تعاملات، سرنخ‌ها و قراردادها در یک فضای کاری متمرکز جمع می‌شود. تیم فروش بدون جابجایی بین ابزارها، سریع‌تر پیش‌بینی می‌کند و معاملات بیشتری می‌بندد.
<ArrowUpRight className="rtl:rotate-180" />
<Group className="size-4 shrink-0 sm:size-5" />
<span className="text-pretty">
تمام تعاملات، سرنخ‌ها و قراردادها در یک فضای کاری متمرکز جمع می‌شود.
تیم فروش بدون جابجایی بین ابزارها، سریع‌تر پیش‌بینی می‌کند و معاملات
بیشتری می‌بندد.
</span>
<ArrowUpRight className="size-4 shrink-0 rtl:rotate-180" />
</Button>
<BracketedBorder>
<div className="mb-7 flex items-center justify-center gap-5 tracking-tight">
<div className="relative flex items-center justify-center">
<div className="mb-5 flex w-full min-w-0 max-w-2xl items-center justify-center gap-3 px-2 tracking-tight sm:mb-7 sm:gap-5 sm:px-4">
<div className="relative flex shrink-0 items-center justify-center">
<span className="absolute inline-block size-2 rounded-full bg-green-300" />
<span className="absolute inline-block size-3 animate-ping rounded-full bg-green-300" />
</div>
<p>از اولین تماس تا تمدید قرارداد، هر مرحله فروش قابل ردیابی است. گزارش‌های لحظه‌ای به مدیران کمک می‌کند گلوگاه‌ها را قبل از از دست دادن درآمد شناسایی کنند.</p>
<p className="min-w-0 text-pretty text-sm text-muted-foreground sm:text-base">
از اولین تماس تا تمدید قرارداد، هر مرحله فروش قابل ردیابی است.
گزارش‌های لحظه‌ای به مدیران کمک می‌کند گلوگاه‌ها را قبل از از دست
دادن درآمد شناسایی کنند.
</p>
</div>
<h1 className="max-w-xl px-4 text-center text-2xll font-semibold tracking-tighter text-balance text-foreground md:text-[90px] md:leading-22">
<h1 className="max-w-full px-2 text-center text-3xl font-semibold tracking-tighter text-balance text-foreground sm:px-4 sm:text-4xl md:max-w-3xl md:text-5xl md:leading-tight lg:text-6xl xl:text-7xl">
هر سرنخ را تا قرارداد دنبال کنید
</h1>
</BracketedBorder>
<h2 className="text-3xl font-medium tracking-tighter text-foreground">
<h2 className="max-w-full px-2 text-center text-2xl font-medium tracking-tighter text-balance text-foreground sm:text-3xl">
فروش تیم‌تان را یکجا مدیریت کنید
</h2>
<p className="max-w-2xl px-10 text-center text-pretty text-muted-foreground sm:mt-5 lg:text-xl">
پروفایل یکپارچه مشتری، تاریخچه تماس و وضعیت معامله را همزمان نشان می‌دهد. تیم‌ها با دید مشترک، هماهنگ‌تر پیش می‌روند و نرخ تبدیل بالاتر می‌رود.
<p className="max-w-2xl px-2 text-center text-pretty text-muted-foreground sm:mt-5 sm:px-6 lg:text-xl">
پروفایل یکپارچه مشتری، تاریخچه تماس و وضعیت معامله را همزمان نشان
می‌دهد. تیم‌ها با دید مشترک، هماهنگ‌تر پیش می‌روند و نرخ تبدیل بالاتر
می‌رود.
</p>
<div className="flex flex-col gap-4 sm:mt-8 md:flex-row">
<Button className="h-11 w-full rounded-xl sm:w-auto">
<div className="flex w-full min-w-0 max-w-2xl flex-col gap-4 sm:mt-8 md:flex-row md:justify-center">
<Button className="h-auto min-h-11 w-full rounded-xl px-4 py-2 whitespace-normal sm:w-auto">
روابط مشتری را در یک نگاه ببینید
</Button>
<Button
variant="outline"
className="h-11 w-full gap-2 rounded-xl sm:w-auto"
className="h-auto min-h-11 w-full gap-2 rounded-xl px-4 py-2 whitespace-normal sm:w-auto"
>
پایپ‌لاین فروش را شفاف کنید
<ArrowRight className="size-4 rtl:rotate-180" />
<ArrowRight className="size-4 shrink-0 rtl:rotate-180" />
</Button>
</div>
</div>
@@ -61,12 +80,12 @@ const Feature236 = ({ className }: Feature236Props) => {
const BracketedBorder = ({ children }: { children: ReactNode }) => (
<motion.div
initial={{ width: 0, opacity: 0 }}
className="relative flex flex-col items-center justify-center sm:my-10 md:h-[500px] md:overflow-hidden"
whileInView={{ width: "65vw", opacity: 1 }}
initial={{ opacity: 0, scaleX: 0.85 }}
className="relative flex w-full min-w-0 max-w-full flex-col items-center justify-center overflow-hidden sm:my-10 md:min-h-[500px]"
whileInView={{ opacity: 1, scaleX: 1 }}
viewport={{ once: true }}
transition={{
width: {
scaleX: {
type: "spring",
stiffness: 50,
damping: 20,
@@ -86,7 +105,7 @@ const BracketedBorder = ({ children }: { children: ReactNode }) => (
<Vector className="absolute start-1 bottom-1" />
<Vector className="absolute end-1 bottom-1 -rotate-90" />
</div>
<div className="flex h-full w-screen flex-col items-center justify-center">
<div className="flex w-full min-w-0 max-w-4xl flex-col items-center justify-center px-4 py-8 md:max-w-[65vw] md:py-12">
{children}
</div>
</motion.div>
+50 -34
View File
@@ -7,45 +7,53 @@ import { cn } from "@/lib/utils";
const Feature239 = () => {
return (
<section dir="rtl" lang="fa" className="bg-background py-32">
<div className="relative container flex flex-col items-center px-0! lg:pt-8">
<DottedDiv>
<div className="grid lg:grid-cols-2">
<div className="flex w-full flex-col gap-8 px-10 py-20 text-start md:px-14">
<section
dir="rtl"
lang="fa"
className="w-full min-w-0 overflow-x-hidden bg-background py-12 sm:py-20 lg:py-32"
>
<div className="relative container flex min-w-0 flex-col items-center px-4 sm:px-6 lg:px-8 lg:pt-8">
<DottedDiv className="w-full min-w-0">
<div className="grid min-w-0 lg:grid-cols-2">
<div className="flex w-full min-w-0 flex-col gap-6 px-2 py-10 text-start sm:gap-8 sm:px-6 sm:py-14 md:px-10 md:py-20 lg:px-14">
<Badge
variant="outline"
className="flex w-fit cursor-pointer items-center gap-4 rounded-full px-6 py-2 transition-all ease-in-out hover:gap-6"
className="flex h-auto w-full max-w-full cursor-pointer items-start gap-3 rounded-full px-4 py-3 whitespace-normal transition-all ease-in-out hover:gap-4 sm:w-fit sm:items-center sm:px-6 sm:py-2"
>
<span className="text-sm font-medium tracking-tight text-muted-foreground">
قوانین ساده تعریف کنید تا وظایف تکراری بدون دخالت دست انجام شود. تیم روی تصمیم‌های مهم تمرکز می‌کند و سرعت اجرا بالاتر می‌رود.
<span className="min-w-0 text-sm font-medium tracking-tight text-pretty text-muted-foreground">
قوانین ساده تعریف کنید تا وظایف تکراری بدون دخالت دست انجام
شود. تیم روی تصمیم‌های مهم تمرکز می‌کند و سرعت اجرا بالاتر
می‌رود.
</span>
<ChevronRight className="size-4! rtl:rotate-180" />
<ChevronRight className="size-4 shrink-0 rtl:rotate-180" />
</Badge>
<h1 className="text-2xll font-semibold tracking-tighter text-balance md:text-2xll">
<h1 className="text-2xl font-semibold tracking-tighter text-balance text-foreground sm:text-3xl md:text-2xll">
فرایندها را استاندارد و سریع کنید
<br />
اتوماسیون را در کل سازمان یکپارچه کنید
<br />
خطای انسانی را در کارهای روزمره کم کنید
</h1>
<p className="tracking-tight text-pretty text-muted-foreground md:text-xl">
فرایندهای بین‌تیمی را در یک بوم بصری طراحی کنید. هر مرحله قابل ردیابی است و گلوگاه‌ها قبل از تأخیر جدی مشخص می‌شوند.
<p className="tracking-tight text-pretty text-muted-foreground sm:text-lg md:text-xl">
فرایندهای بین‌تیمی را در یک بوم بصری طراحی کنید. هر مرحله قابل
ردیابی است و گلوگاه‌ها قبل از تأخیر جدی مشخص می‌شوند.
</p>
<div className="flex w-full gap-2">
<Button className="text-md h-12 w-fit rounded-full bg-primary px-10 text-primary-foreground">
<div className="flex w-full flex-wrap gap-2">
<Button className="text-md h-12 w-fit rounded-full bg-primary px-8 text-primary-foreground sm:px-10">
شروع کنید
</Button>
<Button
variant="outline"
className="text-md h-12 w-12 rounded-full transition-all ease-in-out hover:rotate-45"
className="text-md h-12 w-12 shrink-0 rounded-full transition-all ease-in-out hover:rotate-45"
>
<ArrowUpRight className="rtl:rotate-180" />
</Button>
</div>
</div>
<DottedDiv className="group size-full place-self-end p-4 lg:w-4/6">
<div className="relative size-full bg-muted/50 p-4 transition-all ease-in-out group-hover:bg-muted">
<div className="relative size-full overflow-hidden rounded-3xl">
<DottedDiv className="group min-w-0 place-self-end p-2 sm:p-4 lg:w-4/6">
<div className="relative min-h-80 w-full bg-muted/50 p-3 transition-all ease-in-out group-hover:bg-muted sm:min-h-96 sm:p-4">
<div className="relative size-full min-h-72 overflow-hidden rounded-2xl sm:min-h-80 sm:rounded-3xl">
<img
src="/block-media/shadcnblocks/block/photos/simone-hutsch-5oYbG-sEImY-unsplash.jpg"
alt="سازنده گردش‌کار"
@@ -54,32 +62,40 @@ const Feature239 = () => {
<div className="absolute inset-0 bg-linear-to-t from-black/70 to-transparent" />
</div>
<div className="absolute top-4 -ms-4 flex size-full flex-col items-center justify-between p-10">
<p className="flex w-full items-center text-xl tracking-tighter text-background">
با اتصال ابزارهای موجود، داده بین سیستم‌ها خودکار جریان پیدا می‌کند و نیاز به کپی دستی از بین می‌رود.
<span className="mx-2 h-2.5 w-px bg-background" />
قوانین ساده تعریف کنید تا وظایف تکراری بدون دخالت دست انجام شود. تیم روی تصمیم‌های مهم تمرکز می‌کند و سرعت اجرا بالاتر می‌رود.
<div className="absolute inset-0 flex flex-col items-center justify-between p-4 sm:p-6 lg:p-10">
<p className="flex w-full min-w-0 flex-col gap-2 text-sm tracking-tighter text-pretty text-background sm:flex-row sm:items-center sm:text-base lg:text-xl">
<span className="min-w-0">
با اتصال ابزارهای موجود، داده بین سیستم‌ها خودکار جریان
پیدا می‌کند و نیاز به کپی دستی از بین می‌رود.
</span>
<span className="hidden h-2.5 w-px shrink-0 bg-background sm:block" />
<span className="min-w-0">
قوانین ساده تعریف کنید تا وظایف تکراری بدون دخالت دست
انجام شود.
</span>
</p>
<div className="flex flex-col items-center justify-center">
<h2 className="text-center text-2xll font-semibold tracking-tight text-balance text-background">
<div className="flex flex-col items-center justify-center px-2">
<h2 className="text-center text-xl font-semibold tracking-tight text-balance text-background sm:text-2xl lg:text-2xll">
کارهای تکراری را خودکار کنید
<br />
گردش‌کار تیم را بدون کد بسازید
</h2>
<div className="mt-2 h-1 w-6 rounded-full bg-background" />
<p className="mt-10 max-w-sm px-2 text-center text-lg leading-5 font-light tracking-tighter text-pretty text-background/80">
فرایندهای بین‌تیمی را در یک بوم بصری طراحی کنید. هر مرحله قابل ردیابی است و گلوگاه‌ها قبل از تأخیر جدی مشخص می‌شوند.
<p className="mt-6 max-w-sm text-center text-sm leading-5 font-light tracking-tighter text-pretty text-background/80 sm:mt-10 sm:text-lg">
فرایندهای بین‌تیمی را در یک بوم بصری طراحی کنید. هر مرحله
قابل ردیابی است و گلوگاه‌ها قبل از تأخیر جدی مشخص
می‌شوند.
</p>
</div>
<a
href="#"
className="group mb-6 flex cursor-pointer flex-col items-center justify-center text-background"
className="group mb-2 flex cursor-pointer flex-col items-center justify-center text-background sm:mb-6"
>
<ChevronUp
size={30}
className="transition-all ease-in-out group-hover:-translate-y-2"
/>
<p className="text-xl tracking-tight text-background">
<p className="text-base tracking-tight text-background sm:text-xl">
مشاهده همه
</p>
</a>
@@ -102,11 +118,11 @@ const DottedDiv = ({
children: React.ReactNode;
className?: string;
}) => (
<div className={cn("relative", className)}>
<div className="absolute top-4 -start-25 h-px w-[115%] bg-muted" />
<div className="absolute bottom-4 -start-25 h-px w-[115%] bg-muted" />
<div className="absolute -top-25 start-4 h-[130%] w-px bg-muted" />
<div className="absolute -top-25 end-4 h-[130%] w-px bg-muted" />
<div className={cn("relative min-w-0 overflow-hidden", className)}>
<div className="absolute top-4 start-4 end-4 h-px bg-muted lg:end-auto lg:w-[115%] lg:-start-25" />
<div className="absolute bottom-4 start-4 end-4 h-px bg-muted lg:end-auto lg:w-[115%] lg:-start-25" />
<div className="absolute start-4 top-4 bottom-4 w-px bg-muted lg:top-auto lg:bottom-auto lg:h-[130%] lg:-top-25" />
<div className="absolute end-4 top-4 bottom-4 w-px bg-muted lg:top-auto lg:bottom-auto lg:h-[130%] lg:-top-25" />
<div className="absolute start-[12.5px] top-[12.5px] z-10 size-2 rounded-full bg-foreground" />
<div className="absolute end-[12.5px] top-[12.5px] z-10 size-2 rounded-full bg-foreground" />
<div className="absolute start-[12.5px] bottom-[12.5px] z-10 size-2 rounded-full bg-foreground" />
+3 -3
View File
@@ -133,9 +133,9 @@ const Feature240 = () => {
))}
</CarouselContent>
<div className="mt-5 flex justify-end gap-3">
<CarouselPrevious className="static size-12" />
<CarouselNext variant="default" className="static size-12" />
<div className="mt-5 flex justify-start gap-3">
<CarouselPrevious className="static size-12 translate-y-0 rtl:rotate-180" />
<CarouselNext variant="default" className="static size-12 translate-y-0 rtl:rotate-180" />
</div>
</Carousel>
</div>
+5 -26
View File
@@ -1,6 +1,6 @@
"use client";
import { ChevronLeft, ChevronRight, Plus } from "lucide-react";
import { ChevronRight, Plus } from "lucide-react";
import React from "react";
import { Button } from "@/components/ui/button";
@@ -9,6 +9,8 @@ import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
import { cn } from "@/lib/utils";
@@ -149,32 +151,9 @@ const Feature242 = ({ className }: Feature242Props) => {
</span>
</div>
{/*
Embla RTL: scrollNext/scrollPrev are index-based, so default
ChevronLeft→prev / ChevronRight→next feels reversed visually.
Match arrow direction to movement by swapping handlers.
*/}
<div className="relative z-10 flex gap-2">
<Button
type="button"
variant="outline"
size="icon"
className="size-10 rounded-full"
onClick={() => api?.scrollNext()}
aria-label="اسلاید بعدی"
>
<ChevronLeft className="size-4" />
</Button>
<Button
type="button"
variant="default"
size="icon"
className="size-10 rounded-full"
onClick={() => api?.scrollPrev()}
aria-label="اسلاید قبلی"
>
<ChevronRight className="size-4" />
</Button>
<CarouselPrevious className="static size-10 translate-y-0 rtl:rotate-180" />
<CarouselNext className="static size-10 translate-y-0 rtl:rotate-180" />
</div>
</div>
</Carousel>
+3 -3
View File
@@ -93,11 +93,11 @@ const Feature244 = () => {
</CarouselItem>
))}
</CarouselContent>
<div className="my-3 flex justify-end gap-3 pe-5">
<CarouselPrevious className="static m-0 size-12 translate-y-0 p-0" />
<div className="my-3 flex justify-start gap-3 ps-4 sm:ps-5">
<CarouselPrevious className="static m-0 size-12 translate-y-0 p-0 rtl:rotate-180" />
<CarouselNext
variant="default"
className="static size-12 translate-y-0"
className="static size-12 translate-y-0 rtl:rotate-180"
/>
</div>
</Carousel>
+15 -11
View File
@@ -7,7 +7,7 @@ import { Card, CardContent } from "@/components/ui/card";
import { cn } from "@/lib/utils";
const handwrittenTextClasses =
"flex gap-3 font-handwriting text-xs tracking-tight md:text-xl";
"flex min-w-0 items-center gap-2 font-handwriting text-xs tracking-tight xl:gap-3 xl:text-xl";
const FEATURES = [
{ id: "dark-mode", label: "ترافیک ارگانیک" },
@@ -97,9 +97,13 @@ interface Feature245Props {
const Feature245 = ({ className }: Feature245Props) => {
return (
<section dir="rtl" lang="fa" className={cn("bg-background", className)}>
<div className="relative container flex flex-col items-center overflow-hidden py-32">
<div className="mx-auto max-w-3xl text-center">
<section
dir="rtl"
lang="fa"
className={cn("relative w-full min-w-0 overflow-hidden bg-background", className)}
>
<div className="relative container flex min-w-0 flex-col items-center overflow-hidden py-16 md:py-24 lg:py-32">
<div className="mx-auto w-full min-w-0 max-w-3xl px-4 text-center">
<h1 className="text-2xll font-semibold tracking-tighter text-balance text-foreground md:text-2xll">
رتبه کلمات کلیدی را پیگیری کنید
</h1>
@@ -107,8 +111,8 @@ const Feature245 = ({ className }: Feature245Props) => {
تحلیل فنی، محتوا و رقبا در یک ابزار جمع می‌شود. تیم محتوا می‌داند روی کدام صفحه و کلمه باید تمرکز کند.
</p>
</div>
<div className="relative z-10 mx-auto mt-12 min-h-full max-w-3xl md:w-1/2">
<div className="absolute -start-52 z-10 hidden h-full flex-col items-end justify-between md:flex lg:-start-62">
<div className="relative z-10 mx-auto mt-12 w-full min-w-0 max-w-3xl px-4 lg:w-1/2 lg:px-0">
<div className="absolute -start-40 z-10 hidden h-full flex-col items-end justify-between xl:flex xl:-start-52 2xl:-start-62">
{[0, 1, 2, 3].map((i) => (
<motion.div
key={FEATURES[i].id}
@@ -129,7 +133,7 @@ const Feature245 = ({ className }: Feature245Props) => {
</motion.div>
))}
</div>
<div className="absolute -end-58 z-10 hidden h-full flex-col justify-between md:flex lg:-end-64">
<div className="absolute -end-40 z-10 hidden h-full flex-col justify-between xl:flex xl:-end-52 2xl:-end-64">
{[4, 5, 6, 7].map((i) => (
<motion.div
key={FEATURES[i].id}
@@ -152,12 +156,12 @@ const Feature245 = ({ className }: Feature245Props) => {
</div>
<motion.div
initial={{ opacity: 0, y: 200 }}
animate={{ opacity: 100, y: 0 }}
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
transition={{ ease: [0.33, 1, 0.68, 1], duration: 0.8 }}
className="relative z-0"
className="relative z-0 w-full min-w-0"
>
<Card className="group h-130 w-full rounded-4xl border border-border bg-background p-2 shadow-none lg:p-3">
<Card className="group aspect-[4/3] h-auto max-h-80 w-full rounded-4xl border border-border bg-background p-2 shadow-none sm:max-h-96 xl:h-130 xl:max-h-none xl:aspect-auto lg:p-3">
<CardContent className="size-full rounded-3xl border-2 border-background bg-muted">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
+14 -10
View File
@@ -24,10 +24,14 @@ const Feature251 = ({ className }: Feature251Props) => {
const div6Ref = useRef<HTMLDivElement>(null);
const div7Ref = useRef<HTMLDivElement>(null);
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<div className="mx-auto flex max-w-5xl flex-wrap items-center justify-center gap-5">
<Card className="relative h-96 w-166 rounded-3xl border">
<section
dir="rtl"
lang="fa"
className={cn("relative w-full min-w-0 overflow-hidden py-16 md:py-24 lg:py-32", className)}
>
<div className="container min-w-0 px-4 sm:px-6">
<div className="mx-auto flex w-full min-w-0 max-w-5xl flex-wrap items-center justify-center gap-5">
<Card className="relative h-96 w-full min-w-0 overflow-hidden rounded-3xl border md:w-166">
<CardHeader className="text-start">
<h3 className="text-xl font-semibold tracking-tight text-balance">
تریگر رویداد
@@ -36,7 +40,7 @@ const Feature251 = ({ className }: Feature251Props) => {
شرط هوشمند
</p>
</CardHeader>
<CardContent ref={containerRef1} className="relative ms-5">
<CardContent ref={containerRef1} className="relative ms-2 overflow-hidden sm:ms-5">
<IconCard
ref={div1Ref as React.RefObject<HTMLDivElement>}
src="/block-media/shadcnblocks/block/logos/google-icon.svg"
@@ -47,7 +51,7 @@ const Feature251 = ({ className }: Feature251Props) => {
ref={div2Ref as React.RefObject<HTMLDivElement>}
src="/block-media/shadcnblocks/block/logos/figma-icon.svg"
imageAlt="نمای اتوماسیون فرایند"
className="translate-x-32 rtl:-translate-x-32"
className="translate-x-12 rtl:-translate-x-12 sm:translate-x-20 sm:rtl:-translate-x-20 md:translate-x-32 md:rtl:-translate-x-32"
/>
<IconCard
ref={div3Ref as React.RefObject<HTMLDivElement>}
@@ -59,7 +63,7 @@ const Feature251 = ({ className }: Feature251Props) => {
ref={div5Ref as React.RefObject<HTMLDivElement>}
src="/block-media/shadcnblocks/block/block-1.svg"
imageAlt="سازنده گردش‌کار"
className="absolute end-12 top-1/2 -translate-y-1/2"
className="absolute end-4 top-1/2 -translate-y-1/2 sm:end-8 md:end-12"
/>
<div
ref={div4Ref as React.RefObject<HTMLDivElement>}
@@ -94,7 +98,7 @@ const Feature251 = ({ className }: Feature251Props) => {
</CardContent>
</Card>
<Card className="h-96 w-full rounded-3xl border md:w-83">
<Card className="h-96 w-full min-w-0 overflow-hidden rounded-3xl border md:w-83">
<CardHeader className="text-start">
<h3 className="text-xl font-semibold tracking-tight text-balance">
اعلان خودکار
@@ -132,7 +136,7 @@ const Feature251 = ({ className }: Feature251Props) => {
</CardContent>
</Card>
<Card className="relative flex h-96 w-full flex-col rounded-3xl border md:w-[330px]">
<Card className="relative flex h-96 w-full min-w-0 flex-col overflow-hidden rounded-3xl border md:w-[330px]">
<CardContent>
<img
src="/block-media/shadcnblocks/block/block-1.svg"
@@ -149,7 +153,7 @@ const Feature251 = ({ className }: Feature251Props) => {
</p>
</CardHeader>
</Card>
<Card className="relative flex h-96 w-166 flex-col overflow-hidden rounded-3xl border">
<Card className="relative flex h-96 w-full min-w-0 flex-col overflow-hidden rounded-3xl border md:w-166">
<CardHeader className="relative z-10 shrink-0 text-start">
<h3 className="text-xl font-semibold tracking-tight text-balance">
اتصال ابزار
+26 -17
View File
@@ -72,12 +72,19 @@ const Feature313 = ({
const isMobile = useIsMobile();
return (
<section dir="rtl" lang="fa" className={cn("bg-muted py-24", className)}>
<div className="container flex flex-col gap-2">
<div className="grid grid-cols-1 gap-1 lg:grid-cols-4">
<section
dir="rtl"
lang="fa"
className={cn("relative w-full min-w-0 overflow-hidden bg-muted py-16 md:py-24", className)}
>
<div className="container flex min-w-0 flex-col gap-2 px-4 sm:px-6">
<div className="grid w-full min-w-0 grid-cols-1 gap-1 lg:grid-cols-4">
{cards.map((card, index) => (
<Card key={card.id} className="border-none p-10 text-start shadow-none">
<div className="flex items-center justify-between gap-2">
<Card
key={card.id}
className="min-w-0 overflow-hidden border-none p-5 text-start shadow-none sm:p-8 lg:p-10"
>
<div className="flex min-w-0 items-center justify-between gap-2">
<div className="flex gap-1">
{cards.map((_, circleIndex) => (
<div
@@ -90,7 +97,7 @@ const Feature313 = ({
{(index + 1).toLocaleString("fa-IR").padStart(2, "۰")}
</div>
</div>
<div className="flex flex-col gap-4 md:flex-row md:justify-between">
<div className="flex min-w-0 flex-col gap-4 md:flex-row md:justify-between">
<div className="aspect-square size-12 shrink-0">
<img
src={card.image}
@@ -99,7 +106,7 @@ const Feature313 = ({
/>
</div>
<div className="min-w-0 flex-1">
<h3 className="text-md leading-tight font-medium text-pretty">
<h3 className="text-md leading-tight font-medium text-balance break-words">
{card.title}
</h3>
</div>
@@ -111,7 +118,7 @@ const Feature313 = ({
<Dialog>
<DialogTrigger asChild>
<motion.div
className="group relative aspect-video min-h-72 cursor-pointer overflow-hidden rounded-lg"
className="group relative aspect-video w-full min-w-0 min-h-48 cursor-pointer overflow-hidden rounded-lg sm:min-h-72"
whileHover="hover"
initial="initial"
>
@@ -126,10 +133,10 @@ const Feature313 = ({
<motion.img
src={imageSrc}
alt={imageAlt}
className="h-full w-full rounded-lg object-cover"
className="h-full w-full max-w-full rounded-lg object-cover"
variants={{
initial: { scale: 1 },
hover: { scale: 1.1 },
hover: isMobile ? { scale: 1 } : { scale: 1.1 },
}}
transition={{ duration: 0.3, ease: "easeOut" }}
/>
@@ -148,7 +155,7 @@ const Feature313 = ({
initial: { x: 0, scale: 1 },
hover: {
x: isMobile ? 0 : "-75%",
scale: 1.2,
scale: isMobile ? 1 : 1.2,
},
}}
transition={{ duration: 0.3, ease: "easeOut" }}
@@ -163,18 +170,20 @@ const Feature313 = ({
</motion.div>
<motion.div
className="text-center md:text-start"
className="min-w-0 text-center md:text-start"
variants={{
initial: { opacity: 1 },
hover: { opacity: 0, transform: "-translate-y-3" },
hover: isMobile
? { opacity: 1 }
: { opacity: 0, transform: "-translate-y-3" },
}}
transition={{ duration: 0.3, ease: "easeOut" }}
>
<div className="px-4 py-2">
<p className="text-lg font-bold text-secondary">
<div className="px-2 py-2 sm:px-4">
<p className="text-base font-bold text-balance break-words text-secondary sm:text-lg">
{videoCaption}
</p>
<p className="text-lg font-medium text-muted-foreground">
<p className="text-base font-medium text-balance break-words text-muted-foreground sm:text-lg">
{videoSecondaryCaption}
</p>
</div>
@@ -183,7 +192,7 @@ const Feature313 = ({
</div>
</motion.div>
</DialogTrigger>
<DialogContent className="w-full border-none bg-black p-0 md:max-w-6xl md:p-1">
<DialogContent className="w-[calc(100%-2rem)] max-w-[calc(100%-2rem)] border-none bg-black p-0 sm:w-full md:max-w-6xl md:p-1">
<DialogTitle className="sr-only">{videoCaption}</DialogTitle>
<DialogDescription className="sr-only">
{videoSecondaryCaption} {videoCaption}
+36 -5
View File
@@ -8,7 +8,7 @@ import {
useScroll,
useTransform,
} from "framer-motion";
import React, { useRef, useState } from "react";
import React, { useLayoutEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
@@ -228,11 +228,31 @@ const Feature322 = ({
testimonial = DEFAULT_TESTIMONIAL,
}: Feature322Props) => {
const scrollRef = useRef<HTMLElement>(null);
const [stageHeight, setStageHeight] = useState(0);
const { scrollYProgress } = useScroll({
container: scrollRef,
});
const [activeIndex, setActiveIndex] = useState(0);
useLayoutEffect(() => {
const scrollContainer = scrollRef.current;
if (!scrollContainer) return;
const updateStageHeight = () => {
setStageHeight(scrollContainer.clientHeight);
};
updateStageHeight();
const observer = new ResizeObserver(updateStageHeight);
observer.observe(scrollContainer);
return () => observer.disconnect();
}, []);
const scrollSegmentCount = Math.max(features.length - 1, 1);
const scrollSpacerHeight =
stageHeight > 0 ? stageHeight * scrollSegmentCount : undefined;
useMotionValueEvent(scrollYProgress, "change", (latest) => {
const nextIndex = getActiveFeatureIndex(latest, features.length);
setActiveIndex((prev) => (prev === nextIndex ? prev : nextIndex));
@@ -246,14 +266,21 @@ const Feature322 = ({
dir="rtl"
lang="fa"
className={cn(
"relative h-svh w-full min-w-[20rem] max-w-full overflow-x-clip overflow-y-auto sm:min-w-[36rem] lg:min-w-[64rem]",
"relative h-svh max-h-full min-h-[28rem] w-full min-w-[20rem] max-w-full overflow-x-clip overflow-y-auto overscroll-contain sm:min-w-[36rem] lg:min-h-[32rem] lg:min-w-[64rem]",
className,
)}
>
<div className="container min-w-0 w-full max-w-full">
{/* Desktop: sticky stage + scroll spacer */}
<div className="relative hidden lg:block">
<div className="sticky top-0 flex h-svh w-full min-w-0 flex-col items-center justify-center">
<div
className="sticky top-0 z-10 flex w-full min-w-0 flex-col items-center justify-center"
style={
stageHeight > 0
? { height: stageHeight }
: { minHeight: "28rem" }
}
>
<div className="relative w-full min-w-0 max-w-full">
<AnimatePresence mode="wait" initial={false}>
{activeFeature ? (
@@ -279,8 +306,12 @@ const Feature322 = ({
</div>
<div
style={{ height: `${Math.max(features.length, 1) * 75}vh` }}
className="pointer-events-none w-full"
style={
scrollSpacerHeight
? { height: scrollSpacerHeight }
: { height: `${scrollSegmentCount * 75}vh` }
}
className="pointer-events-none w-full shrink-0"
aria-hidden
/>
</div>
+1 -1
View File
@@ -70,7 +70,7 @@ const Feature52 = ({ className }: Feature52Props) => {
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<Tabs defaultValue="feature-1">
<Tabs defaultValue="feature-1" dir="rtl">
<TabsList className="flex h-auto w-full flex-wrap items-stretch justify-between gap-2 bg-background p-0 group-data-horizontal/tabs:h-auto">
{tabs.map((tab, index) => (
<TabsTrigger
+2 -2
View File
@@ -1,5 +1,5 @@
import {
ArrowRight,
ArrowLeft,
Blocks,
MessagesSquare,
MoveRight,
@@ -26,7 +26,7 @@ const Hero26 = ({ className }: Hero26Props) => {
<Badge className="rounded-full">نسخه جدید</Badge>
معرفی Collaboration 2.0
<span className="flex size-7 items-center justify-center rounded-full bg-muted">
<ArrowRight className="w-4" />
<ArrowLeft className="w-4" />
</span>
</a>
<h1 className="mx-auto mt-4 mb-3 max-w-3xl text-xl font-semibold text-balance lg:mb-7 lg:text-2xll">
+77 -41
View File
@@ -8,21 +8,33 @@ import {
Share2,
Sparkles,
Video,
type LucideIcon,
} from "lucide-react";
import { Marquee } from "@/components/ui/marquee";
import {
Marquee,
MarqueeContent,
MarqueeFade,
MarqueeItem,
} from "@/components/kibo-ui/marquee";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
const integrations = [
const integrations: {
name: string;
description: string;
icon: LucideIcon;
}[] = [
{
name: "ذخیره‌سازی",
description: "فایل‌های ویدیویی خود را به‌صورت امن در فضای ذخیره‌سازی دلخواه مدیریت کنید.",
description:
"فایل‌های ویدیویی خود را به‌صورت امن در فضای ذخیره‌سازی دلخواه مدیریت کنید.",
icon: Cloud,
},
{
name: "هوش مصنوعی",
description: "با کمک هوش مصنوعی، تولید محتوای ویدیویی و تصویری را سرعت دهید.",
description:
"با کمک هوش مصنوعی، تولید محتوای ویدیویی و تصویری را سرعت دهید.",
icon: Sparkles,
},
{
@@ -37,7 +49,8 @@ const integrations = [
},
{
name: "شبکه‌های اجتماعی",
description: "ویدیوهای خود را تنها با چند کلیک در شبکه‌های اجتماعی منتشر کنید.",
description:
"ویدیوهای خود را تنها با چند کلیک در شبکه‌های اجتماعی منتشر کنید.",
icon: Share2,
},
{
@@ -47,6 +60,27 @@ const integrations = [
},
];
function IntegrationCard({
name,
description,
icon: Icon,
}: (typeof integrations)[number]) {
return (
<div
dir="rtl"
className="flex h-28 w-72 shrink-0 gap-2 rounded-lg border border-border p-4 text-right dark:bg-muted"
>
<Icon className="mt-0.5 size-4 shrink-0" />
<div className="flex min-w-0 flex-1 flex-col gap-2">
<p className="text-sm font-medium">{name}</p>
<p className="line-clamp-3 text-sm text-muted-foreground">
{description}
</p>
</div>
</div>
);
}
interface Hero40Props {
className?: string;
}
@@ -59,10 +93,11 @@ const Hero40 = ({ className }: Hero40Props) => {
<div className="flex flex-col gap-6">
<h1 className="text-2xll font-bold">
همه ابزارهای موردنیاز خود را یکپارچه کنید
</h1>
</h1>
<p className="text-lg text-muted-foreground">
تمام سرویس‌ها و ابزارهای کاری خود را به یکدیگر متصل کنید و فرایند تولید و انتشار محتوا را ساده‌تر کنید.
</p>
تمام سرویس‌ها و ابزارهای کاری خود را به یکدیگر متصل کنید و فرایند
تولید و انتشار محتوا را ساده‌تر کنید.
</p>
<div className="flex gap-4">
<Button variant="outline" size="lg">
<span>مشاهده دمو</span>
@@ -100,41 +135,42 @@ const Hero40 = ({ className }: Hero40Props) => {
alt="تصویر نمونه"
className="aspect-video w-full rounded-lg border border-border object-cover"
/>
<div className="relative flex w-full flex-col items-center justify-center overflow-hidden">
<Marquee className="[--duration:30s]">
{integrations.map((integration) => (
<div
key={integration.name}
className="flex max-w-72 gap-2 rounded-lg border border-border p-4 dark:bg-muted"
>
<integration.icon className="mt-0.5 size-4 shrink-0" />
<div className="flex flex-col gap-2">
<p className="text-sm">{integration.name}</p>
<p className="text-sm text-muted-foreground">
{integration.description}
</p>
</div>
</div>
))}
<div
dir="rtl"
className="relative flex w-full flex-col items-center justify-center gap-4 overflow-hidden"
>
<Marquee className="w-full">
<MarqueeContent
direction="right"
speed={40}
loop={0}
autoFill
pauseOnHover={false}
>
{integrations.map((integration) => (
<MarqueeItem key={`fwd-${integration.name}`}>
<IntegrationCard {...integration} />
</MarqueeItem>
))}
</MarqueeContent>
</Marquee>
<Marquee reverse className="[--duration:30s]">
{integrations.map((integration) => (
<div
key={integration.name}
className="flex max-w-72 gap-2 rounded-lg border border-border p-4 dark:bg-muted"
>
<integration.icon className="mt-0.5 size-4 shrink-0" />
<div className="flex flex-col gap-2">
<p className="text-sm">{integration.name}</p>
<p className="text-sm text-muted-foreground">
{integration.description}
</p>
</div>
</div>
))}
<Marquee className="w-full">
<MarqueeContent
direction="right"
speed={40}
loop={0}
autoFill
pauseOnHover={false}
>
{integrations.map((integration) => (
<MarqueeItem key={`rev-${integration.name}`}>
<IntegrationCard {...integration} />
</MarqueeItem>
))}
</MarqueeContent>
</Marquee>
<div className="pointer-events-none absolute inset-y-0 left-0 w-1/4 bg-gradient-to-r from-background"></div>
<div className="pointer-events-none absolute inset-y-0 right-0 w-1/4 bg-gradient-to-l from-background"></div>
<MarqueeFade side="left" />
<MarqueeFade side="right" />
</div>
</div>
</div>
+9 -6
View File
@@ -1,3 +1,5 @@
"use client";
import { ArrowLeft } from "lucide-react";
import { Button } from "@/components/ui/button";
@@ -91,19 +93,20 @@ const Testimonial31 = ({
</Button>
</div>
<div className="relative mt-8 -mr-[max(3rem,calc((100vw-80rem)/2+3rem))] md:mt-12 lg:mt-20">
<div className="relative mt-8 -ml-[max(3rem,calc((100vw-80rem)/2+3rem))] md:mt-12 lg:mt-20">
<Carousel
opts={{
align: "start",
loop: true,
direction: "rtl",
}}
className="w-full"
>
<CarouselContent>
<CarouselContent className="-mr-4 ml-0">
{testimonials.map((testimonial, index) => (
<CarouselItem
key={index}
className="basis-4/5 sm:basis-3/5 md:basis-2/5 lg:basis-[28%] xl:basis-[28%] 2xl:basis-1/4"
className="basis-4/5 pl-0 pr-4 sm:basis-3/5 md:basis-2/5 lg:basis-[28%] xl:basis-[28%] 2xl:basis-1/4"
>
<Card className="h-full overflow-hidden border-none bg-muted !pt-0">
<CardContent className="flex h-full flex-col p-0">
@@ -132,9 +135,9 @@ const Testimonial31 = ({
</CarouselItem>
))}
</CarouselContent>
<div className="mt-8 flex gap-3">
<CarouselPrevious className="static size-14 translate-x-0 translate-y-0 bg-muted transition-colors hover:bg-muted/80 [&>svg]:size-6 lg:[&>svg]:size-8" />
<CarouselNext className="static size-14 translate-x-0 translate-y-0 bg-muted transition-colors hover:bg-muted/80 [&>svg]:size-6 lg:[&>svg]:size-8" />
<div className="mt-8 flex justify-start gap-3">
<CarouselPrevious className="static size-14 translate-x-0 translate-y-0 bg-muted transition-colors hover:bg-muted/80 rtl:rotate-180 [&>svg]:size-6 lg:[&>svg]:size-8" />
<CarouselNext className="static size-14 translate-x-0 translate-y-0 bg-muted transition-colors hover:bg-muted/80 rtl:rotate-180 [&>svg]:size-6 lg:[&>svg]:size-8" />
</div>
</Carousel>
</div>