snow-flow 3.4.36 → 3.4.39

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 (49) hide show
  1. package/README.md +412 -287
  2. package/dist/cli/deploy-artifact.js +2 -2
  3. package/dist/mcp/servicenow-deployment-mcp.js +1 -1
  4. package/dist/mcp/servicenow-mcp-server.js +2 -2
  5. package/dist/queen/agent-factory.js +134 -52
  6. package/dist/queen/servicenow-queen.d.ts +127 -2
  7. package/dist/queen/servicenow-queen.js +978 -618
  8. package/dist/services/widget-deployment-service.d.ts +1 -1
  9. package/dist/services/widget-deployment-service.js +1 -1
  10. package/dist/templates/claude-md-template.d.ts +1 -1
  11. package/dist/templates/claude-md-template.js +2 -2
  12. package/dist/types/servicenow.types.d.ts +1 -1
  13. package/dist/utils/dependency-detector.d.ts +1 -1
  14. package/dist/utils/dependency-detector.js +1 -1
  15. package/dist/utils/servicenow-client.d.ts +1 -1
  16. package/dist/utils/servicenow-client.js +4 -8
  17. package/package.json +1 -1
  18. package/website/components/README.md +419 -0
  19. package/website/components/code-display/CodeDisplay.css +583 -0
  20. package/website/components/code-display/CodeDisplay.html +200 -0
  21. package/website/components/code-display/CodeDisplay.js +375 -0
  22. package/website/components/code-display/CodeDisplay.jsx +268 -0
  23. package/website/components/demo/ComponentLibraryDemo.html +845 -0
  24. package/website/components/feature-cards/FeatureCards.css +573 -0
  25. package/website/components/feature-cards/FeatureCards.html +247 -0
  26. package/website/components/feature-cards/FeatureCards.js +382 -0
  27. package/website/components/feature-cards/FeatureCards.jsx +235 -0
  28. package/website/components/hero/Hero.css +558 -0
  29. package/website/components/hero/Hero.html +98 -0
  30. package/website/components/hero/Hero.js +415 -0
  31. package/website/components/hero/Hero.jsx +214 -0
  32. package/website/components/interactive/InteractiveElements.css +776 -0
  33. package/website/components/interactive/InteractiveElements.html +283 -0
  34. package/website/components/interactive/InteractiveElements.js +489 -0
  35. package/website/components/interactive/InteractiveElements.jsx +444 -0
  36. package/website/components/layout/LayoutComponents.css +697 -0
  37. package/website/components/layout/LayoutComponents.html +374 -0
  38. package/website/components/layout/LayoutComponents.js +447 -0
  39. package/website/components/layout/LayoutComponents.jsx +379 -0
  40. package/website/components/navigation/Navigation.css +383 -0
  41. package/website/components/navigation/Navigation.html +89 -0
  42. package/website/components/navigation/Navigation.js +248 -0
  43. package/website/components/navigation/Navigation.jsx +124 -0
  44. package/website/css/animations.css +854 -0
  45. package/website/css/style.css +916 -948
  46. package/website/index.html +394 -424
  47. package/website/js/animations.js +707 -0
  48. package/website/js/main.js +310 -383
  49. package/website/mcp-servers.html +310 -0
