initial commit

This commit is contained in:
SridharRajamani 2025-06-24 16:27:31 +05:30
commit f1efa3e658
31 changed files with 5343 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

12
README.md Normal file
View File

@ -0,0 +1,12 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.

33
eslint.config.js Normal file
View File

@ -0,0 +1,33 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...js.configs.recommended.rules,
...reactHooks.configs.recommended.rules,
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]

17
index.html Normal file
View File

@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script type="module" src="https://unpkg.com/ionicons@4.5.10-0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule="" src="https://unpkg.com/ionicons@4.5.10-0/dist/ionicons/ionicons.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
</body>
</html>

2829
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "design-thinking-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"animejs": "^4.0.2",
"gsap": "^3.13.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-icons": "^5.5.0"
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@vitejs/plugin-react": "^4.4.1",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"vite": "^6.3.5"
}
}

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src.zip Normal file

Binary file not shown.

1
src/App.css Normal file
View File

@ -0,0 +1 @@

40
src/App.jsx Normal file
View File

@ -0,0 +1,40 @@
import './App.css';
import Navbar from './Components/NavBar';
import HeroSection from './HeroSection';
import Offerings from '../src/Pages/Offerings';
import GSAPHorizontalScroll from './Components/GSAPHorizontalScroll';
function App() {
return (
// <div className="container">
// <div className="noise-overlay"></div>
// <div className="title-section">
// <h1>Pozo</h1>
// <h2> simplifies, automates, and scales with GenAI at its core.</h2>
// </div>
// <div className="subtitle">
// <p>Here is where your presentation begins</p>
// </div>
// <div className="gradient-blur"></div>
// <div className="floral-shape">
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// </div>
// </div>
<>
<Navbar/>
<HeroSection/>
<Offerings/>
<GSAPHorizontalScroll/>
</>
);
}
export default App;

View File

