├── .eslintrc.json ├── .gitignore ├── README.md ├── animation ├── LinkHover.tsx ├── LogoMarquee.tsx ├── TextHover.tsx ├── TextMask.tsx └── index.ts ├── components ├── BackgroundImg.tsx ├── Button.tsx ├── Curve │ └── Curve.jsx ├── Eyes.tsx ├── Footer.tsx ├── Heading.tsx ├── Marquee.tsx ├── MobileNav.tsx ├── Navbar.tsx ├── PlayVideo.tsx ├── Project.tsx ├── ProjectCard.tsx ├── Ratings.tsx ├── Ready.tsx ├── RoundButton.tsx ├── Rounded.tsx ├── Tags.tsx └── index.ts ├── constants └── index.ts ├── container ├── about-page │ ├── About.tsx │ ├── Hero.tsx │ ├── Insights.tsx │ ├── Partners.tsx │ ├── Principles.tsx │ ├── Publication.tsx │ └── Team.tsx ├── contact-page │ ├── Faq.tsx │ ├── Form.tsx │ ├── Hero.tsx │ └── Socials.tsx ├── home-page │ ├── About.tsx │ ├── Clients.tsx │ ├── Hero.tsx │ ├── Projects.tsx │ └── Video.tsx ├── index.ts ├── insights-page │ ├── Hero.tsx │ └── Publication.tsx ├── presentation-page │ ├── Hero.tsx │ ├── Projects.tsx │ └── Publication.tsx ├── services-page │ ├── Archive.tsx │ ├── Capibilyties.tsx │ ├── Clients.tsx │ ├── Expectations.tsx │ ├── Hero.tsx │ └── Process.tsx └── workiz-page │ ├── About.tsx │ ├── Chelenge.tsx │ ├── Credit.tsx │ ├── Hero.tsx │ ├── Result.tsx │ ├── Video.tsx │ └── Works.tsx ├── fonts ├── FoundersGrotesk.woff └── NeueMontreal.woff ├── motion └── index.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── pages ├── _app.tsx ├── _document.tsx ├── case │ └── index.tsx ├── contact │ └── index.tsx ├── index.tsx ├── insights │ └── index.tsx ├── ochi-team │ └── index.tsx ├── presentation │ └── index.tsx └── services │ └── index.tsx ├── postcss.config.js ├── public ├── aboutImg.webp ├── aflomatric.svg ├── after.png ├── awwwards.svg ├── background-about.jpeg ├── background.png ├── blackBox.svg ├── brand01.svg ├── brand02.svg ├── brand03.png ├── capybilities1.jpeg ├── capybilities2.jpeg ├── capybilities3.jpeg ├── capybilities4.jpeg ├── capybilities5.jpeg ├── chelengeBg.jpeg ├── client01.png ├── client02.png ├── client03.png ├── client04.png ├── client05.png ├── client06.png ├── client07.jpeg ├── client08.png ├── client09.png ├── contacthhero.jpg ├── eyeplay.svg ├── eyes.svg ├── favicon.ico ├── homevideo.mp4 ├── index.ts ├── insights1.png ├── insights2.jpeg ├── insights3.jpeg ├── latest1.png ├── latest2.jpeg ├── lexus.svg ├── logo.svg ├── mobile-logo.png ├── nestle.svg ├── ochi-side.jpg ├── officevibe.svg ├── orderlion.svg ├── phase1.png ├── phase2.png ├── phase3.png ├── phase4.png ├── phase5.png ├── planetly.svg ├── principles1.jpeg ├── principles2.jpeg ├── project1.webp ├── project2.webp ├── project3.webp ├── project4.webp ├── project5.webp ├── project6.webp ├── project7.webp ├── project8.webp ├── project9.webp ├── publication1.jpeg ├── publication2.png ├── publication3.png ├── showcase1.jpeg ├── showcase2.png ├── showcase3.jpeg ├── showcase4.jpeg ├── showcase5.jpeg ├── showcase6.jpeg ├── team1.jpeg ├── team2.jpeg ├── team3.jpeg ├── team4.png ├── toyota.svg ├── videoImg.jpeg ├── welcome.jpeg ├── workiz.jpeg ├── workizhero.jpg └── workizvideo.mp4 ├── styles └── globals.css ├── tailwind.config.ts ├── tsconfig.json └── types └── index.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ochi Website Clone created with 2 | 3 | ✅ Next JS 4 | ✅ React JS 5 | ✅ TypeScript 6 | ✅ Tailwind CSS 7 | ✅ GSAP 8 | ✅ Framer Motion 9 | ✅ Locomotive Scroll 10 | 11 |
12 | 13 | ![OCHI - Presentation Design Agency and 2 more pages - Personal - Microsoft​ Edge 5_26_2024 4_34_03 AM](https://github.com/devwithzain/ochi-website-clone/assets/131141179/d47be37b-efa0-45f0-bb18-1c5aed00191b) 14 | -------------------------------------------------------------------------------- /animation/LinkHover.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { TLinkHoverProps } from "@/types"; 3 | 4 | export default function LinkHover({ href, title, className }: TLinkHoverProps) { 5 | return ( 6 |
7 | 10 | {title} 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /animation/LogoMarquee.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TlogoMarqueeProps } from "@/types"; 3 | import { 4 | motion, 5 | useScroll, 6 | useSpring, 7 | useTransform, 8 | useMotionValue, 9 | useVelocity, 10 | useAnimationFrame, 11 | wrap, 12 | } from "framer-motion"; 13 | import { useRef } from "react"; 14 | 15 | export default function LogoMarquee({ 16 | children, 17 | baseVelocity = 100, 18 | }: TlogoMarqueeProps) { 19 | const baseX = useMotionValue(0); 20 | const { scrollY } = useScroll(); 21 | const scrollVelocity = useVelocity(scrollY); 22 | const smoothVelocity = useSpring(scrollVelocity, { 23 | damping: 50, 24 | stiffness: 400, 25 | }); 26 | const velocityFactor = useTransform(smoothVelocity, [0, 1000], [0, 5], { 27 | clamp: false, 28 | }); 29 | const x = useTransform(baseX, (v) => `${wrap(-20, -45, v)}%`); 30 | 31 | const directionFactor = useRef(1); 32 | useAnimationFrame((t, delta) => { 33 | let moveBy = directionFactor.current * baseVelocity * (delta / 1000); 34 | 35 | if (velocityFactor.get() < 0) { 36 | directionFactor.current = -1; 37 | } else if (velocityFactor.get() > 0) { 38 | directionFactor.current = 1; 39 | } 40 | 41 | moveBy += directionFactor.current * moveBy * velocityFactor.get(); 42 | 43 | baseX.set(baseX.get() + moveBy); 44 | }); 45 | 46 | return ( 47 |
48 | 51 | {children} 52 | {children} 53 | {children} 54 | {children} 55 | 56 |
57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /animation/TextHover.tsx: -------------------------------------------------------------------------------- 1 | import { TtextHoverProps } from "@/types"; 2 | 3 | export default function TextHover({ titile1, titile2 }: TtextHoverProps) { 4 | return ( 5 |
6 |
7 |
8 |

9 |
10 | {titile1} 11 |
12 |

13 |

14 |
15 | {titile2} 16 |
17 |

18 |
19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /animation/TextMask.tsx: -------------------------------------------------------------------------------- 1 | import { animation } from "@/motion"; 2 | import { motion } from "framer-motion"; 3 | import { useInView } from "react-intersection-observer"; 4 | 5 | export default function MaskText({ children }: { children: string[] }) { 6 | const { ref, inView } = useInView({ 7 | threshold: 0.75, 8 | triggerOnce: true, 9 | }); 10 | 11 | return ( 12 |
13 | {children.map((phrase, index) => ( 14 |
17 | 22 | {phrase} 23 | 24 |
25 | ))} 26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /animation/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TextHover } from './TextHover'; 2 | export { default as TextMarquee } from './LogoMarquee'; 3 | export { default as TextMask } from './TextMask'; 4 | export { default as LinkHover } from './LinkHover'; -------------------------------------------------------------------------------- /components/BackgroundImg.tsx: -------------------------------------------------------------------------------- 1 | import Image, { StaticImageData } from "next/image"; 2 | 3 | export default function BackgroundImg({ src }: { src: StaticImageData }) { 4 | return ( 5 |
6 |
10 | img 15 |
16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /components/Button.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { TButtonProps } from "@/types"; 3 | import { ArrowUpRight } from "lucide-react"; 4 | 5 | export default function Button({ href, title }: TButtonProps) { 6 | return ( 7 |
8 |
9 |
10 | 13 | {title} 14 | 15 |
16 |
17 |

18 | 22 |

23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /components/Curve/Curve.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { motion } from "framer-motion"; 3 | import { useRouter } from "next/router"; 4 | import { useEffect, useState } from "react"; 5 | import { text, curve, translate } from "@/motion"; 6 | 7 | const routes = { 8 | "/": "Home", 9 | "/services": "Services", 10 | "/presentation": "Our Work", 11 | "/ochi-team": "About Us", 12 | "/insights": "Insights", 13 | "/contact": "Contact Us", 14 | "/case": "Workiz Easy", 15 | }; 16 | 17 | const anim = (variants) => { 18 | return { 19 | variants, 20 | initial: "initial", 21 | animate: "enter", 22 | exit: "exit", 23 | }; 24 | }; 25 | 26 | export default function Curve({ children, backgroundColor }) { 27 | const router = useRouter(); 28 | const [dimensions, setDimensions] = useState({ 29 | width: null, 30 | height: null, 31 | }); 32 | 33 | useEffect(() => { 34 | function resize() { 35 | setDimensions({ 36 | width: window.innerWidth, 37 | height: window.innerHeight, 38 | }); 39 | } 40 | resize(); 41 | window.addEventListener("resize", resize); 42 | return () => { 43 | window.removeEventListener("resize", resize); 44 | }; 45 | }, []); 46 | 47 | return ( 48 |
49 |
54 | 57 | {routes[router.route]} 58 | 59 | {dimensions.width != null && } 60 | {children} 61 |
62 | ); 63 | } 64 | 65 | const SVG = ({ height, width }) => { 66 | const initialPath = ` 67 | M0 300 68 | Q${width / 2} 0 ${width} 300 69 | L${width} ${height + 300} 70 | Q${width / 2} ${height + 600} 0 ${height + 300} 71 | L0 0 72 | `; 73 | 74 | const targetPath = ` 75 | M0 300 76 | Q${width / 2} 0 ${width} 300 77 | L${width} ${height} 78 | Q${width / 2} ${height} 0 ${height} 79 | L0 0 80 | `; 81 | 82 | return ( 83 | 87 | 88 | 89 | ); 90 | }; 91 | -------------------------------------------------------------------------------- /components/Eyes.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Image from "next/image"; 3 | import { eyes } from "@/public"; 4 | import React, { useEffect, useState } from "react"; 5 | 6 | export default function Eyes({ className }: { className: string }) { 7 | const [rotate, setRotate] = useState(0); 8 | useEffect(() => { 9 | window.addEventListener("mousemove", (e) => { 10 | let mouseX = e.clientX; 11 | let mouseY = e.clientY; 12 | 13 | let deltaX = mouseX - window.innerWidth / 2; 14 | let deltaY = mouseY - window.innerHeight / 2; 15 | 16 | var angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI); 17 | setRotate(angle - 280); 18 | }); 19 | }, []); 20 | 21 | return ( 22 |
23 |
25 | img 33 |
34 |
36 | img 44 |
45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Image from "next/image"; 3 | import { logo } from "@/public"; 4 | import { LinkHover, TextMask } from "@/animation"; 5 | import { footerItems, footernavbarItems } from "@/constants"; 6 | 7 | export default function Footer() { 8 | const phrase = ["Eye-", "opening"]; 9 | const phrase1 = ["PRESENTATIONS"]; 10 | return ( 11 |
12 |
13 |
14 |

15 | {phrase} 16 |

17 |
18 |
19 |
20 |

21 | {phrase1} 22 |

23 |
24 |

25 | S: 26 |

27 | {footerItems.map((item) => ( 28 | 34 | ))} 35 |
36 |
37 |
38 |

39 | L: 40 |

41 |
42 | 47 | 52 | 57 | 62 |
63 |
64 |
65 |

66 | M: 67 |

68 | {footernavbarItems.map((item) => ( 69 | 75 | ))} 76 |
77 |
78 |
79 |

80 | E: 81 |

82 | 87 |
88 |
89 |
90 |
91 |
92 |
93 | 94 | ochi logo 100 | 101 |
102 |
103 |
104 |

105 | © ochi design 2024. 106 |

107 | 112 |
113 |
114 | 119 |
120 |
121 |
122 |
123 | ); 124 | } 125 | -------------------------------------------------------------------------------- /components/Heading.tsx: -------------------------------------------------------------------------------- 1 | export default function Heading({ 2 | title, 3 | className, 4 | }: { 5 | title: string; 6 | className?: string; 7 | }) { 8 | return ( 9 |

11 | {title} 12 |

13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /components/Marquee.tsx: -------------------------------------------------------------------------------- 1 | import { TMarqueeProps } from "@/types"; 2 | import { TextMarquee } from "@/animation"; 3 | 4 | export default function Marquee({ title, className }: TMarqueeProps) { 5 | return ( 6 | 7 |

9 | {title}   10 |

11 |

13 | {title}   14 |

15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /components/MobileNav.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { useState } from "react"; 5 | import { IoMdClose } from "react-icons/io"; 6 | import { logo, mobileLogo } from "@/public"; 7 | import { footernavbarItems } from "@/constants"; 8 | import { HiOutlineMenuAlt4 } from "react-icons/hi"; 9 | import { AnimatePresence, motion } from "framer-motion"; 10 | 11 | export default function MobileNav() { 12 | const [toggle, setToggle] = useState(false); 13 | return ( 14 | <> 15 |
16 | 17 | ochi logo 23 | 24 | setToggle(true)} 26 | className="text-3xl cursor-pointer text-black" 27 | /> 28 |
29 | 30 | {toggle && ( 31 | 37 |
38 | 39 | ochi logo 45 | 46 | setToggle(false)} 48 | className="text-3xl cursor-pointer text-background" 49 | /> 50 |
51 |
    52 | {footernavbarItems.map((item) => ( 53 | setToggle(!toggle)} 57 | className="text-[80px] leading-[67px] font-FoundersGrotesk uppercase font-bold tracking-[-.9] text-background"> 58 | {item.title} 59 | 60 | ))} 61 |
62 |
63 | )} 64 |
65 | 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /components/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Image from "next/image"; 3 | import { logo } from "@/public"; 4 | import { useState } from "react"; 5 | import { navVariants } from "@/motion"; 6 | import { TextHover } from "@/animation"; 7 | import { navbarItems } from "@/constants"; 8 | import { useMotionValueEvent, useScroll, motion } from "framer-motion"; 9 | import MobileNav from "./MobileNav"; 10 | 11 | export default function Navbar() { 12 | const [hidden, setHidden] = useState(false); 13 | const { scrollY } = useScroll(); 14 | 15 | useMotionValueEvent(scrollY, "change", (latest) => { 16 | const previous = scrollY.getPrevious(); 17 | if (previous && latest > previous) { 18 | setHidden(true); 19 | } else { 20 | setHidden(false); 21 | } 22 | }); 23 | 24 | return ( 25 | <> 26 | 30 |
31 | 32 | ochi logo 38 | 39 |
40 |
41 | {navbarItems.map((item) => ( 42 | 48 | 52 | 53 | ))} 54 |
55 |
56 | 57 | 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /components/PlayVideo.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Image from "next/image"; 3 | import { eyes } from "@/public"; 4 | import { useEffect, useRef, useState } from "react"; 5 | import { useScroll, useTransform, motion } from "framer-motion"; 6 | 7 | export default function PlayVideo({ videosrc }: { videosrc: string }) { 8 | const [rotate, setRotate] = useState(0); 9 | const [isPlaying, setIsPlaying] = useState(false); 10 | const videoRef = useRef(null); 11 | 12 | const togglePlay = () => { 13 | if (videoRef.current) { 14 | if (isPlaying) { 15 | videoRef.current.pause(); 16 | } else { 17 | videoRef.current.play(); 18 | } 19 | setIsPlaying(!isPlaying); 20 | } 21 | }; 22 | 23 | useEffect(() => { 24 | window.addEventListener("mousemove", (e) => { 25 | let mouseX = e.clientX; 26 | let mouseY = e.clientY; 27 | 28 | let deltaX = mouseX - window.innerWidth / 2; 29 | let deltaY = mouseY - window.innerHeight / 2; 30 | 31 | var angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI); 32 | setRotate(angle - 180); 33 | }); 34 | }, []); 35 | const container = useRef(null); 36 | 37 | const { scrollYProgress } = useScroll({ 38 | target: container, 39 | offset: ["start end", "end start"], 40 | }); 41 | 42 | const mq = useTransform(scrollYProgress, [0, 1], [0, -400]); 43 | return ( 44 |
48 |
53 |
109 |
110 | ); 111 | } 112 | -------------------------------------------------------------------------------- /components/Project.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { useState } from "react"; 5 | import { Rounded } from "@/components"; 6 | import { motion } from "framer-motion"; 7 | import { ArrowUpRight } from "lucide-react"; 8 | 9 | export default function Project({ item }: { item: any }) { 10 | const [hovered, setHovered] = useState(false); 11 | return ( 12 |
13 |
14 |
17 |
18 | 19 |

20 | {item.title} 21 |

22 |
23 |
24 |
setHovered(true)} 27 | onMouseLeave={() => setHovered(false)}> 28 | {`${item.title}Img`} 33 |
34 |
37 | {item.title.split("").map((item: any, i: number) => ( 38 | 48 | {item} 49 | 50 | ))} 51 |
52 |
53 |
54 | {item.links.map((link: any) => ( 55 |
58 | 61 | 64 |

{link.title}

65 |
66 | 67 |
68 | ))} 69 |
70 |
71 |
72 |
73 |
74 | 77 | 80 |

81 | view all case studies 82 |

83 |
84 | 88 |
89 |
90 | 91 |
92 |
93 |
94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /components/ProjectCard.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { useState } from "react"; 5 | import { motion } from "framer-motion"; 6 | 7 | export default function ProjectCard({ item }: { item: any }) { 8 | const [hovered, setHovered] = useState(false); 9 | return ( 10 |
11 |
12 | setHovered(true)} 16 | onMouseLeave={() => setHovered(false)}> 17 | {`${item.title}Img`} 22 | 23 |
26 | {item.title.split("").map((item: any, i: any) => ( 27 | 37 | {item} 38 | 39 | ))} 40 |
41 |
42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /components/Ratings.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Image from "next/image"; 3 | import { Rounded } from "@/components"; 4 | import { brand01, brand02, brand03 } from "@/public"; 5 | 6 | export default function Ratings() { 7 | return ( 8 |
9 |
10 |
11 | brandImg 17 |
18 | 21 | ©2019 - 2024 22 | 23 |
24 |
25 |
26 |
27 |
28 | brandImg 34 |
35 | 38 | 41 |

42 | rating 5.0 on clutch 43 |

44 |
45 | 46 |
47 |
48 |
49 | brandImg 55 |
56 | 59 | 62 |

63 | buisness bootcamp alumini 64 |

65 |
66 | 67 |
68 |
69 |
70 |
71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /components/Ready.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import { useRef } from "react"; 4 | import { TextMask } from "@/animation"; 5 | import { ArrowUpRight } from "lucide-react"; 6 | import { Eyes, RoundButton, Rounded } from "@/components"; 7 | import { useScroll, useTransform, motion } from "framer-motion"; 8 | 9 | export default function Ready() { 10 | const container = useRef(null); 11 | const phrase = ["Ready", "to start", "the project"]; 12 | 13 | const { scrollYProgress } = useScroll({ 14 | target: container, 15 | offset: ["start end", "end start"], 16 | }); 17 | const mq = useTransform(scrollYProgress, [0, 1], [0, -700]); 18 | 19 | return ( 20 |
23 |
24 |
25 |

26 | {phrase} 27 |

28 |
29 |
30 |
31 | 38 |
39 |

OR

40 |
41 | 44 | 47 |

48 | hello@ochi.design 49 |

50 |
51 | 56 |
57 |
58 | 59 |
60 |
61 |
62 | 65 | 66 | 67 |
68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /components/RoundButton.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Rounded from "./Rounded"; 3 | import { ArrowUpRight } from "lucide-react"; 4 | 5 | export default function RoundButton({ 6 | href, 7 | title, 8 | className, 9 | bgcolor, 10 | style, 11 | }: { 12 | href: string; 13 | title: string; 14 | className?: string; 15 | bgcolor: string; 16 | style: React.StyleHTMLAttributes["style"]; 17 | }) { 18 | return ( 19 | 22 | 25 |

28 | {title} 29 |

30 |
32 | 37 |
38 |
39 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /components/Rounded.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TRoundedProps } from "@/types"; 3 | import gsap from "gsap"; 4 | import { useEffect, useRef } from "react"; 5 | 6 | export default function Rounded({ 7 | children, 8 | className, 9 | backgroundColor, 10 | ...attributes 11 | }: TRoundedProps) { 12 | const circle = useRef(null); 13 | let timeline = useRef(null); 14 | let timeoutId: NodeJS.Timeout | null = null; 15 | useEffect(() => { 16 | timeline.current = gsap.timeline({ paused: true }); 17 | timeline.current 18 | .to( 19 | circle.current, 20 | { top: "-25%", width: "150%", duration: 0.2, ease: "power3.in" }, 21 | "enter", 22 | ) 23 | .to( 24 | circle.current, 25 | { top: "-150%", width: "125%", duration: 0.2 }, 26 | "exit", 27 | ); 28 | }, []); 29 | 30 | const manageMouseEnter = () => { 31 | if (timeoutId) clearTimeout(timeoutId); 32 | timeline.current?.tweenFromTo("enter", "exit"); 33 | }; 34 | 35 | const manageMouseLeave = () => { 36 | timeoutId = setTimeout(() => { 37 | timeline.current?.play(); 38 | }, 150); 39 | }; 40 | 41 | return ( 42 |
{ 45 | manageMouseEnter(); 46 | }} 47 | onMouseLeave={() => { 48 | manageMouseLeave(); 49 | }} 50 | {...attributes}> 51 | {children} 52 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /components/Tags.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { Rounded } from "@/components"; 3 | 4 | export default function Tags({ 5 | item, 6 | bgcolor, 7 | className, 8 | }: { 9 | item: any; 10 | bgcolor: string; 11 | className: string; 12 | }) { 13 | return ( 14 |
17 | 20 | 23 |

{item.title}

24 |
25 | 26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Ready } from "./Ready"; 2 | export { default as Rounded } from "./Rounded"; 3 | export { default as Marquee } from "./Marquee"; 4 | export { default as Navbar } from "./Navbar"; 5 | export { default as Footer } from "./Footer"; 6 | export { default as BackgroundImg } from './BackgroundImg'; 7 | export { default as Button } from './Button'; 8 | export { default as RoundButton } from './RoundButton'; 9 | export { default as Eyes } from './Eyes'; 10 | export { default as ProjectCard } from './ProjectCard'; 11 | export { default as Tags } from './Tags'; 12 | export { default as PlayVideo } from './PlayVideo'; 13 | export { default as Curve } from './Curve/Curve'; 14 | export { default as Ratings } from './Ratings'; 15 | export { default as Heading } from './Heading'; 16 | export { default as MobileNav } from './MobileNav'; -------------------------------------------------------------------------------- /container/about-page/About.tsx: -------------------------------------------------------------------------------- 1 | import { backgroundAbout } from "@/public"; 2 | import { BackgroundImg } from "@/components"; 3 | 4 | export default function About() { 5 | return ( 6 |
7 |
8 |
9 |
10 |
11 |

12 | We are ochi design: 13 |

14 |
15 |
16 |
17 |
18 |

19 | The team of designers, storytellers, and 20 |
passionate collaborators, who work 21 |
together to create industry-shifting
22 | presentations that win people's hearts 23 |
24 | and minds. 25 |

26 |
27 |
28 |

29 | And we strive to become one of the most 30 |
31 | recognizable & influential presentation 32 |
agencies of the time who does that. 33 |

34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | 42 |
43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /container/about-page/Hero.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { ochiside } from "@/public"; 5 | import { Eyes } from "@/components"; 6 | import { motion } from "framer-motion"; 7 | import { ArrowUpRight } from "lucide-react"; 8 | 9 | export default function Hero() { 10 | return ( 11 |
12 |
13 |
14 |
15 |
16 |

17 | WE ARE
18 |
19 | 27 | img 34 | 35 |

36 | OCHI DESIGN 37 |

38 |
39 |

40 |
41 |
42 |
43 |
44 |
45 |

46 | About us: 47 |

48 |
49 |
50 |
51 |
52 |

53 | In Ukrainian, ochi - means eyes. It's not 54 |
just a beautiful word, but our philosophy. 55 |
56 | Almost everything that needs to be 57 |
communicated is better shown than
58 | explained. 59 |

60 |
61 |
62 |

63 | We believe a great presentation evokes 64 |
65 | interest and drives business results far 66 |
better than any saying can. Hence, we 67 |
68 | founded ochi to help you persuade 69 |
colleagues and clients by creating eye- 70 |
71 | opening presentations. 72 |

73 |
74 |
75 |
76 |
77 | 80 | Our Work 81 | 82 |
83 |
84 |

85 | 86 |

87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
98 | 99 |
100 |
101 |

102 | We save businesses from ugly and 103 |
ineffective presentations. 104 |

105 |
106 |
107 | ); 108 | } 109 | -------------------------------------------------------------------------------- /container/about-page/Insights.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Image from "next/image"; 3 | import { latestItemss } from "@/constants"; 4 | 5 | export default function Insights() { 6 | return ( 7 |
8 |
9 |
10 |
11 |

12 | Insights 13 |

14 |
15 |
16 |
17 |
18 |

19 | Latest publication: 20 |

21 |
22 |
23 | {latestItemss.map((item) => ( 24 |
29 |
30 | 33 | img 38 | 39 |
40 | {item.links.map((link) => ( 41 |
44 |
45 | 48 | {link.title} 49 | 50 |
51 |
52 | ))} 53 |
54 |
55 |

56 | Presenting to an International Audience:
Tips 57 | and Lessons Learned. 58 |

59 |

60 | {item.subTitle} 61 |

62 |

63 | {item.date} 64 |

65 |
66 |
67 |
68 | ))} 69 |
70 |
71 |
72 |
73 |
74 |
75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /container/about-page/Partners.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { Ratings } from "@/components"; 3 | import { aboutPartberItems } from "@/constants"; 4 | 5 | export default function Principles() { 6 | return ( 7 |
8 |
9 |

10 | We’ve built long-lasting partnerships 11 |
with the most ambitious brands 12 |
13 | across the globe: 14 |

15 |
16 |
17 |
18 | {aboutPartberItems.map((item) => ( 19 |
22 |
23 |
24 | img 31 |
32 |
33 |

34 | {item.title} 35 |

36 |

37 | {item.para} 38 |

39 |
40 |
41 |
42 | ))} 43 |
44 |
45 |
46 | 47 |
48 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /container/about-page/Principles.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { principles1, principles2 } from "@/public"; 3 | 4 | export default function Principles() { 5 | return ( 6 |
7 |
8 |

9 | Two principles we stand behind in 10 |
every part of our work: 11 |

12 |
13 |
14 |
15 |
16 | img 21 |
22 |

23 | Whether the presentation needs to convince or
24 | educate it always has to change audience 25 |
perception. We seek insights to make decks 26 |
unexpectedly enlightening for our audience. 27 |

28 |
29 |
30 |
31 | img 36 |
37 |

38 | The presentation helps to see what's hidden, 39 |
unseen, or simply never known before. We use
40 | design to drive their attention, focus them to
41 | clearly see, and help them feel the message. 42 |

43 |
44 |
45 |
46 |
47 |
48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /container/about-page/Publication.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { insightsPublicationItems } from "@/constants"; 3 | 4 | export default function Publication() { 5 | return ( 6 |
7 |
8 |
9 |
10 |

11 | Latest publication 12 |

13 |
14 |
15 | {insightsPublicationItems.map((item) => ( 16 |
19 |
20 | img 27 |
28 |

29 | {item.title} 30 |

31 |
32 |
33 |
34 | ))} 35 |
36 |
37 |
38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /container/about-page/Team.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { Team1, logo } from "@/public"; 3 | import { Marquee } from "@/components"; 4 | 5 | export default function Team() { 6 | return ( 7 |
8 |
9 | 13 | 14 |
15 |
16 |
17 |
18 |
19 | ochi-logo 25 |
26 |
27 | ochi-logo 34 |

35 | Founder and CEO 36 |

37 |
38 |
39 |
40 |
41 |

42 | IHOR
HULYAHRODSKYY 43 |

44 |
45 |
46 |

47 | 1 / 4 48 |

49 |
50 |
51 |
52 |
53 |
54 | 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /container/contact-page/Faq.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useState } from "react"; 3 | import { FaqItems, clientsItem } from "@/constants"; 4 | import { motion, AnimatePresence } from "framer-motion"; 5 | 6 | export default function Faq() { 7 | const [activeAccordion, setActiveAccordion] = useState(clientsItem[0].id); 8 | const toggleAccordion = (itemId: any) => { 9 | setActiveAccordion((prev) => (prev === itemId ? null : itemId)); 10 | }; 11 | 12 | return ( 13 |
14 |

15 | A few things you
16 | may want to ask us: 17 |

18 | {FaqItems.map((item) => ( 19 |
26 |
27 |
28 |

29 | {item.question} 30 |

31 |
32 |
33 |
34 |

35 | {item.title} 36 |

37 |
38 |
39 | 48 |
49 |
50 |
51 |
52 |
53 |
54 | 55 | {activeAccordion === item.id && ( 56 | 64 |
65 |
66 |

67 | {item.description} 68 |

69 |
70 | {item.links.map((link) => ( 71 |
74 | 75 | {link.title} 76 | 77 |

78 | {link.description} 79 |

80 |
81 | ))} 82 |
83 |
84 | )} 85 |
86 |
87 |
88 |
89 | ))} 90 |
91 | ); 92 | } 93 | -------------------------------------------------------------------------------- /container/contact-page/Form.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { RoundButton } from "@/components"; 3 | 4 | export default function Form() { 5 | return ( 6 |
7 |
8 |
9 |
10 |
11 |

12 | Hi! My name is 13 |

14 |
15 |
16 | 21 |
22 |
23 |
24 |
25 |

26 | and I work with 27 |

28 |
29 |
30 | 35 |
36 |
37 |
38 |
39 |
40 |
41 |

42 | I’m looking for a partner to help me with 43 |

44 |
45 |
46 | 51 |
52 |
53 |
54 |
55 |
56 |
57 |

58 | With an idea of having that completed 59 |

60 |
61 |
62 | 67 |
68 |
69 |
70 |
71 |
72 |
73 |

74 | I am hoping to stay around a budget range of 75 |

76 |
77 |
78 | 83 |
84 |
85 |
86 |
87 |
88 |
89 |

90 | You can reach me at 91 |

92 |
93 |
94 | 99 |
100 |
101 |

102 | to start the conversation. 103 |

104 |
105 |
106 |
107 |
108 |
109 |
110 |

111 | Optionally, i’m sharing more: 112 |

113 |
114 |
115 | 120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | 132 |

133 | I agree with the 134 |

135 |
136 | 139 | Privacy Policy 140 | 141 |
142 |
143 | 150 |
151 |
152 |
153 |
154 | ); 155 | } 156 | -------------------------------------------------------------------------------- /container/contact-page/Hero.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { contactHero } from "@/public"; 3 | import { motion } from "framer-motion"; 4 | 5 | export default function Hero() { 6 | return ( 7 |
8 |
9 |
10 |

11 |
12 | 20 | img 27 | 28 |

29 | LET’S START
30 |

31 |
32 | A PROJECT TOGETHER 33 |

34 |
35 |
36 |

37 | Fill the form below: 38 |

39 |
40 |
41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /container/contact-page/Socials.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { eyes } from "@/public"; 5 | import { ArrowUpRight } from "lucide-react"; 6 | import { LinkHover, TextMask } from "@/animation"; 7 | import { useEffect, useState, useRef } from "react"; 8 | import { useScroll, useTransform, motion } from "framer-motion"; 9 | import { Eyes } from "@/components"; 10 | 11 | export default function Socials() { 12 | const [rotate, setRotate] = useState(0); 13 | const phrase = ["INSTAGRAM", "behance", "facebook", "linkedin"]; 14 | useEffect(() => { 15 | window.addEventListener("mousemove", (e) => { 16 | let mouseX = e.clientX; 17 | let mouseY = e.clientY; 18 | 19 | let deltaX = mouseX - window.innerWidth / 2; 20 | let deltaY = mouseY - window.innerHeight / 2; 21 | 22 | var angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI); 23 | setRotate(angle - 280); 24 | }); 25 | }, []); 26 | const container = useRef(null); 27 | 28 | const { scrollYProgress } = useScroll({ 29 | target: container, 30 | offset: ["start end", "end start"], 31 | }); 32 | 33 | const mq = useTransform(scrollYProgress, [0, 1], [0, -700]); 34 | return ( 35 |
38 |
39 |
40 |

41 | {phrase} 42 |

43 |
44 |
45 |
46 |
47 |

48 | Our contact 49 |

50 |
51 |
52 |
53 |

54 | L: 55 |

56 |
57 | 62 | 67 | 72 | 77 |
78 |
79 |
80 |
81 | 84 | hello@ochi.design 85 | 86 |
87 |
88 |

89 | 93 |

94 |
95 |
96 |
97 |
98 |
99 |
100 | 103 | 104 | 105 |
106 | ); 107 | } 108 | -------------------------------------------------------------------------------- /container/home-page/About.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Image from "next/image"; 3 | import { useState } from "react"; 4 | import { aboutImg } from "@/public"; 5 | import { LinkHover } from "@/animation"; 6 | import { footerItems } from "@/constants"; 7 | import { Heading, RoundButton } from "@/components"; 8 | 9 | export default function About() { 10 | const [hovered, setHovered] = useState(false); 11 | 12 | return ( 13 |
14 |
15 |

16 | Ochi is a strategic partner for fast-grow­ing tech 17 |
businesses that need to  18 | 19 | raise funds, 20 | 21 |  sell prod­ucts,
22 | 23 | ex­plain com­plex ideas, 24 | 25 |  and  26 | 27 | hire great peo­ple. 28 | 29 |

30 |
31 |
32 |
33 |
34 |

35 | What you can expect? 36 |

37 |
38 |
39 |
40 |
41 |

42 | We create tailored presentations to help you persuade your 43 | colleagues, clients, or investors. Whether it’s live or 44 | digital, delivered for one or a hundred people. 45 |

46 |

47 | We believe the mix of strategy and design (with a bit of 48 | coffee) is what makes your message clear, convincing, and 49 | captivating. 50 |

51 |
52 |
53 |

54 | S: 55 |

56 |
57 | {footerItems.map((item) => ( 58 | 64 | ))} 65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | 74 |
setHovered(true)} 77 | onMouseLeave={() => setHovered(false)}> 78 | 85 |
86 |
87 |
91 | about-img 98 |
99 |
100 |
101 | ); 102 | } 103 | -------------------------------------------------------------------------------- /container/home-page/Clients.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { useState } from "react"; 5 | import { clientsItem } from "@/constants"; 6 | import { Button, Ratings } from "@/components"; 7 | import { motion, AnimatePresence } from "framer-motion"; 8 | 9 | export default function Clients() { 10 | const [activeAccordion, setActiveAccordion] = useState(clientsItem[0].id); 11 | const toggleAccordion = (itemId: any) => { 12 | setActiveAccordion((prev) => (prev === itemId ? null : itemId)); 13 | }; 14 | 15 | return ( 16 |
17 |

18 | Clients’ reviews 19 |

20 | {clientsItem.map((item) => ( 21 |
28 |
29 |
30 |
31 | 34 | {item.website} 35 | 36 |
37 |
38 | 42 | {item.title} 43 | 44 |
45 |
46 |
47 |
48 |

49 | {item.name} 50 |

51 |
52 |
53 | 62 |
63 |
64 |
65 | 66 |
68 |
69 |
70 | {item.links.map((link) => ( 71 | 72 | {activeAccordion === item.id && ( 73 | 81 |
91 |
92 | 93 | {activeAccordion === item.id && ( 94 | 102 |
103 |
104 | clientImg 109 |
110 |
111 |

112 | {item.review} 113 |

114 |
115 |
116 |
117 | )} 118 |
119 |
120 |
121 |
122 |
123 | ))} 124 |
125 | 126 |
127 |
128 | ); 129 | } 130 | -------------------------------------------------------------------------------- /container/home-page/Hero.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { motion } from "framer-motion"; 5 | import { ArrowUpRight } from "lucide-react"; 6 | import { awwwards, ochiside } from "@/public"; 7 | 8 | export default function Hero() { 9 | return ( 10 |
14 |
15 |
16 |
17 |
18 |
19 |

20 | we create
21 |
22 | 31 | img 38 | 39 |

40 | eye-opening 41 |

42 |
43 | presentation 44 |

45 |
46 |
47 | awwwards 54 |
55 |
56 |
57 |
58 |
59 |

60 | For public and private companies 61 |

62 |
63 |
64 |
65 |

66 | From the first picth to IPO 67 |

68 |
69 |
70 |
71 | 74 | start the project 75 | 76 |
77 |
78 |

79 | 83 |

84 |
85 |
86 |
87 |
88 |
89 | 98 | scroll down 99 | 100 |
101 |
102 |
103 |
104 |
105 | ); 106 | } 107 | -------------------------------------------------------------------------------- /container/home-page/Projects.tsx: -------------------------------------------------------------------------------- 1 | import { projectItem } from "@/constants"; 2 | import { Heading, ProjectCard, RoundButton, Tags } from "@/components"; 3 | 4 | export default function Projects() { 5 | return ( 6 |
7 | 11 |
12 | {projectItem.map((item) => ( 13 |
16 |
17 | 18 |

19 | {item.title} 20 |

21 |
22 | 26 |
27 | {item.links.map((link) => ( 28 | 34 | ))} 35 |
36 |
37 | ))} 38 |
39 |
40 |
41 | 48 |
49 |
50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /container/home-page/Video.tsx: -------------------------------------------------------------------------------- 1 | import { PlayVideo } from "@/components"; 2 | 3 | export default function Video() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /container/index.ts: -------------------------------------------------------------------------------- 1 | // home page 2 | export { default as Hero } from "./home-page/Hero"; 3 | export { default as About } from "./home-page/About"; 4 | export { default as PlayVideo } from "./home-page/Video"; 5 | export { default as Projects } from "./home-page/Projects"; 6 | export { default as Clients } from "./home-page/Clients"; 7 | export { default as VideoHome } from './home-page/Video'; 8 | 9 | // services page 10 | export { default as Heroservices } from "./services-page/Hero"; 11 | export { default as Process } from "./services-page/Process"; 12 | export { default as Capibilyties } from "./services-page/Capibilyties"; 13 | export { default as Clientsservices } from "./services-page/Clients"; 14 | export { default as Expectations } from "./services-page/Expectations"; 15 | export { default as Archive } from "./services-page/Archive"; 16 | 17 | // presentation page 18 | export { default as Heropresentation } from './presentation-page/Hero'; 19 | export { default as Projectspresentation } from './presentation-page/Projects'; 20 | export { default as Publication } from './presentation-page/Publication'; 21 | 22 | // aboute page 23 | export { default as Heroabout } from './about-page/Hero'; 24 | export { default as Aboutabout } from './about-page/About'; 25 | export { default as Team } from './about-page/Team'; 26 | export { default as Principles } from './about-page/Principles'; 27 | export { default as Partners } from './about-page/Partners'; 28 | export { default as Insights } from './about-page/Insights'; 29 | 30 | // insights page 31 | export { default as Heroinsights } from './insights-page/Hero'; 32 | export { default as Publicationinsights } from './insights-page/Publication'; 33 | 34 | // contact page 35 | export { default as Herocontact } from './contact-page/Hero'; 36 | export { default as Form } from './contact-page/Form'; 37 | export { default as FAQ } from './contact-page/Faq'; 38 | export { default as Socials } from './contact-page/Socials'; 39 | // workiz page 40 | export { default as Heroworkiz } from './workiz-page/Hero'; 41 | export { default as Aboutworkiz } from './workiz-page/About'; 42 | export { default as Works } from './workiz-page/Works'; 43 | export { default as Chelenge } from './workiz-page/Chelenge'; 44 | export { default as Result } from './workiz-page/Result'; 45 | export { default as Credit } from './workiz-page/Credit'; 46 | export { default as VideoWorkiz } from './workiz-page/Video'; -------------------------------------------------------------------------------- /container/insights-page/Hero.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Image from "next/image"; 3 | import { latestItemss } from "@/constants"; 4 | import { Rounded, Tags } from "@/components"; 5 | 6 | export default function Hero() { 7 | const str = [ 8 | { 9 | id: 1, 10 | title: "all", 11 | href: "/", 12 | }, 13 | { 14 | id: 2, 15 | title: "presentation template", 16 | href: "/", 17 | }, 18 | { 19 | id: 3, 20 | title: "public speaking", 21 | href: "/", 22 | }, 23 | { 24 | id: 4, 25 | title: "storytelling", 26 | href: "/", 27 | }, 28 | ]; 29 | return ( 30 |
31 |
32 |
33 |
34 |
35 |

36 | INSIGHTS 37 |

38 |
39 |
40 |
41 |
42 |
43 |

44 | Latest insights: 45 |

46 |
47 |
48 | {str.map((item, i) => ( 49 |
50 | {item.id === 1 ? ( 51 |
54 | 57 |
58 |

{item.title}

59 |
60 | 61 |
62 | ) : ( 63 | 68 | )} 69 |
70 | ))} 71 |
72 |
73 |
74 |
75 |
76 | {latestItemss.map((item) => ( 77 |
80 | 81 |
82 | img 87 |
88 |
89 | {item.links.map((link) => ( 90 |
93 |
94 | 97 | {link.title} 98 | 99 |
100 |
101 | ))} 102 |
103 |
104 |

105 | Presenting to an International Audience:
Tips and 106 | Lessons Learned. 107 |

108 |

109 | {item.subTitle} 110 |

111 |

112 | {item.date} 113 |

114 |
115 | 116 |
117 | ))} 118 |
119 |
120 |
121 |
122 |
123 | ); 124 | } 125 | -------------------------------------------------------------------------------- /container/insights-page/Publication.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { Marquee } from "@/components"; 3 | import { insightsPublicationItems } from "@/constants"; 4 | 5 | export default function Publication() { 6 | return ( 7 |
8 |
9 | 13 | 14 |
15 |
16 |
17 |

18 | Latest publication 19 |

20 |
21 |
22 | {insightsPublicationItems.map((item) => ( 23 |
26 |
27 |
28 | img 33 |
34 |
35 | 36 |

37 | {item.title} 38 |

39 |
40 |
41 |
42 | ))} 43 |
44 |
45 |
46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /container/presentation-page/Hero.tsx: -------------------------------------------------------------------------------- 1 | import { Eyes } from "@/components"; 2 | 3 | export default function Hero() { 4 | return ( 5 |
6 |
7 |
8 |

9 | work 10 | 11 | (9) 12 | 13 |

14 |
15 |
16 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /container/presentation-page/Projects.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useRef } from "react"; 3 | import { ProjectCard, Tags } from "@/components"; 4 | import { presentationProjectItem } from "@/constants"; 5 | 6 | export default function Projects() { 7 | const container = useRef(null); 8 | return ( 9 |
12 |
13 |
18 |
23 |
24 |
25 |
26 |

27 | Purpose driven, strategy-led presentations 28 |
29 | that people care about. 30 |

31 |
32 |
33 | {presentationProjectItem.map((item) => ( 34 |
37 |
38 | 39 |

40 | {item.title} 41 |

42 |
43 | 47 |
48 | {item.links.map((link) => ( 49 | 55 | ))} 56 |
57 |
58 | ))} 59 |
60 |
61 |
62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /container/presentation-page/Publication.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { Marquee } from "@/components"; 3 | import { publicationItems } from "@/constants"; 4 | 5 | export default function Publication() { 6 | return ( 7 |
8 |
9 | 13 | 14 |
15 |
16 |
17 |

18 | Latest publication 19 |

20 |
21 |
22 | {publicationItems.map((item) => ( 23 |
26 |
27 |
28 | asd 33 |
34 |
35 | 36 |

37 | {item.title} 38 |

39 |
40 |
41 |
42 | ))} 43 |
44 |
45 |
46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /container/services-page/Archive.tsx: -------------------------------------------------------------------------------- 1 | import { background } from "@/public"; 2 | import { achiveItems } from "@/constants"; 3 | import { BackgroundImg } from "@/components"; 4 | import { TextMask } from "@/animation"; 5 | 6 | export default function Archive() { 7 | return ( 8 |
9 |
10 | 11 |
12 |
13 |
14 |
15 |

16 | Ochi in numbers: 17 |

18 |
19 |
20 | {achiveItems.map((item) => ( 21 |
24 |
25 |
26 |

27 | {item.title1} 28 |

29 |
30 |
31 | 34 |
35 |
36 |
37 |
38 |

39 | {item.title2} 40 |

41 |
42 |
43 | 46 |
47 |
48 |
49 | ))} 50 |
51 |
52 |
53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /container/services-page/Capibilyties.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Image from "next/image"; 3 | import { useState } from "react"; 4 | import { Button } from "@/components"; 5 | import { serviceCapaybilitiesItem } from "@/constants"; 6 | 7 | export default function Capibilyties() { 8 | const [hovered, setHovered] = useState(false); 9 | const [hovered1, setHovered1] = useState(false); 10 | return ( 11 |
12 |
13 |

14 | 15 | Let’s be honest. 16 | 17 |  There are really no excuses to have a bad presentation anymore. 18 | No one has time for poorly communicated ideas. Focus on what you do 19 | best 20 | growing your business, while we do our best at 21 | 22 |  making your presentations awesome. 23 | 24 |

25 |
26 |
27 |
28 |

29 | Our Capabilities: 30 |

31 |
32 |
33 | {serviceCapaybilitiesItem.map((item) => ( 34 |
37 |
38 | {hovered && item.id === 1 ? ( 39 | img 46 | ) : ( 47 | hovered1 && 48 | item.id === 1 && ( 49 | img 56 | ) 57 | )} 58 |
59 |
60 |
61 |
62 | 63 |

64 | {item.title1} 65 |

66 |
67 |
68 |
setHovered(item.id === 1 && true)} 71 | onMouseLeave={() => setHovered(item.id === 1 && false)}> 72 | {item.links1.map((link) => ( 73 |
80 |
81 |
82 |
83 |
84 | 85 |

86 | {item.title2} 87 |

88 |
89 |
90 |
setHovered1(item.id === 1 && true)} 93 | onMouseLeave={() => setHovered1(item.id === 1 && false)}> 94 | {item.links2.map((link) => ( 95 |
102 |
103 |
104 | ))} 105 |
106 |
107 |
108 | ); 109 | } 110 | -------------------------------------------------------------------------------- /container/services-page/Clients.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { useState } from "react"; 5 | import { Button } from "@/components"; 6 | import { serviceClientsItem } from "@/constants"; 7 | import { motion, AnimatePresence } from "framer-motion"; 8 | 9 | export default function Clients() { 10 | const [activeAccordion, setActiveAccordion] = useState( 11 | serviceClientsItem[0].id, 12 | ); 13 | const toggleAccordion = (itemId: any) => { 14 | setActiveAccordion((prev) => (prev === itemId ? null : itemId)); 15 | }; 16 | return ( 17 |
18 |

19 | Clients’ reviews 20 |

21 | {serviceClientsItem.map((item) => ( 22 |
29 |
30 |
31 |
32 | 35 | {item.website} 36 | 37 |
38 |
39 | 43 | {item.title} 44 | 45 |
46 |
47 |
48 |
49 |

50 | {item.name} 51 |

52 |
53 |
54 | 63 |
64 |
65 |
66 | 67 |
69 |
70 |
71 | {item.links.map((link) => ( 72 | 73 | {activeAccordion === item.id && ( 74 | 82 |
92 |
93 | 94 | {activeAccordion === item.id && ( 95 | 103 |
104 |
105 | clientImg 110 |
111 |
112 |

113 | {item.review} 114 |

115 |
116 |
117 |
118 | )} 119 |
120 |
121 |
122 |
123 |
124 | ))} 125 |
126 | ); 127 | } 128 | -------------------------------------------------------------------------------- /container/services-page/Expectations.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useState } from "react"; 3 | import { Marquee } from "@/components"; 4 | import { TextHover } from "@/animation"; 5 | import { expectationsItems } from "@/constants"; 6 | import { AnimatePresence, motion } from "framer-motion"; 7 | 8 | export default function Expectations() { 9 | const [openItemId, setOpenItemId] = useState(null); 10 | 11 | const handleButtonClick = (id: any) => { 12 | setOpenItemId(openItemId === id ? null : id); 13 | }; 14 | 15 | return ( 16 |
17 |
18 | 22 | 23 |
24 |
25 |
26 |

27 | What you can expect? 28 |

29 |
30 |
31 | {expectationsItems.map((item) => ( 32 |
35 |
36 |
37 |

38 | {item.title1} 39 |

40 |
41 |
42 | 48 | 60 |
61 | 62 | {openItemId === item.id && ( 63 | 71 |
72 | {item.para1} 73 |
74 |
75 | )} 76 |
77 |
78 |
79 | ))} 80 |
81 |
82 |
83 | 84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /container/services-page/Hero.tsx: -------------------------------------------------------------------------------- 1 | export default function Hero() { 2 | return ( 3 |
4 |
5 |
6 |
7 |
8 |

9 | services 10 |

11 |
12 |
13 |
14 |

15 | We create  16 | 17 | eye-catching  18 | 19 | and  20 | 21 | eye-opening  22 | 23 | presentations that educate, inspire and influence action. 24 |

25 |
26 |
27 |
28 |
29 |

30 | We do this by following
simple approach: 31 |

32 |
33 |
34 |
35 |
36 |

37 | Goal defines it all 38 |

39 |

40 | What do you want to achieve? 41 |
Understanding the 42 | purpose of your
43 | presentation allows us to tailor it to ensure it 44 |
hits the mark and 45 | drives results. 46 |

47 |
48 |
49 |

50 | Audience is the hero 51 |

52 |

53 | Who is it for? What do they want? Why 54 |
does it matter to 55 | them? We need to know 56 |
your audience well 57 | enough to deliver a
58 | personalized presentation that they truly 59 |
60 | care about. 61 |

62 |
63 |
64 |
65 |
66 |

67 | Context makes a difference 68 |

69 |

70 | When do you present? Online or live? At a 71 |
72 | sales meeting, at a conference, or just 73 |
sending a cold 74 | email? We knit the context 75 |
together to decide 76 | the style of the 77 |
presentation. 78 |

79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /container/services-page/Process.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { useState } from "react"; 3 | import { serviceProcessItems } from "@/constants"; 4 | import { AnimatePresence, motion } from "framer-motion"; 5 | 6 | export default function Process() { 7 | const [activeAccordion, setActiveAccordion] = useState( 8 | serviceProcessItems[0].id, 9 | ); 10 | const toggleAccordion = (itemId: any) => { 11 | setActiveAccordion((prev) => (prev === itemId ? null : itemId)); 12 | }; 13 | 14 | return ( 15 |
16 |
17 |

18 | Holistic process 19 |

20 |
21 | {serviceProcessItems.map((item) => ( 22 |
29 |
30 |
31 |

32 | {item.phase} 33 |

34 |
35 |
36 |

37 | {item.name} 38 |

39 |
40 |
41 | 50 |
51 |
52 |
54 |
55 |
56 | 57 | {activeAccordion === item.id && ( 58 | 66 |
67 |
68 | clientImg 73 |
74 |
75 |

76 | {item.review} 77 |

78 |
79 |
80 |
81 | )} 82 |
83 |
84 |
85 |
86 |
87 | ))} 88 |
89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /container/workiz-page/About.tsx: -------------------------------------------------------------------------------- 1 | import { welcome } from "@/public"; 2 | import { BackgroundImg } from "@/components"; 3 | 4 | export default function About() { 5 | return ( 6 |
7 |
8 |

9 | Compony 10 |

11 |
12 |
13 |
14 |

15 | About: 16 |

17 |
18 |
19 |
20 |

21 | Built by home service professionals on a 22 |
mission to make your 23 | “work easy,”
24 | Workiz empowers service businesses to get more 25 |
jobs done, work easier, 26 | and grow smarter. 27 |

28 |
29 |
30 |
31 |

32 | Industry: 33 |

34 |

35 | Service Management Software 36 |

37 |
38 |
39 |

40 | Company Size: 41 |

42 |

43 | 100+ People 44 |

45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 |
54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /container/workiz-page/Chelenge.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { 3 | after, 4 | chelengeBg, 5 | showcase1, 6 | showcase2, 7 | showcase3, 8 | showcase4, 9 | showcase5, 10 | showcase6, 11 | } from "@/public"; 12 | import { BackgroundImg } from "@/components"; 13 | 14 | export default function Chelenge() { 15 | return ( 16 |
17 |
18 |

19 | Challenge & Solution 20 |

21 |
22 |
23 |
24 |

25 | Services we provided: 26 |

27 |
28 |
29 |
30 |

31 | The goal was to build straightforward,
professional, 32 | but emotional presentations 33 |
that are delivered to colleagues and 34 |
35 | clients. We’ve tailored the decks for the
36 | audiences and amplified the presentation 37 |
38 | materials to match the brand’s quality and
39 | improve the message. 40 |

41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 |
49 |
50 |
51 | chelengeBgImg 56 |
57 |
58 | chelengeBgImg 63 |
64 |
65 |
66 | 67 |
68 |
69 |
70 | chelengeBgImg 75 |
76 |
77 | chelengeBgImg 82 |
83 |
84 |
85 | 86 |
87 |
88 |
89 | chelengeBgImg 94 |
95 |
96 |
97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /container/workiz-page/Credit.tsx: -------------------------------------------------------------------------------- 1 | export default function Credit() { 2 | return ( 3 |
4 |
5 |

6 | Credit 7 |

8 |
9 |
10 |
11 |

12 | Services we provided: 13 |

14 |
15 |
16 |
17 |
18 |

19 | Client: 20 |

21 |

22 | Tomer Levy 23 |

24 |
25 |
26 |

27 | Project Manager: 28 |

29 |

30 | Ihor Hulyahrodskyy 31 |

32 |
33 |
34 |
35 |
36 |

37 | Team: 38 |

39 |

40 | Ihor Hulyahrodskyy, Kseniia Palamarchuk, 41 |
Olha Sereda 42 |

43 |
44 |
45 |

46 | Motion & Animation: 47 |

48 |

49 | Vadym Herasymenko 50 |

51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /container/workiz-page/Hero.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Image from "next/image"; 3 | import { motion } from "framer-motion"; 4 | import { workiz, workizhero } from "@/public"; 5 | import { BackgroundImg, Rounded } from "@/components"; 6 | 7 | export default function Hero() { 8 | return ( 9 |
10 |
11 |
12 |
13 |
14 |

15 |
16 | 25 | img 32 | 33 |

34 | WORKIZ EASY 35 |

36 |
37 |

38 |
39 |
40 |
41 |
42 |
43 |

44 | Description: 45 |

46 |
47 |
48 |
49 |

50 | Onboarding, internal and sales
51 | presentations for software startup. 52 |

53 |
54 |
55 |
56 | 59 | 62 |

63 | onboarding presentations 64 |

65 |
66 | 67 |
68 |
69 | 72 | 75 |

76 | policy deck and playbook 77 |

78 |
79 | 80 |
81 |
82 | 85 | 88 |

sales deck

89 |
90 | 91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | 100 |
101 |
102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /container/workiz-page/Result.tsx: -------------------------------------------------------------------------------- 1 | export default function Result() { 2 | return ( 3 |
4 |
5 |

6 | The Result 7 |

8 |
9 |
10 |
11 |

12 | Project Outcomes: 13 |

14 |
15 |
16 |
17 |

18 | Feedback 19 |

20 |

21 | “OCHI brought certain level of 22 |
professionalism into 23 | our presentations that 24 |
we were lacking before. 25 | When I showed
26 | our management and HR teams the 27 |
28 | presentation OCHI developed, they were 29 |
30 | amazed

31 | — 32 |

{" "} 33 | the final product was exactly 34 |
35 | what we needed to create a better 36 |
experience for new 37 | employees and our
38 | clients.” 39 |

40 |
41 |
42 |

43 | The Result 44 |

45 |

46 | We’ve created 10 presentations for Workiz 47 |
company which improved 48 | the on-boarding
49 | process, empowered sales team, and just made 50 |
51 | their work easier 52 |

53 |
54 |
55 |
56 |
57 |
58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /container/workiz-page/Video.tsx: -------------------------------------------------------------------------------- 1 | import { PlayVideo } from "@/components"; 2 | 3 | export default function Video() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /container/workiz-page/Works.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { workizItem } from "@/constants"; 3 | import { ArrowUpRight } from "lucide-react"; 4 | import { Marquee, ProjectCard, Rounded, Tags } from "@/components"; 5 | 6 | export default function Works() { 7 | return ( 8 |
9 |
10 | 14 | 15 |
16 | {workizItem.map((item) => ( 17 |
20 |
21 | 22 |

23 | {item.title} 24 |

25 |
26 | 30 |
31 | {item.links.map((link) => ( 32 | 38 | ))} 39 |
40 |
41 | ))} 42 |
43 |
44 |
45 | 48 | 51 |

52 | view all case studies 53 |

54 |
55 | 60 |
61 |
62 | 63 |
64 |
65 | 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /fonts/FoundersGrotesk.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devwithzain/ochi-website-clone/8b233cb4a26f3b197c457213673341e97fb2ebac/fonts/FoundersGrotesk.woff -------------------------------------------------------------------------------- /fonts/NeueMontreal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devwithzain/ochi-website-clone/8b233cb4a26f3b197c457213673341e97fb2ebac/fonts/NeueMontreal.woff -------------------------------------------------------------------------------- /motion/index.ts: -------------------------------------------------------------------------------- 1 | export const navVariants = { 2 | hidden: { y: "-100%" }, 3 | vissible: { y: 0, transition: { ease: [0.76, 0, 0.24, 1], duration: 0.7 }, } 4 | }; 5 | 6 | export const navVariants1 = { 7 | hidden: { y: "-100%" }, 8 | vissible: { y: 0, transition: { ease: [0.76, 0, 0.24, 1], duration: 0.7 }, } 9 | }; 10 | 11 | export const footerVarient = { 12 | hidden: { y: 150, opacity: 0 }, 13 | vissible: { y: 0, opacity: 1, transition: { duration: 1, ease: [0.76, 0, 0.24, 1] }, }, 14 | }; 15 | 16 | 17 | export const animation = { 18 | initial: { y: "100%" }, 19 | visible: { 20 | y: "0", 21 | transition: { 22 | duration: 0.75, 23 | ease: [0.33, 1, 0.68, 1], 24 | }, 25 | }, 26 | }; 27 | 28 | 29 | // navbar 30 | // MENUSLIDE 31 | export const menuSlide = { 32 | initial: { x: "calc(100% + 100px)" }, 33 | enter: { x: "0", transition: { duration: 0.8, ease: [0.76, 0, 0.24, 1] } }, 34 | exit: { 35 | x: "calc(100% + 100px)", 36 | transition: { duration: 0.8, ease: [0.76, 0, 0.24, 1] }, 37 | }, 38 | }; 39 | // SLIDE 40 | export const slide = { 41 | initial: { x: 80 }, 42 | enter: (i: number) => ({ 43 | x: 0, 44 | transition: { duration: 0.8, ease: [0.76, 0, 0.24, 1], delay: 0.05 * i }, 45 | }), 46 | exit: (i: number) => ({ 47 | x: 80, 48 | transition: { duration: 0.8, ease: [0.76, 0, 0.24, 1], delay: 0.05 * i }, 49 | }), 50 | }; 51 | 52 | // SCALE 53 | export const scale = { 54 | open: { scale: 1, transition: { duration: 0.3 } }, 55 | closed: { scale: 0, transition: { duration: 0.4 } }, 56 | }; 57 | 58 | // CURVE 59 | let initialPath = ""; 60 | let targetPath = ""; 61 | 62 | if (typeof window !== "undefined") { 63 | initialPath = `M100 0 L200 0 L200 ${window.innerHeight} L100 ${window.innerHeight 64 | } Q-100 ${window.innerHeight / 2} 100 0`; 65 | targetPath = `M100 0 L200 0 L200 ${window.innerHeight} L100 ${window.innerHeight 66 | } Q100 ${window.innerHeight / 2} 100 0`; 67 | } 68 | 69 | export const text = { 70 | initial: { 71 | opacity: 1, 72 | }, 73 | enter: { 74 | opacity: 0, 75 | top: -100, 76 | transition: { duration: .75, delay: .35, ease: [0.76, 0, 0.24, 1] }, 77 | transitionEnd: { top: "47.5%" } 78 | }, 79 | exit: { 80 | opacity: 1, 81 | top: "40%", 82 | transition: { duration: .5, delay: .4, ease: [0.33, 1, 0.68, 1] } 83 | } 84 | }; 85 | 86 | export const curve = (initialPath: string, targetPath: string) => { 87 | return { 88 | initial: { 89 | d: initialPath 90 | }, 91 | enter: { 92 | d: targetPath, 93 | transition: { duration: .75, delay: .35, ease: [0.76, 0, 0.24, 1] } 94 | }, 95 | exit: { 96 | d: initialPath, 97 | transition: { duration: .75, ease: [0.76, 0, 0.24, 1] } 98 | } 99 | }; 100 | }; 101 | 102 | export const translate = { 103 | initial: { 104 | top: "-300px" 105 | }, 106 | enter: { 107 | top: "-100vh", 108 | transition: { duration: .75, delay: .35, ease: [0.76, 0, 0.24, 1] }, 109 | transitionEnd: { 110 | top: "100vh" 111 | } 112 | }, 113 | exit: { 114 | top: "-300px", 115 | transition: { duration: .75, ease: [0.76, 0, 0.24, 1] } 116 | } 117 | }; 118 | 119 | 120 | export const opacity = { 121 | initial: { 122 | opacity: 0 123 | }, 124 | enter: { 125 | opacity: 0.75, 126 | transition: { duration: 1, delay: 0.2 } 127 | }, 128 | }; 129 | 130 | export const slideUp = { 131 | initial: { 132 | top: 0 133 | }, 134 | exit: { 135 | top: "-100vh", 136 | transition: { duration: 0.8, ease: [0.76, 0, 0.24, 1], delay: 0.2 } 137 | } 138 | }; 139 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ochi-website-clone", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@types/locomotive-scroll": "^4.1.3", 13 | "framer-motion": "^11.0.25", 14 | "gsap": "^3.12.5", 15 | "locomotive-scroll": "^5.0.0-beta.8", 16 | "lucide-react": "^0.365.0", 17 | "next": "14.1.4", 18 | "react": "^18", 19 | "react-dom": "^18", 20 | "react-icons": "^5.1.0", 21 | "react-intersection-observer": "^9.8.1", 22 | "sharp": "^0.33.3" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^20", 26 | "@types/react": "^18", 27 | "@types/react-dom": "^18", 28 | "autoprefixer": "^10.0.1", 29 | "eslint": "^8", 30 | "eslint-config-next": "14.1.4", 31 | "postcss": "^8", 32 | "tailwindcss": "^3.3.0", 33 | "typescript": "^5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css"; 2 | import { Footer, Navbar } from "@/components"; 3 | import { AnimatePresence } from "framer-motion"; 4 | 5 | export default function App({ 6 | Component, 7 | pageProps, 8 | router, 9 | }: { 10 | Component: any; 11 | pageProps: any; 12 | router: any; 13 | }) { 14 | return ( 15 | <> 16 | 17 | 18 | 22 | 23 |