cinematic-web 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/assets/CLAUDE.md +150 -0
  3. package/assets/presets/3d-immersive/README.md +30 -0
  4. package/assets/presets/3d-immersive/preset.json +45 -0
  5. package/assets/presets/antigravity-lift/README.md +24 -0
  6. package/assets/presets/antigravity-lift/preset.json +38 -0
  7. package/assets/presets/brutalist-signal/README.md +24 -0
  8. package/assets/presets/brutalist-signal/preset.json +36 -0
  9. package/assets/presets/midnight-luxe/README.md +24 -0
  10. package/assets/presets/midnight-luxe/preset.json +36 -0
  11. package/assets/presets/organic-tech/README.md +24 -0
  12. package/assets/presets/organic-tech/preset.json +37 -0
  13. package/assets/presets/vapor-clinic/README.md +24 -0
  14. package/assets/presets/vapor-clinic/preset.json +36 -0
  15. package/assets/prompts/product-development/Guided-MVP-Concept.md +67 -0
  16. package/assets/prompts/product-development/Guided-MVP.md +65 -0
  17. package/assets/prompts/product-development/Guided-PRD-Creation.md +51 -0
  18. package/assets/prompts/product-development/Guided-Test-Plan.md +57 -0
  19. package/assets/prompts/product-development/Guided-UX-User-Flow.md +93 -0
  20. package/assets/prompts/product-development/README.md +21 -0
  21. package/assets/prompts/product-development/v0-design-prompt.md +107 -0
  22. package/assets/templates/base-react/index.html +18 -0
  23. package/assets/templates/base-react/package.json +26 -0
  24. package/assets/templates/base-react/postcss.config.js +6 -0
  25. package/assets/templates/base-react/src/App.jsx +33 -0
  26. package/assets/templates/base-react/src/index.css +90 -0
  27. package/assets/templates/base-react/src/main.jsx +10 -0
  28. package/assets/templates/base-react/src/sections/Features.jsx +238 -0
  29. package/assets/templates/base-react/src/sections/Footer.jsx +120 -0
  30. package/assets/templates/base-react/src/sections/Hero.jsx +96 -0
  31. package/assets/templates/base-react/src/sections/Navbar.jsx +119 -0
  32. package/assets/templates/base-react/src/sections/Philosophy.jsx +67 -0
  33. package/assets/templates/base-react/src/sections/Pricing.jsx +135 -0
  34. package/assets/templates/base-react/src/sections/Protocol.jsx +123 -0
  35. package/assets/templates/base-react/tailwind.config.js +26 -0
  36. package/assets/templates/base-react/vite.config.js +6 -0
  37. package/assets/templates/three-fiber/eslint.config.js +21 -0
  38. package/assets/templates/three-fiber/index.html +16 -0
  39. package/assets/templates/three-fiber/package.json +36 -0
  40. package/assets/templates/three-fiber/postcss.config.js +6 -0
  41. package/assets/templates/three-fiber/src/App.jsx +61 -0
  42. package/assets/templates/three-fiber/src/components/CameraRig.jsx +42 -0
  43. package/assets/templates/three-fiber/src/components/NetworkGraph.jsx +120 -0
  44. package/assets/templates/three-fiber/src/components/ParticleSystem.jsx +77 -0
  45. package/assets/templates/three-fiber/src/components/Scene.jsx +39 -0
  46. package/assets/templates/three-fiber/src/components/SocialProofBillboards.jsx +56 -0
  47. package/assets/templates/three-fiber/src/context/SceneContext.jsx +21 -0
  48. package/assets/templates/three-fiber/src/index.css +37 -0
  49. package/assets/templates/three-fiber/src/main.jsx +21 -0
  50. package/assets/templates/three-fiber/src/sections/CTA.jsx +41 -0
  51. package/assets/templates/three-fiber/src/sections/Hero.jsx +66 -0
  52. package/assets/templates/three-fiber/src/sections/HowItWorks.jsx +55 -0
  53. package/assets/templates/three-fiber/src/sections/Navbar.jsx +40 -0
  54. package/assets/templates/three-fiber/src/sections/SocialProof.jsx +50 -0
  55. package/assets/templates/three-fiber/src/sections/TheOldWay.jsx +28 -0
  56. package/assets/templates/three-fiber/src/sections/ValueProps.jsx +50 -0
  57. package/assets/templates/three-fiber/tailwind.config.js +21 -0
  58. package/assets/templates/three-fiber/vite.config.js +7 -0
  59. package/dist/cli.js +539 -0
  60. package/dist/cli.js.map +1 -0
  61. package/package.json +44 -0