@ -0,0 +1,339 @@
import React, { useEffect, useRef } from 'react';
const GSAPHorizontalScroll = () => {
const containerRef = useRef(null);
const scrollTweenRef = useRef(null);
const redTweenRef = useRef(null);
const isInitialized = useRef(false);
useEffect(() => {
// Prevent multiple initializations
if (isInitialized.current) return;
isInitialized.current = true;
const loadGSAP = async () => {
try {
// Check if GSAP is already loaded
if (window.gsap && window.ScrollTrigger && window.ScrollToPlugin) {
initializeAnimations();
return;
}
// Load GSAP scripts sequentially
const loadScript = (src) => {
return new Promise((resolve, reject) => {
// Check if script already exists
if (document.querySelector(`script[src="${src}"]`)) {
resolve();
return;
}
const script = document.createElement('script');
script.src = src;
script.async = false; // Load in order
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
};
// Load scripts in sequence
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js');
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js');
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollToPlugin.min.js');
// Wait for GSAP to be fully available
let attempts = 0;
const maxAttempts = 50;
const waitForGSAP = () => {
return new Promise((resolve) => {
const checkGSAP = () => {
attempts++;
if (window.gsap && window.ScrollTrigger && window.ScrollToPlugin) {
resolve();
} else if (attempts < maxAttempts) {
setTimeout(checkGSAP, 100);
} else {
console.error('GSAP failed to load after maximum attempts');
resolve(); // Continue anyway
}
};
checkGSAP();
});
};
await waitForGSAP();
initializeAnimations();
} catch (error) {
console.error('Error loading GSAP:', error);
}
};
const initializeAnimations = () => {
try {
const { gsap } = window;
if (!gsap || !window.ScrollTrigger || !window.ScrollToPlugin) {
console.error('GSAP not properly loaded');
return;
}
// Register plugins
gsap.registerPlugin(window.ScrollTrigger, window.ScrollToPlugin);
// Clear any existing ScrollTriggers
window.ScrollTrigger.getAll().forEach(trigger => trigger.kill());
// Helper function to get scroll position
const getScrollPosition = (animation, progress) => {
let p = gsap.utils.clamp(0, 1, progress || 0),
nested = !animation.scrollTrigger,
st = nested ? animation.parent.scrollTrigger : animation.scrollTrigger,
containerAnimation = st.vars.containerAnimation,
range = st.end - st.start,
position = st.start + range * p;
if (containerAnimation) {
st = containerAnimation.scrollTrigger;
return (st.start + (st.end - st.start) * (position / containerAnimation.duration()));
} else if (nested) {
let start = st.start + (animation.startTime() / animation.parent.duration()) * range,
end = st.start + ((animation.startTime() + animation.duration()) / animation.parent.duration()) * range;
return start + (end - start) * p;
}
return position;
};
// Get sections
let sections = gsap.utils.toArray(".panel");
if (sections.length === 0) {
console.error('No panels found');
return;
}
// Main horizontal scroll animation
let scrollTween = gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none",
scrollTrigger: {
trigger: ".container",
pin: true,
scrub: 0.1,
end: "+=3000",
anticipatePin: 1
}
});
scrollTweenRef.current = scrollTween;
// Set initial positions
gsap.set(".box-1, .box-2", {y: 100});
// Red section animation
let redTween = gsap.to(".box-1", {
y: -130,
duration: 2,
ease: "elastic.out(1, 0.3)",
scrollTrigger: {
trigger: ".box-1",
containerAnimation: scrollTween,
start: "left center",
toggleActions: "play none none reset"
}
});
redTweenRef.current = redTween;
// Gray section animation
gsap.to(".box-2", {
y: -120,
backgroundColor: "#1e90ff",
ease: "none",
scrollTrigger: {
trigger: ".box-2",
containerAnimation: scrollTween,
start: "center 80%",
end: "center 20%",
scrub: true
}
});
// Purple section - toggle class
window.ScrollTrigger.create({
trigger: ".box-3",
containerAnimation: scrollTween,
toggleClass: "active",
start: "center 60%"
});
// Green section - callbacks
window.ScrollTrigger.create({
trigger: ".green",
containerAnimation: scrollTween,
start: "center 65%",
end: "center 51%",
onEnter: () => console.log("🟢 Green section entered"),
onLeave: () => console.log("🔴 Green section left"),
onEnterBack: () => console.log("⬅️ Green section entered back"),
onLeaveBack: () => console.log("➡️ Green section left back"),
onToggle: self => console.log("🔄 Green section active:", self.isActive)
});
// Scroll to button functionality
const setupScrollButton = () => {
const scrollToButton = document.querySelector("#scrollTo");
if (scrollToButton && redTween) {
// Remove existing listeners
scrollToButton.replaceWith(scrollToButton.cloneNode(true));
const newButton = document.querySelector("#scrollTo");
newButton.addEventListener("click", () => {
const scrollPosition = getScrollPosition(redTween);
gsap.to(window, {
scrollTo: scrollPosition,
duration: 1.5,
ease: "power2.inOut"
});
});
}
};
// Setup button after a short delay to ensure DOM is ready
setTimeout(setupScrollButton, 100);
console.log('GSAP animations initialized successfully');
} catch (error) {
console.error('Error initializing animations:', error);
}
};
loadGSAP();
// Cleanup function
return () => {
if (window.ScrollTrigger) {
window.ScrollTrigger.getAll().forEach(trigger => trigger.kill());
}
isInitialized.current = false;
};
}, []);
return (
<>
<div className="description">
<div>
<h1>getScrollPosition() helper function</h1>
<p>
This helper function even works with the "containerAnimation" feature as seen here!
Click the button below to scroll this page to the exact spot where the red section's tween starts.
<br /><br />
<button id="scrollTo">scroll to red tween start</button>
</p>
<div className="scroll-down">
Scroll down
<div className="arrow"></div>
</div>
</div>
</div>
<div className="container" ref={containerRef}>
<div className="panel blue">
Scroll down to animate horizontally &gt;
</div>
<section className="panel red">
<div>
<pre className="code-block">
{`gsap.to(".box-1", {
y: -130,
duration: 2,
ease: "elastic",
scrollTrigger: {
trigger: ".box-1",
containerAnimation: scrollTween,
start: "left center",
toggleActions: "play none none reset"
}
});`}
</pre>
<p>Fire an animation at a particular spot...</p>
</div>
<div className="box-1 box">box-1</div>
</section>
<section className="panel gray">
<div>
<pre className="code-block">
{`gsap.to(".box-2", {
y: -120,
backgroundColor: "#1e90ff",
ease: "none",
scrollTrigger: {
trigger: ".box-2",
containerAnimation: scrollTween,
start: "center 80%",
end: "center 20%",
scrub: true
}
});`}
</pre>
<p>...or scrub it back & forth with the scrollbar</p>
</div>
<div className="box-2 box">box-2</div>
</section>
<section className="panel purple">
<div>
<pre className="code-block">
{`ScrollTrigger.create({
trigger: ".box-3",
containerAnimation: scrollTween,
toggleClass: "active",
start: "center 60%"
});`}
</pre>
<p>Toggle a CSS class</p>
</div>
<div className="box-3 box">box-3</div>
</section>
<section className="panel green">
<div>
<pre className="code-block">
{`ScrollTrigger.create({
trigger: ".green",
containerAnimation: scrollTween,
start: "center 65%",
end: "center 51%",
onEnter: () => console.log("enter"),
onLeave: () => console.log("leave"),
onEnterBack: () => console.log("enterBack"),
onLeaveBack: () => console.log("leaveBack"),
onToggle: self => console.log("active", self.isActive)
});`}
</pre>
<p>Use the rich callback system</p>
</div>
</section>
</div>
<div className="final">
<div>
<h1>Wasn't that fun?</h1>
<p>Here are a few caveats to keep in mind:</p>
<ul>
<li>The fake-scrolling animation (just the part that's moving the container horizontally) must have no easing (<code>ease: "none"</code>).</li>
<li>Pinning and snapping won't work on ScrollTriggers with a <code>containerAnimation</code>.</li>
<li>The mapping of scroll position trigger points are based on the trigger element itself not being animated horizontally (inside the container). If you need to animate the trigger, you can either wrap it in a &lt;div&gt; and use that as the trigger instead or just factor the trigger's movement into your end position.</li>
<li>Requires ScrollTrigger 3.8.0 or later</li>
</ul>
</div>
</div>
</>
);
};
export default GSAPHorizontalScroll;

437
src/Components/NavBar.css Normal file
View File

@ -0,0 +1,437 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
}
.navbar-island {
position: fixed;
top: 20px;
left: 50%;
width: 60%;
transform: translateX(-50%);
background-color: white;
border-radius: 30px;
padding: 10px 20px;
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.1);
}
.navbar-island {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%) scale(0.8);
opacity: 0;
background-color: white;
border-radius: 30px;
padding: 10px 20px;
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.1);
animation: popIn 0.3s ease-out forwards;
}
@keyframes popIn {
from {
transform: translateX(-50%) scale(0.2);
opacity: 0;
}
to {
transform: translateX(-50%) scale(1);
opacity: 1;
}
}
.island-content {
display: flex;
justify-content: space-between;
align-items: center;
color:#000;
width: 100%;
max-width: 960px; /* Adjust as needed */
}
.logo {
color: black;
font-family: 'DM Sans', sans-serif;
font-size: 1.2em;
font-weight: bold;
border: 1px solid white;
padding: 5px 10px;
border-radius: 5px;
}
.pozo-logo {
height: 24px; /* Adjust as needed */
vertical-align: middle;
filter: grayscale(100%);
}
.nav-links {
display: flex;
gap: 30px;
margin-left: 50px; /* Space between logo and nav links */
}
.nav-links span {
color: black;
font-family: 'DM Sans', sans-serif;
font-size: 1em;
cursor: pointer;
transition: color 0.3s ease;
}
.nav-links span:hover {
color: #007bff; /* Light blue on hover */
}
.nav-links span.active,
.nav-links span.active .arrow-icon {
color: #007bff; /* Blue color when active */
border-color: #007bff; /* Blue color for arrow icon border when active */
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
position: absolute;
background-color: rgba(255, 255, 255, 0.9);
min-width: 100vw;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 99;
left: 0;
transform: translateX(0);
top: 60px;
padding: 20px 0; /* Reset padding on dropdown-content */
display: flex;
flex-wrap: nowrap;
align-items: flex-start;
border-radius: 0;
color: #333;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease-in-out;
}
.dropdown-content.show {
opacity: 1;
pointer-events: auto;
}
.dropdown-inner-content {
max-width: 960px;
/* margin: 0 auto; */
padding: 0 2rem;
display: flex;
justify-content: space-between; /* Distribute sections */
align-items: flex-start;
gap: 20px;
}
.dropdown-section {
padding: 0 20px;
flex-grow: 1;
}
.dropdown-section.explore-mac {
padding-left: 0;
}
.dropdown-section.shop-mac {
padding: 0 20px;
border-left: 1px solid #e0e0e0;
}
.dropdown-section.more-from-mac {
padding-right: 0;
border-left: 1px solid #e0e0e0;
}
.dropdown-section h3 {
font-size: 0.7em;
font-weight: 400;
margin-bottom: 10px;
color: #6e6e73;
text-transform: uppercase;
}
.dropdown-section a {
color: #1d1d1f;
padding: 5px 0;
text-decoration: none;
display: block;
font-size: 1.2em;
font-weight: 600;
}
.dropdown-section a:hover {
color: #007aff;
}
.dropdown-section.explore-mac a {
font-size: 1.2em;
font-weight: 600;
}
.dropdown-section.explore-mac a.small-link {
font-size: 0.8em;
font-weight: 400;
color: #007aff;
}
.dropdown-section.explore-mac a.small-link:hover {
text-decoration: underline;
}
.dropdown-section.shop-mac {
}
.dropdown-section.shop-mac a {
font-size: 0.9em;
font-weight: 400;
color: #1d1d1f;
}
.dropdown-section.shop-mac a:hover {
color: #007aff;
}
.dropdown-section.more-from-mac {
border-left: 1px solid #eee;
padding-left: 20px;
}
.dropdown-section.more-from-mac a {
font-size: 0.9em;
font-weight: 400;
color: #1d1d1f;
}
.dropdown-section.more-from-mac a:hover {
color: #007aff;
}
.dropdown-content a,
.dropdown-content .nested-dropdown {
color: #333;
padding: 8px 12px;
text-decoration: none;
display: block;
text-align: left;
width: auto;
white-space: normal;
}
.dropdown-content a:hover,
.dropdown-content .nested-dropdown:hover {
background-color: transparent;
color: #007aff;
}
.dropdown-content .nested-dropdown-content a {
color: #333;
}
.dropdown-content .nested-dropdown-content a:hover {
/* background-color: #f1f1f1; */
}
.nested-dropdown {
position: relative;
display: inline-block;
padding: 8px 12px;
color: #333;
}
.nested-dropdown span {
color: #333;
display: block;
padding: 8px 12px;
text-decoration: none;
text-align: left;
white-space: nowrap;
}
.nested-dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 200px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
left: 100%;
top: 0;
padding: 0;
}
.nested-dropdown-content a {
color: #333;
padding: 8px 16px;
text-decoration: none;
display: block;
white-space: nowrap;
}
.nested-dropdown-content a:hover {
/* background-color: #ddd; */
}
.nested-dropdown-content.show {
display: block;
}
.nested-dropdown .arrow-icon {
border-color: #333;
margin-left: 5px;
}
.dropdown-content a,
.dropdown-content .nested-dropdown {
color: #333;
padding: 8px 12px;
text-decoration: none;
display: block;
text-align: left;
width: auto;
white-space: normal;
}
.dropdown-content a:hover,
.dropdown-content .nested-dropdown:hover {
/* background-color: #ddd; */
color: #007aff;
}
.dropdown-content .nested-dropdown-content a {
color: #333;
}
.dropdown-content .nested-dropdown-content a:hover {
background-color: #007bff;
}
.dropdown-content .nested-dropdown-content {
background-color: #007bff;
min-width: 200px;
left: 100%;
top: 0;
padding: 0;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
}
.nested-dropdown {
position: relative;
display: inline-block;
padding: 8px 12px;
color: rgb(0, 0, 0);
}
.nested-dropdown span {
color: #333;
display: block;
padding: 8px 12px;
text-decoration: none;
text-align: left;
white-space: nowrap;
}
.nested-dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 200px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
left: 100%;
top: 0;
padding: 0;
}
.nested-dropdown-content a {
color: black;
padding: 8px 16px;
text-decoration: none;
display: block;
white-space: nowrap;
}
.nested-dropdown-content a:hover {
background-color: #007bff;
}
.nested-dropdown-content.show {
display: block;
}
.nested-dropdown .arrow-icon {
border-color: #333;
margin-left: 5px;
}
.nested-dropdown span.active {
color: #007bff;
}
.nested-dropdown span.active .arrow-icon {
border-color: #007bff;
}
.dropdown-content.show {
display: flex;
opacity: 1;
visibility: visible;
}
.arrow-icon {
margin-left: 3px;
margin-bottom: 2px;
border: solid rgb(7, 7, 7);
border-width: 0 2px 2px 0;
display: inline-block;
padding: 2.2px;
transform: rotate(45deg);
transition: transform 0.3s ease;
font-weight: 200;
}
.arrow-icon.open {
transform: rotate(-135deg);
}
.right-items {
display: flex;
gap: 30px;
margin-left: auto; /* Push to the right */
}
.right-items span {
color: rgb(0, 0, 0);
font-family: 'DM Sans', sans-serif;
font-size: 15px;
cursor: pointer;
transition: color 0.3s ease;
}
.right-items span:hover {
color: #007bff; /* Light blue on hover */
}

