diff --git a/src/components/FadeIn.tsx b/src/components/FadeIn.tsx new file mode 100644 index 0000000..7751384 --- /dev/null +++ b/src/components/FadeIn.tsx @@ -0,0 +1,28 @@ +'use client' + +import { motion, type Transition } from 'framer-motion' +import React from 'react' +import { useMediaQuery } from '@/hooks/useMediaQuery' + +type FadeInProps = { + children: React.ReactNode + transition?: Transition + className?: string +} + +export function FadeIn({ children, transition, className }: FadeInProps) { + const isMobile = useMediaQuery('(max-width: 768px)') + + return ( + + {children} + + ) +} + diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts new file mode 100644 index 0000000..7ef31e3 --- /dev/null +++ b/src/hooks/useMediaQuery.ts @@ -0,0 +1,21 @@ +'use client' + +import { useState, useEffect } from 'react' + +export function useMediaQuery(query: string) { + const [matches, setMatches] = useState(false) + + useEffect(() => { + const media = window.matchMedia(query) + if (media.matches !== matches) { + setMatches(media.matches) + } + const listener = () => { + setMatches(media.matches) + } + media.addEventListener('change', listener) + return () => media.removeEventListener('change', listener) + }, [matches, query]) + + return matches +} diff --git a/src/hooks/useResponsiveCarousel.ts b/src/hooks/useResponsiveCarousel.ts new file mode 100644 index 0000000..624e769 --- /dev/null +++ b/src/hooks/useResponsiveCarousel.ts @@ -0,0 +1,39 @@ +'use client' + +import { useState, useEffect } from 'react'; + +// đź”§ Carousel Config +const desktopConfig = { + GAP: 300, + ROT_Y: 18, + DEPTH: 210, + SCALE_DROP: 0.12, +}; + +const mobileConfig = { + GAP: 110, // Smaller gap for mobile + ROT_Y: 0, // Flatter view on mobile + DEPTH: 150, // Less depth + SCALE_DROP: 0.1, // Less aggressive scaling +}; + +export const useResponsiveCarousel = () => { + const [config, setConfig] = useState(desktopConfig); + + useEffect(() => { + const checkScreenSize = () => { + if (window.innerWidth < 768) { + setConfig(mobileConfig); + } else { + setConfig(desktopConfig); + } + }; + + checkScreenSize(); + window.addEventListener('resize', checkScreenSize); + + return () => window.removeEventListener('resize', checkScreenSize); + }, []); + + return config; +}; diff --git a/src/hooks/useScroll.ts b/src/hooks/useScroll.ts new file mode 100644 index 0000000..fe5eacd --- /dev/null +++ b/src/hooks/useScroll.ts @@ -0,0 +1,45 @@ +'use client' + +import { useState, useEffect, useCallback } from 'react' + +export function useScroll() { + const [isAtBottom, setIsAtBottom] = useState(false) + + const handleScroll = useCallback(() => { + const footer = document.querySelector('footer') + if (footer) { + const footerTop = footer.getBoundingClientRect().top + setIsAtBottom(footerTop < window.innerHeight) + } + }, []) + + useEffect(() => { + window.addEventListener('scroll', handleScroll) + handleScroll() // Initial check + return () => window.removeEventListener('scroll', handleScroll) + }, [handleScroll]) + + const scrollToNext = () => { + const sections = Array.from( + document.querySelectorAll('section[id]') + ) as HTMLElement[] + const scrollPosition = window.scrollY + window.innerHeight / 2 + + const currentSection = sections.reduce((acc, section) => { + return section.offsetTop < scrollPosition ? section : acc + }, sections[0]) + + const currentIndex = sections.findIndex((sec) => sec.id === currentSection.id) + const nextIndex = currentIndex + 1 + + if (nextIndex < sections.length) { + sections[nextIndex].scrollIntoView({ behavior: 'smooth' }) + } + } + + const scrollToTop = () => { + window.scrollTo({ top: 0, behavior: 'smooth' }) + } + + return { isAtBottom, scrollToNext, scrollToTop } +} diff --git a/src/pages/agents/BentoSection.tsx b/src/pages/agents/BentoSection.tsx index 7b908a5..a2aaf68 100644 --- a/src/pages/agents/BentoSection.tsx +++ b/src/pages/agents/BentoSection.tsx @@ -36,7 +36,7 @@ const items = [ export function BentoSection() { return ( -
+
-

+

Augmented Intelligence Fabric

-

+

A complete infrastructure for building and deploying AI agents with enterprise-grade security and performance.

@@ -61,11 +61,11 @@ export function BentoSection() { whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: index * 0.1 }} - className="rounded-2xl bg-gray-50 border border-gray-200 p-6 hover:border-cyan-500 hover:shadow-lg transition-all duration-300" + className="rounded-2xl bg-gray-900 border border-gray-800 p-6 hover:border-cyan-500 hover:shadow-lg transition-all duration-300 hover:scale-105" > -

{item.title}

+

{item.title}

{item.subtitle}

-

{item.description}

+