@@ -0,0 +1,415 @@
1
+ // Snow-Flow Hero Component JavaScript
2
+
3
+ class SnowFlowHero {
4
+ constructor(options = {}) {
5
+ // Configuration
6
+ this.config = {
7
+ heroId: options.heroId || 'sf-hero',
8
+ titleId: options.titleId || 'sf-hero-title',
9
+ subtitleId: options.subtitleId || 'sf-hero-subtitle',
10
+ descriptionId: options.descriptionId || 'sf-hero-description',
11
+ actionsId: options.actionsId || 'sf-hero-actions',
12
+ statsId: options.statsId || 'sf-hero-stats',
13
+ scrollId: options.scrollId || 'sf-hero-scroll',
14
+ meshId: options.meshId || 'sf-hero-mesh',
15
+
16
+ // Content
17
+ title: options.title || 'Snow-Flow',
18
+ subtitle: options.subtitle || 'Advanced ServiceNow Development Framework',
19
+
20
+ // Effects
21
+ typewriterEffect: options.typewriterEffect !== false,
22
+ backgroundAnimation: options.backgroundAnimation !== false,
23
+ typewriterSpeed: {
24
+ title: options.titleSpeed || 100,
25
+ subtitle: options.subtitleSpeed || 50
26
+ },
27
+
28
+ // Animation states
29
+ isVisible: false,
30
+ titleComplete: false,
31
+ subtitleComplete: false,
32
+ descriptionVisible: false,
33
+ actionsVisible: false,
34
+ statsVisible: false,
35
+ scrollVisible: false
36
+ };
37
+
38
+ // DOM elements
39
+ this.elements = {
40
+ hero: document.getElementById(this.config.heroId),
41
+ title: document.getElementById(this.config.titleId),
42
+ subtitle: document.getElementById(this.config.subtitleId),
43
+ description: document.getElementById(this.config.descriptionId),
44
+ actions: document.getElementById(this.config.actionsId),
45
+ stats: document.getElementById(this.config.statsId),
46
+ scroll: document.getElementById(this.config.scrollId),
47
+ mesh: document.getElementById(this.config.meshId)
48
+ };
49
+
50
+ this.init();
51
+ }
52
+
53
+ init() {
54
+ if (!this.elements.hero) {
55
+ console.warn('SnowFlowHero: Hero element not found');
56
+ return;
57
+ }
58
+
59
+ this.setupIntersectionObserver();
60
+ this.setupEventListeners();
61
+ this.generateMeshDots();
62
+ this.setupProgressIndicator();
63
+ }
64
+
65
+ setupIntersectionObserver() {
66
+ const observer = new IntersectionObserver(
67
+ (entries) => {
68
+ entries.forEach(entry => {
69
+ if (entry.isIntersecting && !this.config.isVisible) {
70
+ this.config.isVisible = true;
71
+ this.startAnimations();
72
+ }
73
+ });
74
+ },
75
+ { threshold: 0.1 }
76
+ );
77
+
78
+ observer.observe(this.elements.hero);
79
+ }
80
+
81
+ setupEventListeners() {
82
+ // CTA Button clicks
83
+ const primaryCTA = this.elements.hero.querySelector('#sf-hero-primary-cta');
84
+ const secondaryCTA = this.elements.hero.querySelector('#sf-hero-secondary-cta');
85
+
86
+ if (primaryCTA) {
87
+ primaryCTA.addEventListener('click', (e) => {
88
+ this.handleCTAClick(e, primaryCTA.getAttribute('data-href'));
89
+ });
90
+ }
91
+
92
+ if (secondaryCTA) {
93
+ secondaryCTA.addEventListener('click', (e) => {
94
+ this.handleCTAClick(e, secondaryCTA.getAttribute('data-href'));
95
+ });
96
+ }
97
+
98
+ // Scroll indicator click
99
+ if (this.elements.scroll) {
100
+ this.elements.scroll.addEventListener('click', () => {
101
+ this.scrollToNextSection();
102
+ });
103
+ }
104
+
105
+ // Keyboard accessibility
106
+ document.addEventListener('keydown', (e) => {
107
+ if (e.key === 'Enter' || e.key === ' ') {
108
+ const focusedElement = document.activeElement;
109
+ if (focusedElement && focusedElement.classList.contains('sf-hero__cta')) {
110
+ e.preventDefault();
111
+ focusedElement.click();
112
+ }
113
+ }
114
+ });
115
+ }
116
+
117
+ generateMeshDots() {
118
+ if (!this.config.backgroundAnimation || !this.elements.mesh) return;
119
+
120
+ // Clear existing dots
121
+ this.elements.mesh.innerHTML = '';
122
+
123
+ // Generate animated dots
124
+ for (let i = 0; i < 50; i++) {
125
+ const dot = document.createElement('div');
126
+ dot.className = 'sf-hero__mesh-dot';
127
+ dot.style.cssText = `
128
+ animation-delay: ${Math.random() * 5}s;
129
+ left: ${Math.random() * 100}%;
130
+ top: ${Math.random() * 100}%;
131
+ `;
132
+ this.elements.mesh.appendChild(dot);
133
+ }
134
+ }
135
+
136
+ setupProgressIndicator() {
137
+ // Update scroll progress for the scroll indicator
138
+ window.addEventListener('scroll', () => {
139
+ const scrolled = window.pageYOffset;
140
+ const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
141
+ const progress = Math.min(scrolled / maxScroll, 1);
142
+
143
+ // You can use this progress value for additional effects if needed
144
+ this.updateScrollProgress(progress);
145
+ });
146
+ }
147
+
148
+ updateScrollProgress(progress) {
149
+ // Optional: Add scroll-based effects here
150
+ // For example, changing the hero opacity as user scrolls
151
+ if (this.elements.hero) {
152
+ const opacity = Math.max(0.3, 1 - progress * 0.7);
153
+ this.elements.hero.style.opacity = opacity;
154
+ }
155
+ }
156
+
157
+ startAnimations() {
158
+ // Add visible class to hero
159
+ this.elements.hero.classList.add('sf-hero--visible');
160
+
161
+ // Start typewriter effects
162
+ if (this.config.typewriterEffect) {
163
+ setTimeout(() => this.typewriteTitle(), 500);
164
+ } else {
165
+ // Set text immediately if typewriter is disabled
166
+ this.elements.title.textContent = this.config.title;
167
+ this.elements.subtitle.textContent = this.config.subtitle;
168
+ this.config.titleComplete = true;
169
+ this.config.subtitleComplete = true;
170
+ this.showRemainingElements();
171
+ }
172
+ }
173
+
174
+ typewriteTitle() {
175
+ if (!this.elements.title) return;
176
+
177
+ const title = this.config.title;
178
+ let index = 0;
179
+
180
+ const typeChar = () => {
181
+ if (index < title.length) {
182
+ this.elements.title.textContent = title.slice(0, index + 1);
183
+
184
+ // Add cursor
185
+ const cursor = document.createElement('span');
186
+ cursor.className = 'sf-hero__cursor';
187
+ cursor.textContent = '|';
188
+ this.elements.title.appendChild(cursor);
189
+
190
+ index++;
191
+ setTimeout(typeChar, this.config.typewriterSpeed.title);
192
+ } else {
193
+ // Remove cursor and mark title complete
194
+ const cursor = this.elements.title.querySelector('.sf-hero__cursor');
195
+ if (cursor) cursor.remove();
196
+
197
+ this.config.titleComplete = true;
198
+ setTimeout(() => this.typewriteSubtitle(), 300);
199
+ }
200
+ };
201
+
202
+ typeChar();
203
+ }
204
+
205
+ typewriteSubtitle() {
206
+ if (!this.elements.subtitle) return;
207
+
208
+ const subtitle = this.config.subtitle;
209
+ let index = 0;
210
+
211
+ const typeChar = () => {
212
+ if (index < subtitle.length) {
213
+ this.elements.subtitle.textContent = subtitle.slice(0, index + 1);
214
+
215
+ // Add cursor
216
+ const cursor = document.createElement('span');
217
+ cursor.className = 'sf-hero__cursor';
218
+ cursor.textContent = '|';
219
+ this.elements.subtitle.appendChild(cursor);
220
+
221
+ index++;
222
+ setTimeout(typeChar, this.config.typewriterSpeed.subtitle);
223
+ } else {
224
+ // Remove cursor and mark subtitle complete
225
+ const cursor = this.elements.subtitle.querySelector('.sf-hero__cursor');
226
+ if (cursor) cursor.remove();
227
+
228
+ this.config.subtitleComplete = true;
229
+ this.showRemainingElements();
230
+ }
231
+ };
232
+
233
+ typeChar();
234
+ }
235
+
236
+ showRemainingElements() {
237
+ // Show description
238
+ setTimeout(() => {
239
+ if (this.elements.description) {
240
+ this.elements.description.classList.add('sf-hero__description--visible');
241
+ this.config.descriptionVisible = true;
242
+ }
243
+ }, 200);
244
+
245
+ // Show actions
246
+ setTimeout(() => {
247
+ if (this.elements.actions) {
248
+ this.elements.actions.classList.add('sf-hero__actions--visible');
249
+ this.config.actionsVisible = true;
250
+ }
251
+ }, 400);
252
+
253
+ // Show stats
254
+ setTimeout(() => {
255
+ if (this.elements.stats) {
256
+ this.elements.stats.classList.add('sf-hero__stats--visible');
257
+ this.config.statsVisible = true;
258
+ this.animateStats();
259
+ }
260
+ }, 600);
261
+
262
+ // Show scroll indicator
263
+ setTimeout(() => {
264
+ if (this.elements.scroll) {
265
+ this.elements.scroll.classList.add('sf-hero__scroll-indicator--visible');
266
+ this.config.scrollVisible = true;
267
+ }
268
+ }, 800);
269
+ }
270
+
271
+ animateStats() {
272
+ const statElements = this.elements.stats.querySelectorAll('.sf-hero__stat');
273
+ statElements.forEach((stat, index) => {
274
+ setTimeout(() => {
275
+ stat.style.animationDelay = `${0.5 + index * 0.1}s`;
276
+ stat.style.opacity = '1';
277
+ stat.style.transform = 'translateY(0)';
278
+ }, index * 100);
279
+ });
280
+ }
281
+
282
+ handleCTAClick(e, href) {
283
+ e.preventDefault();
284
+
285
+ if (!href) return;
286
+
287
+ // Add ripple effect for primary button
288
+ if (e.target.classList.contains('sf-hero__cta--primary')) {
289
+ this.createRippleEffect(e.target, e);
290
+ }
291
+
292
+ // Navigate to target
293
+ if (href.startsWith('#')) {
294
+ this.scrollToSection(href);
295
+ } else {
296
+ window.open(href, '_blank', 'noopener,noreferrer');
297
+ }
298
+ }
299
+
300
+ createRippleEffect(button, event) {
301
+ const ripple = button.querySelector('.sf-hero__cta-ripple');
302
+ if (!ripple) return;
303
+
304
+ // Reset ripple
305
+ ripple.style.width = '0';
306
+ ripple.style.height = '0';
307
+
308
+ // Trigger ripple animation
309
+ setTimeout(() => {
310
+ ripple.style.width = '300px';
311
+ ripple.style.height = '300px';
312
+ }, 10);
313
+
314
+ // Reset after animation
315
+ setTimeout(() => {
316
+ ripple.style.width = '0';
317
+ ripple.style.height = '0';
318
+ }, 500);
319
+ }
320
+
321
+ scrollToSection(target) {
322
+ const element = document.querySelector(target);
323
+ if (element) {
324
+ const offset = 80; // Account for fixed navbar
325
+ const targetPosition = element.offsetTop - offset;
326
+
327
+ window.scrollTo({
328
+ top: targetPosition,
329
+ behavior: 'smooth'
330
+ });
331
+ }
332
+ }
333
+
334
+ scrollToNextSection() {
335
+ // Find the next section after hero
336
+ const nextSection = this.elements.hero.nextElementSibling;
337
+ if (nextSection) {
338
+ nextSection.scrollIntoView({ behavior: 'smooth' });
339
+ }
340
+ }
341
+
342
+ // Public API methods
343
+ updateContent(newContent) {
344
+ if (newContent.title) {
345
+ this.config.title = newContent.title;
346
+ if (!this.config.typewriterEffect) {
347
+ this.elements.title.textContent = newContent.title;
348
+ }
349
+ }
350
+
351
+ if (newContent.subtitle) {
352
+ this.config.subtitle = newContent.subtitle;
353
+ if (!this.config.typewriterEffect) {
354
+ this.elements.subtitle.textContent = newContent.subtitle;
355
+ }
356
+ }
357
+
358
+ if (newContent.description && this.elements.description) {
359
+ this.elements.description.textContent = newContent.description;
360
+ }
361
+ }
362
+
363
+ resetAnimations() {
364
+ // Reset all animation states
365
+ this.config.isVisible = false;
366
+ this.config.titleComplete = false;
367
+ this.config.subtitleComplete = false;
368
+ this.config.descriptionVisible = false;
369
+ this.config.actionsVisible = false;
370
+ this.config.statsVisible = false;
371
+ this.config.scrollVisible = false;
372
+
373
+ // Remove classes
374
+ this.elements.hero.classList.remove('sf-hero--visible');
375
+ if (this.elements.description) {
376
+ this.elements.description.classList.remove('sf-hero__description--visible');
377
+ }
378
+ if (this.elements.actions) {
379
+ this.elements.actions.classList.remove('sf-hero__actions--visible');
380
+ }
381
+ if (this.elements.stats) {
382
+ this.elements.stats.classList.remove('sf-hero__stats--visible');
383
+ }
384
+ if (this.elements.scroll) {
385
+ this.elements.scroll.classList.remove('sf-hero__scroll-indicator--visible');
386
+ }
387
+
388
+ // Clear content
389
+ this.elements.title.textContent = '';
390
+ this.elements.subtitle.textContent = '';
391
+ }
392
+
393
+ destroy() {
394
+ // Cleanup method
395
+ this.resetAnimations();
396
+ // Remove event listeners if needed
397
+ }
398
+ }
399
+
400
+ // Auto-initialize on DOM content loaded
401
+ document.addEventListener('DOMContentLoaded', () => {
402
+ // Check if hero exists before initializing
403
+ const heroElement = document.getElementById('sf-hero');
404
+ if (heroElement) {
405
+ window.snowFlowHero = new SnowFlowHero();
406
+ }
407
+ });
408
+
409
+ // Export for module usage
410
+ if (typeof module !== 'undefined' && module.exports) {
411
+ module.exports = SnowFlowHero;
412
+ }
413
+
414
+ // Global access
415
+ window.SnowFlowHero = SnowFlowHero;
@@ -0,0 +1,214 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import './Hero.css';
3
+
4
+ const Hero = ({
5
+ title = "Snow-Flow",
6
+ subtitle = "Advanced ServiceNow Development Framework",
7
+ description = "Revolutionize your ServiceNow development with AI-powered automation, intelligent orchestration, and cutting-edge tools designed for the modern enterprise.",
8
+ primaryCTA = { text: "Get Started", href: "#get-started" },
9
+ secondaryCTA = { text: "Learn More", href: "#features" },
10
+ stats = [
11
+ { number: "17", label: "MCP Servers" },
12
+ { number: "200+", label: "Tools" },
13
+ { number: "80%", label: "Time Saved" },
14
+ { number: "100%", label: "Real Data" }
15
+ ],
16
+ backgroundAnimation = true,
17
+ typewriterEffect = true,
18
+ className = ""
19
+ }) => {
20
+ const [displayedTitle, setDisplayedTitle] = useState('');
21
+ const [titleComplete, setTitleComplete] = useState(false);
22
+ const [displayedSubtitle, setDisplayedSubtitle] = useState('');
23
+ const [subtitleComplete, setSubtitleComplete] = useState(false);
24
+ const [isVisible, setIsVisible] = useState(false);
25
+ const heroRef = useRef(null);
26
+
27
+ // Intersection Observer for entrance animation
28
+ useEffect(() => {
29
+ const observer = new IntersectionObserver(
30
+ ([entry]) => {
31
+ if (entry.isIntersecting) {
32
+ setIsVisible(true);
33
+ }
34
+ },
35
+ { threshold: 0.1 }
36
+ );
37
+
38
+ if (heroRef.current) {
39
+ observer.observe(heroRef.current);
40
+ }
41
+
42
+ return () => observer.disconnect();
43
+ }, []);
44
+
45
+ // Typewriter effect for title
46
+ useEffect(() => {
47
+ if (!typewriterEffect || !isVisible) return;
48
+
49
+ let index = 0;
50
+ const typeTitle = () => {
51
+ if (index < title.length) {
52
+ setDisplayedTitle(title.slice(0, index + 1));
53
+ index++;
54
+ setTimeout(typeTitle, 100);
55
+ } else {
56
+ setTitleComplete(true);
57
+ }
58
+ };
59
+
60
+ const timer = setTimeout(typeTitle, 500);
61
+ return () => clearTimeout(timer);
62
+ }, [title, typewriterEffect, isVisible]);
63
+
64
+ // Typewriter effect for subtitle
65
+ useEffect(() => {
66
+ if (!typewriterEffect || !titleComplete) return;
67
+
68
+ let index = 0;
69
+ const typeSubtitle = () => {
70
+ if (index < subtitle.length) {
71
+ setDisplayedSubtitle(subtitle.slice(0, index + 1));
72
+ index++;
73
+ setTimeout(typeSubtitle, 50);
74
+ } else {
75
+ setSubtitleComplete(true);
76
+ }
77
+ };
78
+
79
+ const timer = setTimeout(typeSubtitle, 300);
80
+ return () => clearTimeout(timer);
81
+ }, [subtitle, typewriterEffect, titleComplete]);
82
+
83
+ // Set initial text if typewriter is disabled
84
+ useEffect(() => {
85
+ if (!typewriterEffect) {
86
+ setDisplayedTitle(title);
87
+ setDisplayedSubtitle(subtitle);
88
+ setTitleComplete(true);
89
+ setSubtitleComplete(true);
90
+ }
91
+ }, [title, subtitle, typewriterEffect]);
92
+
93
+ const handleCTAClick = (e, href) => {
94
+ e.preventDefault();
95
+ if (href.startsWith('#')) {
96
+ const target = document.querySelector(href);
97
+ if (target) {
98
+ const offset = 80;
99
+ const targetPosition = target.offsetTop - offset;
100
+ window.scrollTo({
101
+ top: targetPosition,
102
+ behavior: 'smooth'
103
+ });
104
+ }
105
+ } else {
106
+ window.open(href, '_blank', 'noopener,noreferrer');
107
+ }
108
+ };
109
+
110
+ return (
111
+ <section
112
+ ref={heroRef}
113
+ className={`sf-hero ${isVisible ? 'sf-hero--visible' : ''} ${className}`}
114
+ >
115
+ {backgroundAnimation && (
116
+ <>
117
+ {/* Animated Background Mesh */}
118
+ <div className="sf-hero__background">
119
+ <div className="sf-hero__mesh">
120
+ {Array.from({ length: 50 }, (_, i) => (
121
+ <div
122
+ key={i}
123
+ className="sf-hero__mesh-dot"
124
+ style={{
125
+ animationDelay: `${Math.random() * 5}s`,
126
+ left: `${Math.random() * 100}%`,
127
+ top: `${Math.random() * 100}%`
128
+ }}
129
+ />
130
+ ))}
131
+ </div>
132
+
133
+ {/* Gradient Orbs */}
134
+ <div className="sf-hero__orb sf-hero__orb--1"></div>
135
+ <div className="sf-hero__orb sf-hero__orb--2"></div>
136
+ <div className="sf-hero__orb sf-hero__orb--3"></div>
137
+ </div>
138
+ </>
139
+ )}
140
+
141
+ <div className="sf-hero__container">
142
+ <div className="sf-hero__content">
143
+ {/* Main Title */}
144
+ <h1 className="sf-hero__title">
145
+ {displayedTitle}
146
+ {typewriterEffect && !titleComplete && (
147
+ <span className="sf-hero__cursor">|</span>
148
+ )}
149
+ </h1>
150
+
151
+ {/* Subtitle */}
152
+ <h2 className="sf-hero__subtitle">
153
+ {displayedSubtitle}
154
+ {typewriterEffect && titleComplete && !subtitleComplete && (
155
+ <span className="sf-hero__cursor">|</span>
156
+ )}
157
+ </h2>
158
+
159
+ {/* Description */}
160
+ <p className={`sf-hero__description ${subtitleComplete ? 'sf-hero__description--visible' : ''}`}>
161
+ {description}
162
+ </p>
163
+
164
+ {/* CTA Buttons */}
165
+ <div className={`sf-hero__actions ${subtitleComplete ? 'sf-hero__actions--visible' : ''}`}>
166
+ <button
167
+ className="sf-hero__cta sf-hero__cta--primary"
168
+ onClick={(e) => handleCTAClick(e, primaryCTA.href)}
169
+ >
170
+ {primaryCTA.text}
171
+ <span className="sf-hero__cta-ripple"></span>
172
+ </button>
173
+
174
+ <button
175
+ className="sf-hero__cta sf-hero__cta--secondary"
176
+ onClick={(e) => handleCTAClick(e, secondaryCTA.href)}
177
+ >
178
+ {secondaryCTA.text}
179
+ <span className="sf-hero__cta-arrow">→</span>
180
+ </button>
181
+ </div>
182
+
183
+ {/* Stats */}
184
+ {stats && stats.length > 0 && (
185
+ <div className={`sf-hero__stats ${subtitleComplete ? 'sf-hero__stats--visible' : ''}`}>
186
+ {stats.map((stat, index) => (
187
+ <div
188
+ key={index}
189
+ className="sf-hero__stat"
190
+ style={{ animationDelay: `${0.5 + index * 0.1}s` }}
191
+ >
192
+ <div className="sf-hero__stat-number">{stat.number}</div>
193
+ <div className="sf-hero__stat-label">{stat.label}</div>
194
+ </div>
195
+ ))}
196
+ </div>
197
+ )}
198
+ </div>
199
+
200
+ {/* Scroll Indicator */}
201
+ <div className={`sf-hero__scroll-indicator ${subtitleComplete ? 'sf-hero__scroll-indicator--visible' : ''}`}>
202
+ <div className="sf-hero__scroll-arrow">
203
+ <span></span>
204
+ <span></span>
205
+ <span></span>
206
+ </div>
207
+ <span className="sf-hero__scroll-text">Scroll to explore</span>
208
+ </div>
209
+ </div>
210
+ </section>
211
+ );
212
+ };
213
+
214
+ export default Hero;