100
src/Components/NavBar.jsx Normal file
View File

@ -0,0 +1,100 @@
import React, { useState, useEffect, useRef } from 'react';
import './NavBar.css';
import PozoLogo from '../Images/PozoLogo1.svg';
import { RiStore2Line } from "react-icons/ri";
const NavBar = () => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const dropdownRef = useRef(null);
const toggleDropdown = () => {
setIsDropdownOpen(prev => !prev);
};
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsDropdownOpen(false);
}
};
if (isDropdownOpen) {
document.addEventListener('mousedown', handleClickOutside);
} else {
document.removeEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isDropdownOpen]);
return (
<>
<div className="navbar-island">
<div className="island-content">
{/* Logo */}
<img src={PozoLogo} alt="Pozo Logo" className="pozo-logo" />
{/* Navigation Links */}
<div className="nav-links">
<div className="dropdown" ref={dropdownRef}>
<span onClick={toggleDropdown} className={isDropdownOpen ? 'active' : ''}>Industries <i className={`arrow-icon ${isDropdownOpen ? 'open' : ''}`}></i></span>
</div>
<span>Offerings</span>
<span>Testimonials</span>
</div>
{/* Right-aligned items */}
<div className="right-items">
<span> <RiStore2Line/> Pozo Store</span>
<span>Sign In</span>
</div>
</div>
</div>
<div className={`dropdown-content ${isDropdownOpen ? 'show' : ''}`}>
<div className="dropdown-inner-content">
<div className="dropdown-section explore-mac">
<h3>Explore Mac</h3>
<a href="#">Explore All Mac</a>
<a href="#">MacBook Air</a>
<a href="#">MacBook Pro</a>
<a href="#">iMac</a>
<a href="#">Mac mini</a>
<a href="#">Mac Studio</a>
<a href="#">Mac Pro</a>
<a href="#">Displays</a>
<a href="#" className="small-link">Compare Mac</a>
<a href="#" className="small-link">Switch from PC to Mac</a>
</div>
<div className="dropdown-section shop-mac">
<h3>Shop Mac</h3>
<a href="#">Shop Mac</a>
<a href="#">Mac Accessories</a>
<a href="#">Ways to Buy</a>
<a href="#">College Student Offer</a>
</div>
<div className="dropdown-section more-from-mac">
<h3>More from Mac</h3>
<a href="#">Mac Support</a>
<a href="#">AppleCare+ for Mac</a>
<a href="#">macOS Sequoia</a>
<a href="#">Apple Intelligence</a>
<a href="#">Apps by Apple</a>
<a href="#">Continuity</a>
<a href="#">iCloud+</a>
<a href="#">Mac for Business</a>
<a href="#">Education</a>
</div>
</div>
</div>
</>
);
};
export default NavBar;