{item.description}

))} diff --git a/src/pages/agents/GallerySection.tsx b/src/pages/agents/GallerySection.tsx index f7d7ecf..890633f 100644 --- a/src/pages/agents/GallerySection.tsx +++ b/src/pages/agents/GallerySection.tsx @@ -1,58 +1,178 @@ -import { motion } from 'framer-motion' -import { Container } from '../../components/Container' +'use client' + +import { useEffect, useMemo, useState } from 'react' +import { useResponsiveCarousel } from '@/hooks/useResponsiveCarousel'; +import { motion, AnimatePresence } from 'framer-motion' +import { wrap } from 'popmotion' +import { Button } from '@/components/Button'; +import { SectionHeader, P, Eyebrow } from '@/components/Texts'; +import { TypeAnimation } from 'react-type-animation' +import { FadeIn } from '@/components/FadeIn'; const galleryItems = [ - { text: 'Navigate and interact with any web interface', image: '/images/gallery/interface.jpg' }, - { text: 'Process documents across all formats', image: '/images/gallery/docs.jpg' }, - { text: 'Execute multi-step workflows autonomously', image: '/images/gallery/flow.jpg' }, - { text: 'Manage calendars, emails, and tasks', image: '/images/gallery/calendar.jpg' }, - { text: 'Perform deep semantic search across all data sources', image: '/images/gallery/data.jpg' }, - { text: 'Identify patterns in complex datasets', image: '/images/gallery/datasets.jpg' }, + { text: 'Navigate and interact with any web interface', image: '/images/gallery/interface.jpg', width: 448, height: 277 }, + { text: 'Process documents across all formats', image: '/images/gallery/docs.jpg', width: 448, height: 277 }, + { text: 'Execute multi-step workflows autonomously', image: '/images/gallery/flow.jpg', width: 448, height: 277 }, + { text: 'Manage calendars, emails, and tasks', image: '/images/gallery/calendar.jpg', width: 448, height: 277 }, + { text: 'Perform deep semantic search across all data sources', image: '/images/gallery/data.jpg', width: 448, height: 277 }, + { text: 'Identify patterns in complex datasets', image: '/images/gallery/datasets.jpg', width: 448, height: 277 }, + { text: 'Provide real-time market intelligence', image: '/images/gallery/market.jpg', width: 448, height: 277 }, + { text: 'Generate and debug code in multiple languages', image: '/images/gallery/code.jpg', width: 448, height: 277 }, + { text: 'Create consistent branded content', image: '/images/gallery/branding.jpg', width: 448, height: 277 }, + { text: 'Translate and localize materials', image: '/images/gallery/translate.jpg', width: 448, height: 277 }, + { text: 'Transform and migrate data structures', image: '/images/gallery/structure.jpg', width: 448, height: 277 }, ] -export function GallerySection() { - return ( -
- - -

- Agents with Endless Possibilities. -

-

- Your private agent coordinates a team of specialists that spin up on demand, collaborate across your world, and deliver end-to-end results. Many agents, one intelligence—yours. -

-
+// đź”§ Carousel Config +const VISIBLE = 4; +const AUTOPLAY_MS = 3200; -
- {galleryItems.map((item, index) => ( - -
- {item.text} -
-
-

{item.text}

-
-
- ))} -
-
-
+export function GallerySection() { + const [active, setActive] = useState(0); + const [hovering, setHovering] = useState(false); + const { GAP, ROT_Y, DEPTH, SCALE_DROP } = useResponsiveCarousel(); + + // autoplay + useEffect(() => { + if (hovering) return + const id = setInterval(() => setActive((i) => wrap(0, galleryItems.length, i + 1)), AUTOPLAY_MS) + return () => clearInterval(id) + }, [hovering]) + + const indices = useMemo( + () => [...Array(VISIBLE * 2 + 1)].map((_, i) => wrap(0, galleryItems.length, active + i - VISIBLE)), + [active] ) + + const next = () => setActive((i) => wrap(0, galleryItems.length, i + 1)) + const prev = () => setActive((i) => wrap(0, galleryItems.length, i - 1)) + + return ( +
+
+ +
+ Use Cases + Agents with Endless Possibilities. +
+
+ +
+

+ Your private agent coordinates a team of specialists that spin up on demand, collaborate across your world, and deliver end-to-end results. + Many agents, one intelligence—yours. +

+
+
+
+ +
setHovering(true)} + onMouseLeave={() => setHovering(false)} + > +
+
+ + {indices.map((idx, i) => { + const distance = i - VISIBLE; + const item = galleryItems[idx]; + + const x = distance * GAP; + const z = -Math.abs(distance) * DEPTH; + const r = distance * ROT_Y; + const s = 1 - Math.abs(distance) * SCALE_DROP; + const o = distance === 0 ? 1 : 0.80; + const zIndex = 100 - Math.abs(distance); + + return ( + setActive(idx)} + > +
+ {item.text} +
+
+ ); + })} +
+
+
+ + {/* Arrows */} +
+ +
+
+ +
+ + {/* Foreground pill (Desktop) */} +
+
+

+ +

+ +
+
+
+ + {/* Text box (Mobile) */} +
+
+

+ +

+ +
+
+
+
+ ); }