diff --git a/Version 2 of Cinematic Agency Portfolio 2/App.tsx b/Version 2 of Cinematic Agency Portfolio 2/App.tsx new file mode 100644 index 0000000..7703bf8 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/App.tsx @@ -0,0 +1,76 @@ +import React, { useEffect } from 'react'; +import Lenis from 'lenis'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; +import { Navbar } from './components/Navbar'; +import { Hero } from './components/Hero'; +import { Features } from './components/Features'; +import { VisualSection } from './components/VisualSection'; +import { ProductCarousel } from './components/ProductCarousel'; +import { Footer } from './components/Footer'; +import { CallToAction } from './components/CallToAction'; + +// Register GSAP plugin globally +gsap.registerPlugin(ScrollTrigger); + +const App: React.FC = () => { + useEffect(() => { + // Initialize Lenis for smooth scrolling + const lenis = new Lenis({ + duration: 1.2, + easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), + direction: 'vertical', + gestureDirection: 'vertical', + smooth: true, + smoothTouch: false, + touchMultiplier: 2, + }); + + // Expose lenis to window for Navbar access + (window as any).lenis = lenis; + + // Connect Lenis to GSAP ScrollTrigger + lenis.on('scroll', ScrollTrigger.update); + + // GSAP ticker sync + gsap.ticker.add((time) => { + lenis.raf(time * 1000); + }); + + gsap.ticker.lagSmoothing(0); + + return () => { + lenis.destroy(); + (window as any).lenis = null; + gsap.ticker.remove((time) => lenis.raf(time * 1000)); + }; + }, []); + + return ( +
+ +
+ + + + + + +
+
+ ); +}; + +export default App; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/components/CallToAction.tsx b/Version 2 of Cinematic Agency Portfolio 2/components/CallToAction.tsx new file mode 100644 index 0000000..b9ba143 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/components/CallToAction.tsx @@ -0,0 +1,43 @@ +import React, { useRef, useEffect } from 'react'; +import gsap from 'gsap'; + +interface CallToActionProps { + id?: string; +} + +export const CallToAction: React.FC = ({ id }) => { + const containerRef = useRef(null); + const textRef = useRef(null); + + useEffect(() => { + // Reveal animation + gsap.fromTo(textRef.current, + { y: 100, opacity: 0 }, + { + y: 0, + opacity: 1, + duration: 1.5, + ease: 'power4.out', + scrollTrigger: { + trigger: containerRef.current, + start: 'top 75%', + } + } + ); + }, []); + + return ( +
+
+ +
+

+ Ready to disrupt? +

+ +
+
+ ); +}; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/components/Features.tsx b/Version 2 of Cinematic Agency Portfolio 2/components/Features.tsx new file mode 100644 index 0000000..f362808 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/components/Features.tsx @@ -0,0 +1,83 @@ +import React, { useEffect, useRef } from 'react'; +import gsap from 'gsap'; +import { SectionWrapper } from './SectionWrapper'; +import { Cpu, Globe, Zap, Shield } from 'lucide-react'; + +interface FeaturesProps { + id?: string; +} + +const featureList = [ + { + icon: , + title: "AI-Driven Core", + desc: "Our systems leverage the latest in neural processing to optimize user pathways in real-time." + }, + { + icon: , + title: "Global CDN", + desc: "Content delivered from the edge, ensuring millisecond latency regardless of user location." + }, + { + icon: , + title: "Instant State", + desc: "Optimistic UI updates and predictive pre-fetching make the interface feel instant." + }, + { + icon: , + title: "Enterprise Security", + desc: "Bank-grade encryption and compliance standards built into every layer of the stack." + } +]; + +export const Features: React.FC = ({ id }) => { + const gridRef = useRef(null); + + useEffect(() => { + if (!gridRef.current) return; + + const cards = gridRef.current.children; + + gsap.fromTo(cards, + { opacity: 0, y: 40 }, + { + opacity: 1, + y: 0, + duration: 1, + stagger: 0.2, + ease: 'power3.out', + scrollTrigger: { + trigger: gridRef.current, + start: 'top 80%', + } + } + ); + }, []); + + return ( +
+
+ +

+ Technical Superiority +

+
+ + +
+ {featureList.map((f, i) => ( +
+
+ {f.icon} +
+

{f.title}

+

+ {f.desc} +

+
+ ))} +
+
+
+ ); +}; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/components/Footer.tsx b/Version 2 of Cinematic Agency Portfolio 2/components/Footer.tsx new file mode 100644 index 0000000..bbf4eb9 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/components/Footer.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +export const Footer: React.FC = () => { + return ( +
+
+
+
+ AGENCY +
+ + + +
+ © {new Date().getFullYear()} Cinematic Agency. All rights reserved. +
+
+
+ ); +}; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/components/Hero.tsx b/Version 2 of Cinematic Agency Portfolio 2/components/Hero.tsx new file mode 100644 index 0000000..0320fc1 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/components/Hero.tsx @@ -0,0 +1,66 @@ +import React, { useEffect, useRef } from 'react'; +import gsap from 'gsap'; +import { ArrowDown } from 'lucide-react'; + +export const Hero: React.FC = () => { + const containerRef = useRef(null); + const headingRef = useRef(null); + const subRef = useRef(null); + const badgeRef = useRef(null); + + useEffect(() => { + const tl = gsap.timeline(); + + tl.fromTo( + badgeRef.current, + { opacity: 0, y: 20 }, + { opacity: 1, y: 0, duration: 0.8, ease: 'power3.out', delay: 0.2 } + ) + .fromTo( + headingRef.current, + { opacity: 0, y: 60 }, + { opacity: 1, y: 0, duration: 1.2, ease: 'power3.out' }, + "-=0.4" + ) + .fromTo( + subRef.current, + { opacity: 0, y: 40 }, + { opacity: 1, y: 0, duration: 1, ease: 'power3.out' }, + "-=0.8" + ); + + }, []); + + return ( +
+ {/* Background Gradients */} +
+
+ +
+ Next Generation Agency +
+ +

+ ELEVATE
REALITY +

+ +

+ We craft digital experiences that blur the line between the virtual and the tangible. Precision engineering meets cinematic design. +

+ +
+ +
+
+ ); +}; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/components/Navbar.tsx b/Version 2 of Cinematic Agency Portfolio 2/components/Navbar.tsx new file mode 100644 index 0000000..085f431 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/components/Navbar.tsx @@ -0,0 +1,99 @@ +import React, { useEffect, useRef } from 'react'; +import gsap from 'gsap'; +import { Menu, X } from 'lucide-react'; + +export const Navbar: React.FC = () => { + const navRef = useRef(null); + const [isOpen, setIsOpen] = React.useState(false); + + useEffect(() => { + // Animate navbar in after a slight delay + gsap.fromTo( + navRef.current, + { y: -100, opacity: 0 }, + { y: 0, opacity: 1, duration: 1, ease: 'power3.out', delay: 0.5 } + ); + }, []); + + const handleScroll = (e: React.MouseEvent, href: string) => { + e.preventDefault(); + const targetId = href.replace('#', ''); + const element = document.getElementById(targetId); + + if (element) { + // Use globally exposed Lenis instance if available, otherwise fallback to native + const lenis = (window as any).lenis; + if (lenis) { + lenis.scrollTo(element, { duration: 1.5 }); + } else { + element.scrollIntoView({ behavior: 'smooth' }); + } + setIsOpen(false); + } + }; + + const navLinks = [ + { name: 'Work', href: '#work' }, + { name: 'Services', href: '#services' }, + { name: 'About', href: '#about' }, + { name: 'Contact', href: '#contact' }, + ]; + + return ( + + ); +}; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/components/ProductCarousel.tsx b/Version 2 of Cinematic Agency Portfolio 2/components/ProductCarousel.tsx new file mode 100644 index 0000000..1fdae8b --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/components/ProductCarousel.tsx @@ -0,0 +1,216 @@ +import React, { useState, useRef, useEffect } from 'react'; +import gsap from 'gsap'; +import { ArrowLeft, ArrowRight, ArrowUpRight } from 'lucide-react'; + +interface ProductCarouselProps { + id?: string; +} + +const products = [ + { + id: 1, + category: "Hardware", + title: "Neuro-Core V1", + description: "The world's first biological processing unit designed for consumer-grade synthesis. Experience zero-latency computing.", + image: "https://images.unsplash.com/photo-1550751827-4bd374c3f58b?q=80&w=2070&auto=format&fit=crop" + }, + { + id: 2, + category: "Interface", + title: "Haptic Suit Pro", + description: "Full-body sensory immersion. Feel the digital wind, the weight of data, and the texture of code in real-time.", + image: "https://images.unsplash.com/photo-1535378437327-b71280637040?q=80&w=2076&auto=format&fit=crop" + }, + { + id: 3, + category: "Robotics", + title: "Sentinel Drone", + description: "Silent, efficient, and omnipresent. The ultimate tool for aerial data acquisition and environmental monitoring.", + image: "https://images.unsplash.com/photo-1473968512647-3e447244af8f?q=80&w=2070&auto=format&fit=crop" + } +]; + +export const ProductCarousel: React.FC = ({ id }) => { + const [currentIndex, setCurrentIndex] = useState(0); + const [isAnimating, setIsAnimating] = useState(false); + + // Refs for animation targets + const slideRefs = useRef<(HTMLDivElement | null)[]>([]); + const textRefs = useRef<(HTMLDivElement | null)[]>([]); + const imageRefs = useRef<(HTMLImageElement | null)[]>([]); + + useEffect(() => { + // Initial setup: Ensure only first slide is visible and set Z-indices + slideRefs.current.forEach((slide, index) => { + if (slide) { + gsap.set(slide, { + autoAlpha: index === 0 ? 1 : 0, + zIndex: index === 0 ? 10 : 0, + clipPath: 'none' // Reset any clips + }); + } + }); + }, []); + + const goToSlide = (index: number, direction: 'next' | 'prev' = 'next') => { + if (isAnimating || index === currentIndex) return; + setIsAnimating(true); + + const nextIndex = (index + products.length) % products.length; + + const currentSlide = slideRefs.current[currentIndex]; + const nextSlide = slideRefs.current[nextIndex]; + + const currentText = textRefs.current[currentIndex]; + const nextText = textRefs.current[nextIndex]; + + const currentImage = imageRefs.current[currentIndex]; + const nextImage = imageRefs.current[nextIndex]; + + if (!currentSlide || !nextSlide || !currentText || !nextText || !currentImage || !nextImage) return; + + const tl = gsap.timeline({ + onComplete: () => { + setCurrentIndex(nextIndex); + setIsAnimating(false); + // Clean up outgoing slide + gsap.set(currentSlide, { autoAlpha: 0, zIndex: 0, clipPath: 'none' }); + } + }); + + // 1. Setup Z-Index to ensure next slide is on top + gsap.set(nextSlide, { autoAlpha: 1, zIndex: 20 }); + gsap.set(currentSlide, { zIndex: 10 }); + + // 2. Define Clip Path Wipes (Cinematic Curtain Effect) + // Next: Wipe from Right to Left | Prev: Wipe from Left to Right + const clipRight = 'polygon(100% 0, 100% 0, 100% 100%, 100% 100%)'; + const clipLeft = 'polygon(0 0, 0 0, 0 100%, 0 100%)'; + const clipFull = 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)'; + + const initialClip = direction === 'next' ? clipRight : clipLeft; + + gsap.set(nextSlide, { clipPath: initialClip }); + + // 3. Animate Outgoing Slide (Text Fade Out + Image Parallax) + const currentElements = Array.from(currentText.children); + tl.to(currentElements, { + y: -30, + opacity: 0, + stagger: 0.05, + duration: 0.5, + ease: "power2.in" + }, 0); + + tl.to(currentImage, { + scale: 1.1, + filter: "brightness(0.4)", // Dim it down as it leaves + duration: 1.2, + ease: "power2.out" + }, 0); + + // 4. Animate Incoming Slide (Wipe In + Image Scale Down) + tl.to(nextSlide, { + clipPath: clipFull, + duration: 1.2, + ease: "power4.inOut" // Smooth, dramatic easing + }, 0); + + // Image scales down from 1.3 to 1 for a subtle "zoom out" feel + gsap.set(nextImage, { scale: 1.3, filter: "brightness(1)" }); + tl.to(nextImage, { + scale: 1, + duration: 1.6, + ease: "power2.out" + }, 0); + + // 5. Animate Incoming Text (Stagger In from bottom) + const nextElements = Array.from(nextText.children); + gsap.set(nextElements, { y: 60, opacity: 0 }); // Reset positions + + tl.to(nextElements, { + y: 0, + opacity: 1, + stagger: 0.1, + duration: 1, + ease: "power3.out" + }, 0.5); // Start halfway through the wipe + }; + + const next = () => goToSlide(currentIndex + 1, 'next'); + const prev = () => goToSlide(currentIndex - 1, 'prev'); + + const goToIndex = (idx: number) => { + if (idx === currentIndex) return; + const direction = idx > currentIndex ? 'next' : 'prev'; + goToSlide(idx, direction); + }; + + return ( +
+ {products.map((product, index) => ( +
{ slideRefs.current[index] = el; }} + className="absolute inset-0 w-full h-full" + > + {/* Image Background */} +
+ { imageRefs.current[index] = el; }} + src={product.image} + alt={product.title} + className="w-full h-full object-cover" + /> + + {/* Content Overlay */} +
+
{ textRefs.current[index] = el; }} className="max-w-4xl"> +
+ + {product.category} +
+

+ {product.title} +

+

+ {product.description} +

+ +
+
+
+ ))} + + {/* Navigation Controls */} +
+ + +
+ + {/* Progress Indicators */} +
+ {products.map((_, idx) => ( +
+
+ ) +}; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/components/SectionWrapper.tsx b/Version 2 of Cinematic Agency Portfolio 2/components/SectionWrapper.tsx new file mode 100644 index 0000000..d6fec50 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/components/SectionWrapper.tsx @@ -0,0 +1,41 @@ +import React, { useEffect, useRef } from 'react'; +import gsap from 'gsap'; + +interface SectionWrapperProps { + children: React.ReactNode; + className?: string; + delay?: number; +} + +export const SectionWrapper: React.FC = ({ children, className = "", delay = 0 }) => { + const ref = useRef(null); + + useEffect(() => { + const element = ref.current; + if (!element) return; + + gsap.fromTo( + element, + { opacity: 0, y: 60, scale: 0.98 }, + { + opacity: 1, + y: 0, + scale: 1, + duration: 1.2, + ease: 'power3.out', + delay, + scrollTrigger: { + trigger: element, + start: 'top 85%', // Starts animation when top of element hits 85% of viewport + toggleActions: 'play none none reverse', + }, + } + ); + }, [delay]); + + return ( +
+ {children} +
+ ); +}; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/components/VisualSection.tsx b/Version 2 of Cinematic Agency Portfolio 2/components/VisualSection.tsx new file mode 100644 index 0000000..ed32c92 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/components/VisualSection.tsx @@ -0,0 +1,72 @@ +import React, { useRef, useEffect } from 'react'; +import gsap from 'gsap'; +import { SectionWrapper } from './SectionWrapper'; + +interface VisualSectionProps { + id?: string; + title: string; + description: string; + image: string; + alignment: 'left' | 'right'; +} + +export const VisualSection: React.FC = ({ id, title, description, image, alignment }) => { + const imgRef = useRef(null); + const containerRef = useRef(null); + + useEffect(() => { + if (!imgRef.current || !containerRef.current) return; + + // Parallax effect on image + gsap.fromTo(imgRef.current, + { scale: 1.1 }, + { + scale: 1, + ease: 'none', + scrollTrigger: { + trigger: containerRef.current, + start: 'top bottom', + end: 'bottom top', + scrub: true, + } + } + ); + }, []); + + return ( +
+
+ +
+
+
+ {title} +
+
+ +
+ +

+ {title} +

+

+ {description} +

+
+ +
+
+
+ +
+
+ ); +}; \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/index.html b/Version 2 of Cinematic Agency Portfolio 2/index.html new file mode 100644 index 0000000..99d055b --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/index.html @@ -0,0 +1,77 @@ + + + + + + Agency | Cinematic Experience + + + + + + + + + +
+ + \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/index.tsx b/Version 2 of Cinematic Agency Portfolio 2/index.tsx new file mode 100644 index 0000000..6ca5361 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +const rootElement = document.getElementById('root'); +if (!rootElement) { + throw new Error("Could not find root element to mount to"); +} + +const root = ReactDOM.createRoot(rootElement); +root.render( + + + +); \ No newline at end of file diff --git a/Version 2 of Cinematic Agency Portfolio 2/metadata.json b/Version 2 of Cinematic Agency Portfolio 2/metadata.json new file mode 100644 index 0000000..9ea6c17 --- /dev/null +++ b/Version 2 of Cinematic Agency Portfolio 2/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Copy of Cinematic Agency Portfolio", + "description": "A high-performance, scroll-driven agency website featuring smooth Lenis scrolling, GSAP animations, and a cinematic dark aesthetic.", + "requestFramePermissions": [] +} \ No newline at end of file