308
src/HeroSection.css Normal file
View File

@ -0,0 +1,308 @@
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400&family=Roboto:wght@400;700&display=swap');
#root {
/* max-width: 1280px; */
width: 100%;
margin: 0 auto;
/* padding: 2rem; */
overflow: auto;
text-align: center;
}
.spline-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh; /* Full screen height */
z-index: 0; /* Push behind text */
pointer-events: none; /* So user can click UI elements above */
overflow: hidden;
}
.LandigMaster {
position: relative;
overflow: hidden;
height: 100vh;
width: 100%;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafb);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
.LandigMaster {
display: flex;
flex-direction: column;
justify-content: center;
min-height: 100vh;
position: relative;
overflow: hidden;
padding:3rem; /* Add padding to the left for content */
box-sizing: border-box;
}
.title-section {
z-index: 10;
text-align: center;
/* width: 100%; */
gap: 1rem;
font-family: "Inter";
box-sizing: border-box;
justify-content: center;
display: flex;
flex-direction: column;
align-items: center;
}
.title-section h1 {
font-size: 5em; /* Adjusted font size for prominence */
margin-bottom: 0; /* Reduced margin */
color: #1f1f1f;
font-weight: 500 !important; /* Lighter weight */
}
.title-section .head {
font-size: 5.5em; /* Adjusted font size */
line-height: 1.1; /* Adjusted line height */
letter-spacing: -7px !important;
color: #000000;
font-weight: 500; /* Bolder weight for GenAI */
}
.title-section p{
font-size: 1.2em; /* Adjusted font size */
line-height: 1.1; /* Adjusted line height */
letter-spacing: -0px !important;
color: #000000;
}
.subtitle {
font-size: 1.2em; /* Further reduced font size */
color: #555;
/* max-width: 600px; */
/* width: 100%; */
box-sizing: border-box;
/* text-align: left; */
z-index: 10;
}
.subtitle p:first-child {
font-size: 1em;
color: #000000;
}
.gradient-blur {
position: absolute;
top: 50%; /* Adjusted position */
left:50%; /* Adjusted position */
transform: translate(-50%, -50%);
width: 400px; /* Adjusted size */
height: 400px; /* Adjusted size */
background: radial-gradient(circle at 50% 50%, rgba(173, 216, 230, 0.5), rgba(255, 255, 255, 0));
border-radius: 50%;
filter: blur(50px); /* Adjusted blur */
opacity: 1; /* Adjusted opacity */
animation: floatEffect 5s ease-in-out infinite alternate;
z-index: 5;
}
@keyframes floatEffect {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
50% {
transform: translate(-45%, -55%) scale(1.02);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
}
/* Floral shape using multiple divs for better control */
.floral-shape { position: absolute;
top: 50%; /* Adjusted position to match gradient-blur */
left: 55%; /* Adjusted position to match gradient-blur */
transform: translate(-50%, -50%);
width: 400px; /* Adjusted size */
height: 400px; /* Adjusted size */
z-index: 6;
animation: rotateAndScale 10s linear infinite;
display: flex;
justify-content: center;
align-items: center;
opacity: 0.7; /* Make it visible and slightly transparent */
}
.floral-petal {
position: absolute;
width: 250px; /* Adjusted size of each petal */
height: 250px; /* Adjusted size of each petal */
background: rgba(170, 200, 255, 0.4); /* Adjusted transparency */
border-radius: 10%;
filter: blur(22px); /* Adjusted blur for definition */
opacity: 0.8; /* Make petals visible */
}
.floral-petal:nth-child(1) { transform: rotate(0deg) translate(80px) scale(0.9); }
.floral-petal:nth-child(2) { transform: rotate(60deg) translate(80px) scale(0.9); }
.floral-petal:nth-child(3) { transform: rotate(120deg) translate(80px) scale(0.9); }
.floral-petal:nth-child(4) { transform: rotate(180deg) translate(80px) scale(0.9); }
.floral-petal:nth-child(5) { transform: rotate(240deg) translate(80px) scale(0.9); }
.floral-petal:nth-child(6) { transform: rotate(300deg) translate(80px) scale(0.9); }
@keyframes rotateAndScale {
0% {
transform: translate(-50%, -50%) rotate(0deg) scale(1);
}
50% {
transform: translate(-50%, -50%) rotate(180deg) scale(1.05);
}
100% {
transform: translate(-50%, -50%) rotate(360deg) scale(1);
}
}
/* Add a subtle noise/grain effect */
.noise-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAAD9M+WBAAAABlBMVEUAAAAAAAClsPZIAAAAAXRSTlMAQObYZgAAABxJREFUeNpjYBgFo2AUjIJRMApGwSigAACgAAABJ8+cAAAAAElFTkSuQmCC');
background-size: 100px;
opacity: 0.05;
pointer-events: none;
z-index: 100;
}
/* From Uiverse.io by adamgiebl */
button {
font-family: inherit;
font-size: 20px;
background: #0066cc;
color: white;
padding: 0.7em 1em;
padding-left: 0.9em;
display: flex;
align-items: center;
border: none;
border-radius: 16px;
overflow: hidden;
transition: all 0.2s;
cursor: pointer;
backdrop-filter: blur(10px);
}
button span {
display: block;
margin-left: 0.3em;
transition: all 0.3s ease-in-out;
}
button svg {
display: block;
transform-origin: center center;
transition: transform 0.3s ease-in-out;
}
button:hover .svg-wrapper {
animation: fly-1 0.6s ease-in-out infinite alternate;
}
button:hover svg {
transform: translateX(1.9em) rotate(45deg) scale(1.1);
button{
width: 60px !important;
}
}
button:hover span {
transform: translateX(5em);
}
button:active {
transform: scale(0.95);
border: none;
outline: none;
}
@keyframes fly-1 {
from {
transform: translateY(0.1em);
}
to {
transform: translateY(-0.1em);
}
}
.blurry-button {
background-color: rgba(2, 102, 231, 0.808); /* Darker, semi-transparent background */
border: 1px solid rgba(16, 104, 219, 0.2); /* Subtle white border */
backdrop-filter: blur(10px); /* Blurry effect */
color: #fff; /* White text */
padding: 15px 30px;
border-radius: 30px;
font-size: 1em;
font-weight: 400;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 0.3em; /* Space from subtitle */
z-index: 10;
}
.blurry-button:hover {
background-color: rgba(16, 104, 219, 0.8); /* Slightly lighter on hover */
border-color: rgba(16, 104, 219, 0.4);
}