@@ -0,0 +1,238 @@
1
+ import { useEffect, useRef, useState } from 'react'
2
+ import { gsap } from 'gsap'
3
+ import { ScrollTrigger } from 'gsap/ScrollTrigger'
4
+
5
+ // ─── Card 1: Diagnostic Shuffler ──────────────────────────────────────────────
6
+ function DiagnosticShuffler({ title, description }) {
7
+ const ITEMS = [
8
+ { label: 'Analyzing', value: '98.7%', color: 'var(--color-accent)' },
9
+ { label: 'Processing', value: '< 2ms', color: 'var(--color-primary)' },
10
+ { label: 'Verified', value: '✓ Pass', color: '#22c55e' },
11
+ ]
12
+ const [active, setActive] = useState(0)
13
+
14
+ useEffect(() => {
15
+ const id = setInterval(() => setActive((a) => (a + 1) % ITEMS.length), 2200)
16
+ return () => clearInterval(id)
17
+ }, [])
18
+
19
+ return (
20
+ <div className="relative h-48 flex flex-col justify-end mb-6">
21
+ {ITEMS.map((item, i) => {
22
+ const offset = i - active
23
+ const isVisible = Math.abs(offset) <= 1
24
+ return (
25
+ <div
26
+ key={item.label}
27
+ className="absolute w-full transition-all duration-700"
28
+ style={{
29
+ transform: `translateY(${offset * 52}px) scale(${i === active ? 1 : 0.94})`,
30
+ opacity: i === active ? 1 : isVisible ? 0.45 : 0,
31
+ zIndex: i === active ? 10 : 5 - Math.abs(offset),
32
+ }}
33
+ >
34
+ <div
35
+ className="flex items-center justify-between px-5 py-3 rounded-2xl font-data text-sm"
36
+ style={{
37
+ background: i === active ? 'var(--color-dark)' : 'var(--color-primary)/10',
38
+ border: `1px solid ${i === active ? item.color : 'transparent'}`,
39
+ }}
40
+ >
41
+ <span className="text-white/60">{item.label}</span>
42
+ <span style={{ color: item.color }} className="font-bold">{item.value}</span>
43
+ </div>
44
+ </div>
45
+ )
46
+ })}
47
+ </div>
48
+ )
49
+ }
50
+
51
+ // ─── Card 2: Telemetry Typewriter ──────────────────────────────────────────────
52
+ const TELEMETRY_LINES = [
53
+ '> Initializing protocol...',
54
+ '> Data stream: active',
55
+ '> Running analysis...',
56
+ '> Signal strength: 99.2%',
57
+ '> All systems nominal',
58
+ '> Ready.',
59
+ ]
60
+
61
+ function TelemetryTypewriter({ title, description }) {
62
+ const [lines, setLines] = useState([])
63
+ const [charIdx, setCharIdx] = useState(0)
64
+ const [lineIdx, setLineIdx] = useState(0)
65
+ const [showCursor, setShowCursor] = useState(true)
66
+
67
+ useEffect(() => {
68
+ const cursorId = setInterval(() => setShowCursor((c) => !c), 530)
69
+ return () => clearInterval(cursorId)
70
+ }, [])
71
+
72
+ useEffect(() => {
73
+ if (lineIdx >= TELEMETRY_LINES.length) return
74
+ const current = TELEMETRY_LINES[lineIdx] ?? ''
75
+ if (charIdx < current.length) {
76
+ const id = setTimeout(() => setCharIdx((c) => c + 1), 42)
77
+ return () => clearTimeout(id)
78
+ } else {
79
+ const id = setTimeout(() => {
80
+ setLines((l) => [...l, current])
81
+ setCharIdx(0)
82
+ setLineIdx((l) => l + 1)
83
+ }, 400)
84
+ return () => clearTimeout(id)
85
+ }
86
+ }, [charIdx, lineIdx])
87
+
88
+ const currentLine = TELEMETRY_LINES[lineIdx] ?? ''
89
+
90
+ return (
91
+ <div
92
+ className="h-48 overflow-hidden rounded-2xl p-4 mb-6 font-data text-xs leading-6"
93
+ style={{ background: 'var(--color-dark)' }}
94
+ >
95
+ {lines.map((l, i) => (
96
+ <div key={i} className="text-white/40">{l}</div>
97
+ ))}
98
+ {lineIdx < TELEMETRY_LINES.length && (
99
+ <div className="text-white/90">
100
+ {currentLine.slice(0, charIdx)}
101
+ <span
102
+ className="inline-block w-1.5 h-4 ml-0.5 align-middle"
103
+ style={{
104
+ background: 'var(--color-accent)',
105
+ opacity: showCursor ? 1 : 0,
106
+ }}
107
+ />
108
+ </div>
109
+ )}
110
+ </div>
111
+ )
112
+ }
113
+
114
+ // ─── Card 3: Cursor Protocol Scheduler ────────────────────────────────────────
115
+ const DAYS = ['M', 'T', 'W', 'T', 'F', 'S', 'S']
116
+ const HOURS = ['9', '11', '13', '15', '17']
117
+
118
+ function ProtocolScheduler({ title, description }) {
119
+ const [cursor, setCursor] = useState({ col: 0, row: 0 })
120
+
121
+ useEffect(() => {
122
+ const id = setInterval(() => {
123
+ setCursor({
124
+ col: Math.floor(Math.random() * DAYS.length),
125
+ row: Math.floor(Math.random() * HOURS.length),
126
+ })
127
+ }, 1800)
128
+ return () => clearInterval(id)
129
+ }, [])
130
+
131
+ return (
132
+ <div className="h-48 mb-6 flex flex-col justify-center">
133
+ <div className="grid gap-1" style={{ gridTemplateColumns: `repeat(${DAYS.length}, 1fr)` }}>
134
+ {DAYS.map((d, ci) => (
135
+ <div key={ci} className="font-data text-xs text-center text-white/30 pb-1">{d}</div>
136
+ ))}
137
+ {HOURS.flatMap((h, ri) =>
138
+ DAYS.map((_, ci) => {
139
+ const isActive = cursor.col === ci && cursor.row === ri
140
+ return (
141
+ <div
142
+ key={`${ri}-${ci}`}
143
+ className="rounded-md h-6 transition-all duration-500"
144
+ style={{
145
+ background: isActive
146
+ ? 'var(--color-accent)'
147
+ : (ri + ci) % 3 === 0
148
+ ? 'var(--color-primary)/30'
149
+ : 'var(--color-dark)/30',
150
+ transform: isActive ? 'scale(1.1)' : 'scale(1)',
151
+ boxShadow: isActive ? '0 0 12px var(--color-accent)' : 'none',
152
+ }}
153
+ />
154
+ )
155
+ })
156
+ )}
157
+ </div>
158
+ </div>
159
+ )
160
+ }
161
+
162
+ // ─── Main Features Section ─────────────────────────────────────────────────────
163
+ const CARD_COMPONENTS = [DiagnosticShuffler, TelemetryTypewriter, ProtocolScheduler]
164
+
165
+ export default function Features() {
166
+ const sectionRef = useRef(null)
167
+ const cardsRef = useRef([])
168
+
169
+ const VALUE_PROPS = [
170
+ { title: '{{valueProp0}}', description: 'Precision diagnostics that surface signal from noise, delivering actionable insights in real time.' },
171
+ { title: '{{valueProp1}}', description: 'Live telemetry stream keeping you informed at every step with zero latency.' },
172
+ { title: '{{valueProp2}}', description: 'Intelligent scheduling that optimizes your workflow and eliminates friction.' },
173
+ ]
174
+
175
+ useEffect(() => {
176
+ const ctx = gsap.context(() => {
177
+ cardsRef.current.forEach((card, i) => {
178
+ gsap.from(card, {
179
+ y: 50,
180
+ opacity: 0,
181
+ duration: 0.9,
182
+ ease: 'power3.out',
183
+ delay: i * 0.15,
184
+ scrollTrigger: {
185
+ trigger: card,
186
+ start: 'top 80%',
187
+ toggleActions: 'play none none none',
188
+ },
189
+ })
190
+ })
191
+ }, sectionRef)
192
+ return () => ctx.revert()
193
+ }, [])
194
+
195
+ return (
196
+ <section
197
+ ref={sectionRef}
198
+ id="features"
199
+ className="py-24 md:py-32 px-8 md:px-16 lg:px-24"
200
+ style={{ background: 'var(--color-background)' }}
201
+ >
202
+ <div className="max-w-6xl mx-auto">
203
+ <p className="font-data text-xs tracking-widest uppercase mb-4" style={{ color: 'var(--color-accent)' }}>
204
+ Capabilities
205
+ </p>
206
+ <h2 className="font-heading font-bold text-4xl md:text-5xl mb-16 tracking-tight" style={{ color: 'var(--color-dark)' }}>
207
+ Built for precision.
208
+ </h2>
209
+
210
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
211
+ {VALUE_PROPS.map((prop, i) => {
212
+ const CardWidget = CARD_COMPONENTS[i]
213
+ return (
214
+ <div
215
+ key={i}
216
+ ref={(el) => { cardsRef.current[i] = el }}
217
+ className="rounded-3xl p-6 flex flex-col"
218
+ style={{
219
+ background: i === 1 ? 'var(--color-primary)' : 'var(--color-dark)',
220
+ }}
221
+ >
222
+ <CardWidget title={prop.title} description={prop.description} />
223
+ <div className="mt-auto">
224
+ <h3 className="font-heading font-bold text-lg text-white mb-2 tracking-tight">
225
+ {prop.title}
226
+ </h3>
227
+ <p className="font-heading text-sm text-white/50 leading-relaxed">
228
+ {prop.description}
229
+ </p>
230
+ </div>
231
+ </div>
232
+ )
233
+ })}
234
+ </div>
235
+ </div>
236
+ </section>
237
+ )
238
+ }
@@ -0,0 +1,120 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { gsap } from 'gsap'
3
+ import { ScrollTrigger } from 'gsap/ScrollTrigger'
4
+
5
+ const FOOTER_LINKS = {
6
+ Product: ['Features', 'Protocol', 'Pricing', 'API'],
7
+ Company: ['About', 'Blog', 'Careers', 'Press'],
8
+ Legal: ['Privacy', 'Terms', 'Security', 'Cookies'],
9
+ }
10
+
11
+ export default function Footer() {
12
+ const footerRef = useRef(null)
13
+ const dotRef = useRef(null)
14
+
15
+ useEffect(() => {
16
+ const ctx = gsap.context(() => {
17
+ // Pulsing status dot
18
+ gsap.to(dotRef.current, {
19
+ opacity: 0.3,
20
+ duration: 1.2,
21
+ ease: 'power2.inOut',
22
+ repeat: -1,
23
+ yoyo: true,
24
+ })
25
+
26
+ gsap.from(footerRef.current, {
27
+ opacity: 0,
28
+ y: 30,
29
+ duration: 1,
30
+ ease: 'power3.out',
31
+ scrollTrigger: {
32
+ trigger: footerRef.current,
33
+ start: 'top 90%',
34
+ toggleActions: 'play none none none',
35
+ },
36
+ })
37
+ }, footerRef)
38
+
39
+ return () => ctx.revert()
40
+ }, [])
41
+
42
+ return (
43
+ <footer
44
+ ref={footerRef}
45
+ className="rounded-t-[4rem] px-8 md:px-16 lg:px-24 pt-16 pb-12"
46
+ style={{ background: 'var(--color-dark)' }}
47
+ >
48
+ <div className="max-w-6xl mx-auto">
49
+ {/* Top row */}
50
+ <div className="flex flex-col md:flex-row justify-between items-start gap-12 mb-16">
51
+ {/* Brand */}
52
+ <div className="max-w-xs">
53
+ <p className="font-heading font-bold text-white text-xl mb-3 tracking-tight">
54
+ {{brand}}
55
+ </p>
56
+ <p className="font-heading text-sm text-white/40 leading-relaxed">
57
+ {{purpose}}
58
+ </p>
59
+
60
+ {/* System status */}
61
+ <div className="flex items-center gap-2 mt-6">
62
+ <span
63
+ ref={dotRef}
64
+ className="w-2 h-2 rounded-full"
65
+ style={{ background: '#22c55e' }}
66
+ />
67
+ <span className="font-data text-xs text-white/40 tracking-wide">
68
+ System Operational
69
+ </span>
70
+ </div>
71
+ </div>
72
+
73
+ {/* Link columns */}
74
+ <div className="grid grid-cols-3 gap-8 md:gap-16">
75
+ {Object.entries(FOOTER_LINKS).map(([group, links]) => (
76
+ <div key={group}>
77
+ <p className="font-data text-xs tracking-widest uppercase text-white/30 mb-4">
78
+ {group}
79
+ </p>
80
+ <ul className="space-y-2.5">
81
+ {links.map((link) => (
82
+ <li key={link}>
83
+ <a
84
+ href="#"
85
+ className="font-heading text-sm text-white/50 hover:text-white interactive-lift transition-colors duration-200"
86
+ >
87
+ {link}
88
+ </a>
89
+ </li>
90
+ ))}
91
+ </ul>
92
+ </div>
93
+ ))}
94
+ </div>
95
+ </div>
96
+
97
+ {/* Divider */}
98
+ <div className="h-px mb-8" style={{ background: 'rgba(255,255,255,0.06)' }} />
99
+
100
+ {/* Bottom row */}
101
+ <div className="flex flex-col md:flex-row justify-between items-center gap-4">
102
+ <p className="font-data text-xs text-white/25">
103
+ © {new Date().getFullYear()} {{brand}}. All rights reserved.
104
+ </p>
105
+ <p className="font-data text-xs text-white/20">
106
+ Built with{' '}
107
+ <a
108
+ href="https://github.com/pushkar-verma/cinematic-web"
109
+ className="text-white/40 hover:text-white/60 interactive-lift transition-colors duration-200"
110
+ target="_blank"
111
+ rel="noopener noreferrer"
112
+ >
113
+ cinematic-web
114
+ </a>
115
+ </p>
116
+ </div>
117
+ </div>
118
+ </footer>
119
+ )
120
+ }
@@ -0,0 +1,96 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { gsap } from 'gsap'
3
+
4
+ // Unsplash image matching preset imageMood
5
+ const HERO_IMAGE = 'https://images.unsplash.com/photo-{{unsplashPhotoId}}?auto=format&fit=crop&w=1920&q=80'
6
+
7
+ export default function Hero() {
8
+ const containerRef = useRef(null)
9
+ const line1Ref = useRef(null)
10
+ const line2Ref = useRef(null)
11
+ const subtitleRef = useRef(null)
12
+ const ctaRef = useRef(null)
13
+
14
+ useEffect(() => {
15
+ const ctx = gsap.context(() => {
16
+ const tl = gsap.timeline({ defaults: { ease: 'power3.out' } })
17
+
18
+ tl.from(line1Ref.current, { y: 40, opacity: 0, duration: 1 })
19
+ .from(line2Ref.current, { y: 40, opacity: 0, duration: 1.1 }, '-=0.6')
20
+ .from(subtitleRef.current, { y: 30, opacity: 0, duration: 0.9 }, '-=0.5')
21
+ .from(ctaRef.current, { y: 20, opacity: 0, duration: 0.8 }, '-=0.4')
22
+ }, containerRef)
23
+
24
+ return () => ctx.revert()
25
+ }, [])
26
+
27
+ return (
28
+ <section
29
+ ref={containerRef}
30
+ id="hero"
31
+ className="relative w-full"
32
+ style={{ height: '100dvh' }}
33
+ >
34
+ {/* Background image with gradient overlay */}
35
+ <div
36
+ className="absolute inset-0 bg-cover bg-center"
37
+ style={{ backgroundImage: `url('${HERO_IMAGE}')` }}
38
+ aria-hidden="true"
39
+ />
40
+ <div
41
+ className="absolute inset-0"
42
+ style={{
43
+ background: 'linear-gradient(to top, var(--color-primary) 0%, var(--color-dark) 30%, transparent 70%)',
44
+ opacity: 0.85,
45
+ }}
46
+ aria-hidden="true"
47
+ />
48
+
49
+ {/* Content — bottom-left third */}
50
+ <div className="relative z-10 flex flex-col justify-end h-full px-8 md:px-16 lg:px-24 pb-16 md:pb-24 max-w-5xl">
51
+ {/* Line 1: bold sans */}
52
+ <p
53
+ ref={line1Ref}
54
+ className="font-heading font-bold text-white/80 text-xl md:text-2xl lg:text-3xl tracking-tight mb-2"
55
+ >
56
+ {{heroLine1}}
57
+ </p>
58
+
59
+ {/* Line 2: massive serif italic */}
60
+ <h1
61
+ ref={line2Ref}
62
+ className="font-drama text-white leading-none mb-6"
63
+ style={{
64
+ fontSize: 'clamp(3.5rem, 10vw, 9rem)',
65
+ fontStyle: 'italic',
66
+ }}
67
+ >
68
+ {{heroLine2}}
69
+ </h1>
70
+
71
+ {/* Subtitle */}
72
+ <p
73
+ ref={subtitleRef}
74
+ className="font-heading text-white/60 text-base md:text-lg max-w-xl mb-8 leading-relaxed"
75
+ >
76
+ {{purpose}}
77
+ </p>
78
+
79
+ {/* CTA */}
80
+ <div ref={ctaRef}>
81
+ <a
82
+ href="#features"
83
+ className="btn-magnetic relative inline-flex items-center px-8 py-4 rounded-full text-base font-semibold font-heading text-white overflow-hidden"
84
+ style={{ background: 'var(--color-accent)' }}
85
+ >
86
+ <span
87
+ className="btn-fill"
88
+ style={{ background: 'rgba(255,255,255,0.15)' }}
89
+ />
90
+ <span className="relative z-10">{{cta}}</span>
91
+ </a>
92
+ </div>
93
+ </div>
94
+ </section>
95
+ )
96
+ }
@@ -0,0 +1,119 @@
1
+ import { useEffect, useRef, useState } from 'react'
2
+ import { gsap } from 'gsap'
3
+
4
+ const NAV_LINKS = ['About', 'Features', 'Protocol', 'Pricing']
5
+
6
+ export default function Navbar() {
7
+ const navRef = useRef(null)
8
+ const [scrolled, setScrolled] = useState(false)
9
+ const [menuOpen, setMenuOpen] = useState(false)
10
+
11
+ useEffect(() => {
12
+ const handleScroll = () => setScrolled(window.scrollY > 80)
13
+ window.addEventListener('scroll', handleScroll, { passive: true })
14
+ return () => window.removeEventListener('scroll', handleScroll)
15
+ }, [])
16
+
17
+ useEffect(() => {
18
+ const ctx = gsap.context(() => {
19
+ gsap.from(navRef.current, {
20
+ y: -60,
21
+ opacity: 0,
22
+ duration: 1,
23
+ ease: 'power3.out',
24
+ delay: 0.2,
25
+ })
26
+ })
27
+ return () => ctx.revert()
28
+ }, [])
29
+
30
+ return (
31
+ <nav
32
+ ref={navRef}
33
+ className={`fixed top-4 left-1/2 -translate-x-1/2 z-50 px-6 py-3 rounded-full transition-all duration-500 ${
34
+ scrolled
35
+ ? 'bg-[color:var(--color-background)]/60 backdrop-blur-xl border border-[color:var(--color-primary)]/20 shadow-lg'
36
+ : 'bg-transparent'
37
+ }`}
38
+ >
39
+ <div className="flex items-center gap-8">
40
+ {/* Logo */}
41
+ <a
42
+ href="#"
43
+ className={`font-heading font-bold text-sm tracking-tight interactive-lift transition-colors duration-300 ${
44
+ scrolled ? 'text-[color:var(--color-dark)]' : 'text-white'
45
+ }`}
46
+ >
47
+ {{brand}}
48
+ </a>
49
+
50
+ {/* Desktop nav links */}
51
+ <div className="hidden md:flex items-center gap-6">
52
+ {NAV_LINKS.map((link) => (
53
+ <a
54
+ key={link}
55
+ href={`#${link.toLowerCase()}`}
56
+ className={`font-heading text-sm font-medium interactive-lift transition-colors duration-300 ${
57
+ scrolled
58
+ ? 'text-[color:var(--color-dark)]/70 hover:text-[color:var(--color-dark)]'
59
+ : 'text-white/70 hover:text-white'
60
+ }`}
61
+ >
62
+ {link}
63
+ </a>
64
+ ))}
65
+ </div>
66
+
67
+ {/* CTA */}
68
+ <a
69
+ href="#pricing"
70
+ className="btn-magnetic relative hidden md:inline-flex items-center px-5 py-2 rounded-full text-sm font-semibold font-heading text-white overflow-hidden"
71
+ style={{ background: 'var(--color-accent)' }}
72
+ >
73
+ <span className="btn-fill" style={{ background: 'var(--color-primary)' }} />
74
+ <span className="relative z-10">{{cta}}</span>
75
+ </a>
76
+
77
+ {/* Mobile hamburger */}
78
+ <button
79
+ onClick={() => setMenuOpen(!menuOpen)}
80
+ className={`md:hidden p-1 transition-colors duration-300 ${
81
+ scrolled ? 'text-[color:var(--color-dark)]' : 'text-white'
82
+ }`}
83
+ aria-label="Toggle menu"
84
+ >
85
+ <div className="w-5 flex flex-col gap-1">
86
+ <span className={`block h-0.5 bg-current transition-transform duration-300 ${menuOpen ? 'rotate-45 translate-y-1.5' : ''}`} />
87
+ <span className={`block h-0.5 bg-current transition-opacity duration-300 ${menuOpen ? 'opacity-0' : ''}`} />
88
+ <span className={`block h-0.5 bg-current transition-transform duration-300 ${menuOpen ? '-rotate-45 -translate-y-1.5' : ''}`} />
89
+ </div>
90
+ </button>
91
+ </div>
92
+
93
+ {/* Mobile menu */}
94
+ {menuOpen && (
95
+ <div className="md:hidden mt-4 pb-2 flex flex-col gap-3">
96
+ {NAV_LINKS.map((link) => (
97
+ <a
98
+ key={link}
99
+ href={`#${link.toLowerCase()}`}
100
+ onClick={() => setMenuOpen(false)}
101
+ className="font-heading text-sm font-medium text-[color:var(--color-dark)] interactive-lift"
102
+ >
103
+ {link}
104
+ </a>
105
+ ))}
106
+ <a
107
+ href="#pricing"
108
+ onClick={() => setMenuOpen(false)}
109
+ className="btn-magnetic relative inline-flex items-center justify-center px-5 py-2 rounded-full text-sm font-semibold font-heading text-white overflow-hidden mt-2"
110
+ style={{ background: 'var(--color-accent)' }}
111
+ >
112
+ <span className="btn-fill" style={{ background: 'var(--color-primary)' }} />
113
+ <span className="relative z-10">{{cta}}</span>
114
+ </a>
115
+ </div>
116
+ )}
117
+ </nav>
118
+ )
119
+ }
@@ -0,0 +1,67 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { gsap } from 'gsap'
3
+ import { ScrollTrigger } from 'gsap/ScrollTrigger'
4
+
5
+ const MANIFESTO_WORDS = `{{manifesto}}`.split(' ')
6
+
7
+ export default function Philosophy() {
8
+ const sectionRef = useRef(null)
9
+ const wordsRef = useRef([])
10
+
11
+ useEffect(() => {
12
+ const ctx = gsap.context(() => {
13
+ gsap.from(wordsRef.current, {
14
+ opacity: 0.1,
15
+ duration: 0.6,
16
+ ease: 'power2.inOut',
17
+ stagger: 0.05,
18
+ scrollTrigger: {
19
+ trigger: sectionRef.current,
20
+ start: 'top 70%',
21
+ end: 'bottom 40%',
22
+ scrub: 1.5,
23
+ },
24
+ })
25
+ }, sectionRef)
26
+ return () => ctx.revert()
27
+ }, [])
28
+
29
+ return (
30
+ <section
31
+ ref={sectionRef}
32
+ id="about"
33
+ className="relative py-32 md:py-48 px-8 md:px-16 lg:px-24 overflow-hidden"
34
+ style={{ background: 'var(--color-dark)' }}
35
+ >
36
+ {/* Parallax texture */}
37
+ <div
38
+ className="absolute inset-0 opacity-5"
39
+ style={{
40
+ backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60'%3E%3Cpath d='M0 0h60v60H0z' fill='none'/%3E%3Ccircle cx='30' cy='30' r='1' fill='white'/%3E%3C/svg%3E")`,
41
+ backgroundSize: '60px 60px',
42
+ }}
43
+ aria-hidden="true"
44
+ />
45
+
46
+ <div className="relative z-10 max-w-4xl mx-auto">
47
+ <p className="font-data text-xs tracking-widest uppercase mb-12" style={{ color: 'var(--color-accent)' }}>
48
+ Philosophy
49
+ </p>
50
+ <p
51
+ className="font-drama text-3xl md:text-5xl lg:text-6xl leading-tight"
52
+ style={{ color: 'var(--color-background)', fontStyle: 'italic' }}
53
+ >
54
+ {MANIFESTO_WORDS.map((word, i) => (
55
+ <span
56
+ key={i}
57
+ ref={(el) => { wordsRef.current[i] = el }}
58
+ className="inline-block mr-[0.25em]"
59
+ >
60
+ {word}
61
+ </span>
62
+ ))}
63
+ </p>
64
+ </div>
65
+ </section>
66
+ )
67
+ }