92
src/HeroSection.jsx Normal file
View File

@ -0,0 +1,92 @@
// import './HeroSection.css';
// function HeroSection() {
// return (
// <div className="LandigMaster">
// <div className="noise-overlay"></div>
// <div className="title-section">
// {/* <h1>Pozo</h1> */}
// <div className='head'>simplifies, automates, and scales with GenAI at its core.</div>
// </div>
// <div className="subtitle">
// <p>Here is where your presentation begins</p>
// </div>
// <button className="blurry-button">Create with Pozo</button>
// <div className="gradient-blur"></div>
// <div className="floral-shape">
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// <div className="floral-petal"></div>
// </div>
// </div>
// );
// }
// export default HeroSection;
import React, { useEffect } from 'react';
import './HeroSection.css';
import Spline from '@splinetool/react-spline';
function HeroSection() {
useEffect(() => {
const scriptId = 'spline-script';
if (!document.getElementById(scriptId)) {
const script = document.createElement('script');
script.src = 'https://unpkg.com/@splinetool/viewer@1.10.13/build/spline-viewer.js';
script.type = 'module';
script.async = true;
script.id = scriptId;
document.body.appendChild(script);
}
}, []);
return (
<div className="LandigMaster">
<div className="title-section">
<div className="head">
<span className="letters">Core-crafted intelligence</span>
</div>
<p>Lightweight interface. Heavyweight intelligence</p>
<button className="explore-button">
<div className="svg-wrapper">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path fill="none" d="M0 0h24v24H0z"></path>
<path
fill="currentColor"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-5.5-2.5l7.51-3.49L17.5 6.5 9.99 9.99 6.5 17.5zm5.5-6.6c.61 0 1.1.49 1.1 1.1s-.49 1.1-1.1 1.1-1.1-.49-1.1-1.1.49-1.1 1.1-1.1z"
></path>
</svg>
</div>
<span>Explore</span>
</button>
</div>
<div style={{ width: '100%', height: '100vh', position: 'absolute', left: '0' }}>
<spline-viewer url="https://prod.spline.design/hlzbpWnemLgYuOCg/scene.splinecode">
</spline-viewer>
</div>
</div>
);
}
export default HeroSection;

1
src/Images/PozoLogo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

1
src/Images/PozoLogo1.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,8 @@
<svg width="200" height="150" viewBox="0 0 200 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="180" height="130" rx="10" fill="#FFC107"/>
<path d="M30 40H170V120H30V40Z" fill="#FFECB3"/>
<path d="M50 60L70 80L50 100" stroke="#FFA000" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M150 60L130 80L150 100" stroke="#FFA000" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="80" y="70" width="40" height="20" fill="#FFA000"/>
<text x="100" y="25" font-family="Arial" font-size="12" fill="white" text-anchor="middle">Accounting</text>
</svg>

After

Width:  |  Height:  |  Size: 622 B

11
src/Images/billing.svg Normal file
View File

@ -0,0 +1,11 @@
<svg width="200" height="150" viewBox="0 0 200 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="180" height="130" rx="10" fill="#2196F3"/>
<path d="M30 40H170V120H30V40Z" fill="#BBDEFB"/>
<circle cx="50" cy="60" r="10" fill="#1976D2"/>
<rect x="70" y="55" width="80" height="10" fill="#1976D2"/>
<circle cx="50" cy="90" r="10" fill="#1976D2"/>
<rect x="70" y="85" width="80" height="10" fill="#1976D2"/>
<circle cx="50" cy="120" r="10" fill="#1976D2"/>
<rect x="70" y="115" width="80" height="10" fill="#1976D2"/>
<text x="100" y="25" font-family="Arial" font-size="12" fill="white" text-anchor="middle">Billing/POS</text>
</svg>

After

Width:  |  Height:  |  Size: 660 B

14
src/Images/inventory.svg Normal file
View File

@ -0,0 +1,14 @@
<svg width="200" height="150" viewBox="0 0 200 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="180" height="130" rx="10" fill="#4CAF50"/>
<path d="M30 40H170V120H30V40Z" fill="#E8F5E9"/>
<rect x="40" y="50" width="30" height="20" fill="#81C784"/>
<rect x="80" y="50" width="30" height="20" fill="#81C784"/>
<rect x="120" y="50" width="30" height="20" fill="#81C784"/>
<rect x="40" y="80" width="30" height="20" fill="#81C784"/>
<rect x="80" y="80" width="30" height="20" fill="#81C784"/>
<rect x="120" y="80" width="30" height="20" fill="#81C784"/>
<rect x="40" y="110" width="30" height="20" fill="#81C784"/>
<rect x="80" y="110" width="30" height="20" fill="#81C784"/>
<rect x="120" y="110" width="30" height="20" fill="#81C784"/>
<text x="100" y="25" font-family="Arial" font-size="12" fill="white" text-anchor="middle">Inventory</text>
</svg>

After

Width:  |  Height:  |  Size: 878 B

153
src/Pages/Offerings.css Normal file
View File

@ -0,0 +1,153 @@
.offerings-wrapper {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #000;
overflow: hidden;
font-family: 'DM Sans', sans-serif;
z-index: 6;
flex-direction: column;
display: flex;
}
.section-heading {
font-size: 1rem;
font-weight: 500;
color: #ffffff;
letter-spacing: -0px !important;
position: relative;
padding-bottom: 5rem;
top: 0;
z-index: 2;
text-align: center;
}
.background-fade {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
opacity: 0;
transform: scale(1.05);
transition: opacity 0.4s ease, transform 0.4s ease;
z-index: 0;
filter: blur(30px);
}
.background-fade.show {
opacity: 1;
transform: scale(1);
}
.offerings-list {
position: relative;
z-index: 2;
display: flex;
font-size:60px;
flex-direction: column;
align-items: center;
gap: 10px;
}
.offering-item {
position: relative;
cursor: pointer;
transition: opacity 0.3s ease;
}
.offering-item.dimmed {
opacity: 0.4;
}
.offering-title {
font-size: 110px;
font-weight: 420;
color: white;
line-height: 1;
font-family: "inter";
transition: transform 0.3s ease;
letter-spacing: -8px;
}
.hover-video {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 10px;
}
.background-video {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
filter: blur(30px);
opacity: 0;
transition: opacity 0.5s ease;
z-index: 0;
}
.background-fade.show .background-video {
opacity: 1;
}
.offerings-list {
position: relative;
z-index: 2;
}
.hover-preview {
position: absolute;
top: 50%;
transform: translateY(-50%) scale(0.7); /* Start smaller */
width: 320px;
height: 200px;
border-radius: 16px;
overflow: hidden;
z-index: 3;
opacity: 0;
pointer-events: none;
transition:
opacity 0.6s ease-out,
transform 0.6s cubic-bezier(0.22, 1.61, 0.36, 1),
box-shadow 0.6s ease-out;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
}
.hover-preview.visible,
.offering-item:hover .hover-preview {
opacity: 1;
transform: translateY(-50%) scale(1.2); /* Scale up beyond original size */
pointer-events: auto;
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.6);
}
.hover-preview img {
width: 100%;
height: 100%;
object-fit: cover;
transform: scale(0.8);
opacity: 0;
transition: opacity 0.4s ease, transform 0.4s ease;
}
.hover-preview.visible img {
transform: scale(1);
opacity: 1;
}
/* Direction classes */
.hover-preview.right {
left: calc(100% + 30px);
}
.hover-preview.left {
right: calc(100% + 30px);
}

187
src/Pages/Offerings.jsx Normal file
View File

@ -0,0 +1,187 @@
// import React, { useState } from 'react';
// import inventoryImage from '../Images/inventory.svg';
// import billingImage from '../Images/billing.svg';
// import accountingImage from '../Images/accounting.svg';
// import './Offerings.css';
// const offeringsData = [
// {
// id: 1,
// title: 'Inventory Control',
// image: inventoryImage,
// alt: 'Inventory/Warehouse Management',
// position: 'right',
// },
// {
// id: 2,
// title: 'Billing/POS',
// image: billingImage,
// alt: 'Billing/POS',
// position: 'left',
// },
// {
// id: 3,
// title: 'Accounting',
// image: accountingImage,
// alt: 'Accounting',
// position: 'right',
// },
// ];
// function Offerings() {
// const [hoveredId, setHoveredId] = useState(null);
// const hoveredOffering = offeringsData.find((item) => item.id === hoveredId);
// return (
// <div className="offerings-wrapper">
// {/* Background Image layer */}
// <div
// className={`background-fade ${hoveredOffering ? 'show' : ''}`}
// style={{
// backgroundImage: hoveredOffering ? `url(${hoveredOffering.image})` : 'none',
// }}
// ></div>
// <div className="offerings-list">
// {offeringsData.map((offering) => (
// <div
// key={offering.id}
// className={`offering-item ${
// hoveredId && hoveredId !== offering.id ? 'dimmed' : ''
// }`}
// onMouseEnter={() => setHoveredId(offering.id)}
// onMouseLeave={() => setHoveredId(null)}
// >
// <h2 className="offering-title">{offering.title}</h2>
// {/* Image popup */}
// <div
// className={`hover-preview ${hoveredId === offering.id ? 'visible' : ''}`}
// >
// <img src={offering.image} alt={offering.alt} />
// </div>
// </div>
// ))}
// </div>
// </div>
// );
// }
// export default Offerings;
import React, { useState } from 'react';
import inventoryImage from '../Images/inventory.svg';
import billingImage from '../Images/billing.svg';
import accountingImage from '../Images/accounting.svg';
import Billing from "../Videos/billing.mp4"
import inventory from "../Videos/inventory.mp4"
import './Offerings.css';
const offeringsData = [
{
id: 1,
title: 'Billing/POS',
image: Billing,
type: 'video',
alt: ' Billing/POS',
position: 'right',
},
{
id: 2,
title: 'Accounting',
image: 'https://img.freepik.com/premium-photo/comprehensive-business-financial-analysis_1057472-196.jpg?uid=R2740650&ga=GA1.1.1997829733.1728553786&semt=ais_items_boosted&w=740',
alt: 'Accounting',
position: 'left',
},
{
id: 3,
image: inventory,
title: ' Inventory Control',
position: 'right',
type: 'video',
alt: 'Inventory/Warehouse Management',
},
];
function Offerings() {
const [hoveredId, setHoveredId] = useState(null);
const hoveredOffering = offeringsData.find((item) => item.id === hoveredId);
return (
<>
<div className="offerings-wrapper">
<div className="section-heading">See Pozo Offerings</div>
<div
className={`background-fade ${hoveredOffering ? 'show' : ''}`}
style={{
backgroundImage:
hoveredOffering?.type !== 'video'
? `url(${hoveredOffering?.image})`
: 'none',
}}
>
{hoveredOffering?.type === 'video' && (
<video
src={hoveredOffering.image}
autoPlay
muted
loop
playsInline
className="background-video"
/>
)}
</div>
<div className="offerings-list">
{offeringsData.map((offering) => (
<div
key={offering.id}
className={`offering-item ${hoveredId && hoveredId !== offering.id ? 'dimmed' : ''}`}
onMouseEnter={() => setHoveredId(offering.id)}
onMouseLeave={() => setHoveredId(null)}
>
<div className="offering-title">{offering.title}</div>
{hoveredId === offering.id && (
<div className={`hover-preview ${offering.position} ${hoveredId === offering.id ? 'visible' : ''}`}>
{offering.type === 'video' ? (
<video
src={offering.image}
autoPlay
muted
loop
playsInline
className="hover-video"
/>
) : (
<img src={offering.image} alt={offering.alt} />
)}
</div>
)}
</div>
))}
</div>
</div>
</>
);
}
export default Offerings;

BIN
src/Videos/billing.mp4 Normal file

Binary file not shown.

BIN
src/Videos/billing2.mp4 Normal file

Binary file not shown.

BIN
src/Videos/inventory.mp4 Normal file

Binary file not shown.

1
src/assets/react.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

276
src/index.css Normal file
View File

@ -0,0 +1,276 @@
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: 1.4;
font-weight: 300;
color: #333;
}
h1, h2 {
font-weight: 400;
margin-bottom: 0;
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Code styling */
code .str, pre .str {
color: #0ae448 !important;
}
.panel pre.prettyprint {
font-size: 20px;
text-align: left;
width: auto;
font-weight: normal;
margin: 10px;
border: none;
background-color: #28292b;
color: white;
padding: 20px;
border-radius: 8px;
overflow: auto;
}
.prettyprint .linenums {
padding: 0;
list-style: none;
}
.prettyprint ol li {
background-color: #28292b;
}
.panel.red .prettyprint .linenums > li:nth-child(n+7):nth-child(-n+9),
.panel.gray .prettyprint .linenums > li:nth-child(10),
.panel.purple .prettyprint .linenums > li:nth-child(4),
.panel.green .prettyprint .linenums > li:nth-child(n+6):nth-child(-n+10) {
background-color: #222;
}
/* Box styling */
.box {
width: 100px;
height: 80px;
text-align: center;
line-height: 80px;
background-color: white;
border-radius: 8px;
color: #222;
margin-left: 20px;
will-change: transform;
font-weight: 500;
}
.box.active {
background-color: orange !important;
border: 2px solid white !important;
transform: scale(1.1) !important;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3) !important;
}
/* Layout sections */
.description, .final {
display: flex;
justify-content: center;
align-items: center;
padding: 40px;
min-height: 80vh;
background-color: #f5f5f5;
}
.description {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
}
.description h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.description p {
font-size: 1.2rem;
max-width: 600px;
line-height: 1.6;
}
.description button {
padding: 12px 24px;
background-color: #007bff;
color: white;
border: none;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.3);
}
.description button:hover {
background-color: #0056b3;
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 123, 255, 0.4);
}
.scroll-down {
margin-top: 3rem;
font-size: 1.1rem;
opacity: 0.8;
}
.arrow {
font-size: 2rem;
margin-top: 0.5rem;
animation: bounce 2s infinite;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
60% {
transform: translateY(-5px);
}
}
.arrow::after {
content: '↓';
}
/* Container and panels */
.container {
width: 500%;
height: 100vh;
display: flex;
flex-wrap: nowrap;
}
.panel {
width: 20%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-around;
color: white;
font-weight: 300;
padding: 40px;
font-size: 18px;
}
.panel.blue {
background-color: #4285f4;
justify-content: center;
font-size: 24px;
}
.panel.red {
background-color: #ea4335;
}
.panel.gray {
background-color: #666666;
}
.panel.purple {
background-color: #9c27b0;
}
.panel.green {
background-color: #34a853;
}
.panel > div:first-child {
flex: 1;
max-width: 500px;
}
.panel pre {
margin-bottom: 20px;
}
/* Final section */
.final {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
color: #333;
}
.final h1 {
font-size: 2.5rem;
margin-bottom: 1.5rem;
color: #2c3e50;
}
.final p {
font-size: 1.2rem;
margin-bottom: 2rem;
}
.final ul {
text-align: left;
max-width: 800px;
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.final li {
margin-bottom: 1rem;
line-height: 1.6;
}
.final code {
background-color: #f8f9fa;
color: #e83e8c;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.9em;
font-family: 'SF Mono', Monaco, 'Inconsolata', 'Roboto Mono', Consolas, monospace;
}
/* Responsive design */
@media (max-width: 768px) {
.panel {
flex-direction: column;
padding: 20px;
font-size: 16px;
}
.panel pre {
font-size: 14px;
padding: 15px;
}
.box {
margin: 20px 0 0 0;
}
.description, .final {
padding: 20px;
}
.description h1 {
font-size: 2rem;
}
.final h1 {
font-size: 2rem;
}
}

10
src/main.jsx Normal file
View File

@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)

View File

@ -0,0 +1,240 @@
export const styles = {
container: {
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", sans-serif',
fontSize: '16px',
lineHeight: '1.6',
fontWeight: 300,
color: '#333',
margin: 0,
padding: 0
},
heroSection: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '60px 40px',
minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
textAlign: 'center'
},
heroContent: {
maxWidth: '800px',
width: '100%'
},
heroTitle: {
fontSize: '3.5rem',
fontWeight: 700,
marginBottom: '1.5rem',
letterSpacing: '-0.02em',
background: 'linear-gradient(45deg, #fff, #e0e7ff)',
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent'
},
heroDescription: {
fontSize: '1.25rem',
marginBottom: '1rem',
opacity: 0.9,
lineHeight: '1.7'
},
heroSubtext: {
fontSize: '1rem',
marginBottom: '2.5rem',
opacity: 0.8
},
scrollButton: {
padding: '16px 32px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '50px',
cursor: 'pointer',
fontSize: '18px',
fontWeight: 600,
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
boxShadow: '0 8px 32px rgba(0, 123, 255, 0.3)',
outline: 'none'
},
scrollIndicator: {
marginTop: '4rem',
opacity: 0.7
},
scrollText: {
fontSize: '1rem',
marginBottom: '0.5rem'
},
scrollArrow: {
fontSize: '2rem',
animation: 'bounce 2s infinite'
},
scrollContainer: {
width: '500%',
height: '100vh',
display: 'flex',
flexWrap: 'nowrap'
},
section: {
width: '20%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
position: 'relative'
},
sectionContent: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '60px',
width: '100%',
maxWidth: '1000px',
gap: '40px'
},
contentWrapper: {
flex: 1,
maxWidth: '600px'
},
sectionTitle: {
fontSize: '2.5rem',
fontWeight: 600,
marginBottom: '1rem',
textAlign: 'center'
},
sectionText: {
fontSize: '1.5rem',
textAlign: 'center',
opacity: 0.9
},
codeBlock: {
backgroundColor: '#1a1a1a',
color: '#e6e6e6',
padding: '24px',
borderRadius: '12px',
fontSize: '14px',
lineHeight: '1.5',
marginBottom: '20px',
fontFamily: '"SF Mono", Monaco, "Inconsolata", "Roboto Mono", Consolas, monospace',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
border: '1px solid rgba(255, 255, 255, 0.1)',
overflow: 'auto'
},
sectionDescription: {
fontSize: '1.1rem',
opacity: 0.9,
lineHeight: '1.6'
},
animatedBox: {
width: '120px',
height: '100px',
backgroundColor: 'white',
color: '#333',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '12px',
fontWeight: 600,
fontSize: '16px',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.2)',
willChange: 'transform',
cursor: 'pointer'
},
boxLabel: {
textAlign: 'center'
},
footerSection: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '80px 40px',
minHeight: '100vh',
background: 'linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)',
color: '#333'
},
footerContent: {
maxWidth: '900px',
width: '100%',
textAlign: 'center'
},
footerTitle: {
fontSize: '2.5rem',
fontWeight: 700,
marginBottom: '1.5rem',
color: '#2c3e50'
},
footerText: {
fontSize: '1.25rem',
marginBottom: '3rem',
opacity: 0.8,
lineHeight: '1.7'
},
caveatsSection: {
textAlign: 'left',
backgroundColor: 'white',
padding: '40px',
borderRadius: '16px',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.1)',
border: '1px solid rgba(0, 0, 0, 0.05)'
},
caveatsTitle: {
fontSize: '1.5rem',
fontWeight: 600,
marginBottom: '1.5rem',
color: '#2c3e50'
},
caveatsList: {
listStyle: 'none',
padding: 0
},
caveatItem: {
fontSize: '1rem',
marginBottom: '1rem',
lineHeight: '1.6',
paddingLeft: '1.5rem',
position: 'relative',
'&::before': {
content: '•',
color: '#007bff',
fontWeight: 'bold',
position: 'absolute',
left: 0
}
},
inlineCode: {
backgroundColor: '#f8f9fa',
color: '#e83e8c',
padding: '2px 6px',
borderRadius: '4px',
fontSize: '0.9em',
fontFamily: '"SF Mono", Monaco, "Inconsolata", "Roboto Mono", Consolas, monospace'
}
};

168
src/utils/scrollManager.js Normal file
View File

@ -0,0 +1,168 @@
export class ScrollManager {
constructor() {
this.scrollTween = null;
this.redTween = null;
this.isGSAPLoaded = false;
}
async loadGSAP() {
if (typeof window !== 'undefined' && !window.gsap) {
const scripts = [
{
src: 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js',
name: 'gsap'
},
{
src: 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js',
name: 'ScrollTrigger'
},
{
src: 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollToPlugin.min.js',
name: 'ScrollToPlugin'
}
];
await this.loadScripts(scripts);
// Wait for GSAP to be fully available
await new Promise(resolve => setTimeout(resolve, 100));
}
if (window.gsap && window.gsap.registerPlugin) {
window.gsap.registerPlugin(window.ScrollTrigger, window.ScrollToPlugin);
this.isGSAPLoaded = true;
}
}
loadScripts(scripts) {
return Promise.all(
scripts.map(script => {
return new Promise((resolve, reject) => {
const scriptElement = document.createElement('script');
scriptElement.src = script.src;
scriptElement.onload = resolve;
scriptElement.onerror = reject;
document.head.appendChild(scriptElement);
});
})
);
}
createHorizontalScroll(container, sections) {
if (!this.isGSAPLoaded || !window.gsap) return;
const { gsap } = window;
// Create main horizontal scroll tween
this.scrollTween = gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none",
scrollTrigger: {
trigger: container,
pin: true,
scrub: 0.1,
end: "+=3000",
anticipatePin: 1
}
});
// Set initial positions for animated boxes
gsap.set(".box-1, .box-2", { y: 100 });
}
setupSectionAnimations() {
if (!this.isGSAPLoaded || !window.gsap || !this.scrollTween) return;
const { gsap } = window;
// Red section animation with elastic effect
this.redTween = gsap.to(".box-1", {
y: -130,
duration: 2,
ease: "elastic.out(1, 0.3)",
scrollTrigger: {
trigger: ".box-1",
containerAnimation: this.scrollTween,
start: "left center",
toggleActions: "play none none reset"
}
});
// Gray section scrub animation
gsap.to(".box-2", {
y: -120,
backgroundColor: "#1e90ff",
ease: "none",
scrollTrigger: {
trigger: ".box-2",
containerAnimation: this.scrollTween,
start: "center 80%",
end: "center 20%",
scrub: true
}
});
// Purple section class toggle
window.ScrollTrigger.create({
trigger: ".box-3",
containerAnimation: this.scrollTween,
toggleClass: "active",
start: "center 60%"
});
// Green section callbacks
window.ScrollTrigger.create({
trigger: ".green",
containerAnimation: this.scrollTween,
start: "center 65%",
end: "center 51%",
onEnter: () => console.log("🟢 Green section entered"),
onLeave: () => console.log("🔴 Green section left"),
onEnterBack: () => console.log("⬅️ Green section entered back"),
onLeaveBack: () => console.log("➡️ Green section left back"),
onToggle: self => console.log("🔄 Green section active:", self.isActive)
});
}
getScrollPosition(animation, progress = 0) {
if (!window.gsap || !animation) return 0;
const { gsap } = window;
const p = gsap.utils.clamp(0, 1, progress);
const nested = !animation.scrollTrigger;
const st = nested ? animation.parent.scrollTrigger : animation.scrollTrigger;
const containerAnimation = st.vars.containerAnimation;
const range = st.end - st.start;
const position = st.start + range * p;
if (containerAnimation) {
const containerST = containerAnimation.scrollTrigger;
return containerST.start + (containerST.end - containerST.start) * (position / containerAnimation.duration());
} else if (nested) {
const start = st.start + (animation.startTime() / animation.parent.duration()) * range;
const end = st.start + ((animation.startTime() + animation.duration()) / animation.parent.duration()) * range;
return start + (end - start) * p;
}
return position;
}
scrollToRedTween() {
if (!window.gsap || !this.redTween) return;
const scrollPosition = this.getScrollPosition(this.redTween);
window.gsap.to(window, {
scrollTo: scrollPosition,
duration: 1.5,
ease: "power2.inOut"
});
}
cleanup() {
if (window.ScrollTrigger) {
window.ScrollTrigger.getAll().forEach(trigger => trigger.kill());
}
this.scrollTween = null;
this.redTween = null;
}
}

10
vite.config.js Normal file
View File

@ -0,0 +1,10 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
host: true,
},
})