blocks-dusted 0.1.0 → 0.1.2

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 (50) hide show
  1. package/README.md +967 -3
  2. package/package.json +40 -40
  3. package/src/commands/add.js +143 -45
  4. package/src/commands/list.js +2 -2
  5. package/src/installers/checkRequirements.js +1 -0
  6. package/src/installers/copyTemplateFiles.js +23 -2
  7. package/src/installers/patchPayloadConfigCollections.js +92 -0
  8. package/src/installers/writeInstalledBlockReadme.js +17 -4
  9. package/src/registry/listTemplates.js +16 -7
  10. package/src/registry/loadTemplateManifest.js +20 -11
  11. package/src/registry/validateTemplateManifest.js +6 -5
  12. package/src/utils/paths.js +7 -0
  13. package/templates/blocks/DD-BookCall/Component.tsx +535 -573
  14. package/templates/blocks/DD-CardTemplate01/Component.tsx +45 -49
  15. package/templates/blocks/DD-Carousel-A/Component.tsx +249 -266
  16. package/templates/blocks/DD-Carousel-B/Component.tsx +403 -432
  17. package/templates/blocks/DD-ComparisonTable/Component.tsx +256 -272
  18. package/templates/blocks/DD-Contact/Component.tsx +434 -439
  19. package/templates/blocks/DD-Contact/config.ts +8 -3
  20. package/templates/blocks/DD-FeatCat/Component.tsx +302 -361
  21. package/templates/blocks/DD-FeatCat/config.ts +201 -204
  22. package/templates/blocks/DD-FeatStrip/Component.tsx +171 -201
  23. package/templates/blocks/DD-Hero/Component.tsx +297 -317
  24. package/templates/blocks/DD-HorizontalScroll/Component.tsx +244 -303
  25. package/templates/blocks/DD-MasonaryMedia/Component.tsx +202 -228
  26. package/templates/blocks/DD-MasonaryMedia/config.ts +13 -14
  27. package/templates/blocks/DD-Pricing/Component.tsx +372 -380
  28. package/templates/blocks/DD-Process/Component.tsx +149 -155
  29. package/templates/blocks/DD-Section/Component.tsx +266 -327
  30. package/templates/blocks/DD-Services/Component.tsx +176 -188
  31. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +307 -317
  32. package/templates/blocks/DD-Slider-A/Component.tsx +237 -252
  33. package/templates/blocks/DD-StackCards/Component.tsx +137 -160
  34. package/templates/blocks/DD-Team/Component.tsx +247 -265
  35. package/templates/blocks/DD-TechStack/Component.tsx +57 -72
  36. package/templates/blocks/DD-Testimonials/Component.tsx +147 -147
  37. package/templates/blocks/DD-Testimonials/config.ts +66 -66
  38. package/templates/blocks/DD-Work/Component.tsx +315 -328
  39. package/templates/blocks/DD-Work-B/Component.tsx +294 -320
  40. package/templates/collections/Testimonials/README.md +11 -0
  41. package/templates/collections/Testimonials/Testimonials.ts +161 -0
  42. package/templates/collections/Testimonials/manifest.json +55 -0
  43. package/templates/components/RichText/README.md +13 -0
  44. package/templates/components/RichText/converter/componentConverter/blocks.tsx +43 -0
  45. package/templates/components/RichText/converter/componentConverter/types.ts +11 -0
  46. package/templates/components/RichText/converter/index.tsx +18 -0
  47. package/templates/components/RichText/converter/internalLinks.tsx +45 -0
  48. package/templates/components/RichText/converter/textConverter.tsx +27 -0
  49. package/templates/components/RichText/index.tsx +35 -0
  50. package/templates/components/RichText/manifest.json +80 -0
@@ -1,15 +1,15 @@
1
- "use client";
1
+ 'use client'
2
2
 
3
- import { useLayoutEffect, useRef, useMemo } from "react";
4
- import Image from "next/image";
5
- import { gsap } from "gsap";
6
- import { ScrollTrigger } from "gsap/ScrollTrigger";
7
- import dynamic from "next/dynamic";
3
+ import { useLayoutEffect, useRef, useMemo } from 'react'
4
+ import Image from 'next/image'
5
+ import { gsap } from 'gsap'
6
+ import { ScrollTrigger } from 'gsap/ScrollTrigger'
7
+ import dynamic from 'next/dynamic'
8
8
 
9
9
  const DotLottieReact = dynamic(
10
- () => import("@lottiefiles/dotlottie-react").then((m) => m.DotLottieReact),
10
+ () => import('@lottiefiles/dotlottie-react').then((m) => m.DotLottieReact),
11
11
  { ssr: false, loading: () => null },
12
- );
12
+ )
13
13
  import {
14
14
  PencilRuler,
15
15
  Code2,
@@ -30,63 +30,57 @@ import {
30
30
  Send,
31
31
  ArrowUp,
32
32
  Hammer,
33
- } from "lucide-react";
34
- import type { LucideIcon } from "lucide-react";
35
- type Media = { url?: string | null; alt?: string | null; [key: string]: any };
33
+ } from 'lucide-react'
34
+ import type { LucideIcon } from 'lucide-react'
35
+ type Media = { url?: string | null; alt?: string | null; [key: string]: any }
36
36
 
37
- if (typeof window !== "undefined") {
38
- gsap.registerPlugin(ScrollTrigger);
37
+ if (typeof window !== 'undefined') {
38
+ gsap.registerPlugin(ScrollTrigger)
39
39
  }
40
40
 
41
41
  /* ── Types ─────────────────────────────────────────────────────── */
42
42
 
43
43
  interface TagItem {
44
- id?: string;
45
- tag: string;
44
+ id?: string
45
+ tag: string
46
46
  }
47
47
 
48
48
  interface SlideItem {
49
- id?: string;
50
- phase?: string | null;
51
- watermark?: string | null;
52
- icon?: string | null;
53
- accent?: "lime" | "purple" | null;
54
- headline?: string | null;
55
- sub?: string | null;
56
- body?: string | null;
57
- tags?: TagItem[] | null;
58
- visualType?:
59
- | "wireframe"
60
- | "codeEditor"
61
- | "pipeline"
62
- | "funnel"
63
- | "automation"
64
- | null;
65
- customMedia?: Media | string | null;
66
- customMediaType?: "image" | "video" | "lottie" | null;
49
+ id?: string
50
+ phase?: string | null
51
+ watermark?: string | null
52
+ icon?: string | null
53
+ accent?: 'lime' | 'purple' | null
54
+ headline?: string | null
55
+ sub?: string | null
56
+ body?: string | null
57
+ tags?: TagItem[] | null
58
+ visualType?: 'wireframe' | 'codeEditor' | 'pipeline' | 'funnel' | 'automation' | null
59
+ customMedia?: Media | string | null
60
+ customMediaType?: 'image' | 'video' | 'lottie' | null
67
61
  }
68
62
 
69
63
  interface DDHorizontalScrollProps {
70
- id?: string | null;
71
- sectionLabel?: string | null;
72
- slides?: SlideItem[] | null;
73
- sectionId?: string | null;
74
- enableCustomCSS?: boolean | null;
75
- customCSS?: string | null;
76
- [key: string]: unknown;
64
+ id?: string | null
65
+ sectionLabel?: string | null
66
+ slides?: SlideItem[] | null
67
+ sectionId?: string | null
68
+ enableCustomCSS?: boolean | null
69
+ customCSS?: string | null
70
+ [key: string]: unknown
77
71
  }
78
72
 
79
73
  /* ── Accent helpers ─────────────────────────────────────────────── */
80
74
 
81
75
  const ACCENTS = {
82
- lime: { hex: "#84cc16", rgb: "132,204,22" },
83
- purple: { hex: "#9803e2", rgb: "152,3,226" },
84
- } as const;
76
+ lime: { hex: '#84cc16', rgb: '132,204,22' },
77
+ purple: { hex: '#9803e2', rgb: '152,3,226' },
78
+ } as const
85
79
 
86
- type AccentKey = keyof typeof ACCENTS;
80
+ type AccentKey = keyof typeof ACCENTS
87
81
 
88
82
  function getAccent(key?: string | null) {
89
- return ACCENTS[(key as AccentKey) ?? "lime"] ?? ACCENTS.lime;
83
+ return ACCENTS[(key as AccentKey) ?? 'lime'] ?? ACCENTS.lime
90
84
  }
91
85
 
92
86
  /* ── Icon map ───────────────────────────────────────────────────── */
@@ -106,25 +100,25 @@ const ICON_MAP: Record<string, LucideIcon> = {
106
100
  lightbulb: Lightbulb,
107
101
  puzzle: Puzzle,
108
102
  star: Star,
109
- };
103
+ }
110
104
 
111
105
  function SlideIcon({
112
106
  name,
113
107
  size = 14,
114
108
  style,
115
109
  }: {
116
- name?: string | null;
117
- size?: number;
118
- style?: React.CSSProperties;
110
+ name?: string | null
111
+ size?: number
112
+ style?: React.CSSProperties
119
113
  }) {
120
- const Icon = ICON_MAP[name ?? "star"] ?? Star;
121
- return <Icon size={size} style={style} />;
114
+ const Icon = ICON_MAP[name ?? 'star'] ?? Star
115
+ return <Icon size={size} style={style} />
122
116
  }
123
117
 
124
118
  /* ── Visual panels ─────────────────────────────────────────────── */
125
119
 
126
120
  function WireframeVisual({ accent }: { accent: string }) {
127
- const { hex, rgb } = getAccent(accent);
121
+ const { hex, rgb } = getAccent(accent)
128
122
  return (
129
123
  <section className="relative w-full h-full flex items-center justify-center">
130
124
  <div className="relative w-64 h-64 scale-80 md:scale-100">
@@ -136,17 +130,14 @@ function WireframeVisual({ accent }: { accent: string }) {
136
130
  }}
137
131
  >
138
132
  <div className="p-4 space-y-3">
139
- <div
140
- className="h-3 rounded-full w-3/4"
141
- style={{ background: `rgba(${rgb},0.3)` }}
142
- />
133
+ <div className="h-3 rounded-full w-3/4" style={{ background: `rgba(${rgb},0.3)` }} />
143
134
  <div
144
135
  className="h-2 rounded-full w-full"
145
- style={{ background: "rgba(255,255,255,0.08)" }}
136
+ style={{ background: 'rgba(255,255,255,0.08)' }}
146
137
  />
147
138
  <div
148
139
  className="h-2 rounded-full w-5/6"
149
- style={{ background: "rgba(255,255,255,0.08)" }}
140
+ style={{ background: 'rgba(255,255,255,0.08)' }}
150
141
  />
151
142
  <div className="grid grid-cols-2 gap-2 mt-4">
152
143
  <div
@@ -159,8 +150,8 @@ function WireframeVisual({ accent }: { accent: string }) {
159
150
  <div
160
151
  className="h-16 rounded-lg"
161
152
  style={{
162
- background: "rgba(255,255,255,0.04)",
163
- border: "1px solid rgba(255,255,255,0.08)",
153
+ background: 'rgba(255,255,255,0.04)',
154
+ border: '1px solid rgba(255,255,255,0.08)',
164
155
  }}
165
156
  />
166
157
  </div>
@@ -178,52 +169,40 @@ function WireframeVisual({ accent }: { accent: string }) {
178
169
  className="absolute -right-4 top-1/3 w-5 h-5 rounded-full border-2 flex items-center justify-center"
179
170
  style={{ borderColor: hex, background: `rgba(${rgb},0.15)` }}
180
171
  >
181
- <div
182
- className="w-1.5 h-1.5 rounded-full"
183
- style={{ background: hex }}
184
- />
172
+ <div className="w-1.5 h-1.5 rounded-full" style={{ background: hex }} />
185
173
  </div>
186
174
  {/* Grid bg */}
187
175
  <div
188
176
  className="absolute -inset-6 pointer-events-none"
189
177
  style={{
190
178
  backgroundImage: `linear-gradient(rgba(${rgb},0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(${rgb},0.05) 1px, transparent 1px)`,
191
- backgroundSize: "24px 24px",
179
+ backgroundSize: '24px 24px',
192
180
  }}
193
181
  />
194
182
  </div>
195
183
  </section>
196
- );
184
+ )
197
185
  }
198
186
 
199
187
  function CodeEditorVisual({ accent }: { accent: string }) {
200
- const { hex, rgb } = getAccent(accent);
188
+ const { hex, rgb } = getAccent(accent)
201
189
  return (
202
190
  <div className="relative w-full h-full flex items-center justify-center scale-80 md:scale-100">
203
191
  <div
204
192
  className="relative w-72 rounded-xl overflow-hidden"
205
- style={{ background: "#0d0d0d", border: `1px solid rgba(${rgb},0.15)` }}
193
+ style={{ background: '#0d0d0d', border: `1px solid rgba(${rgb},0.15)` }}
206
194
  >
207
195
  {/* Title bar */}
208
196
  <div
209
197
  className="flex items-center gap-2 px-4 py-3 border-b"
210
- style={{ borderColor: "rgba(255,255,255,0.06)" }}
198
+ style={{ borderColor: 'rgba(255,255,255,0.06)' }}
211
199
  >
212
- <div
213
- className="w-3 h-3 rounded-full"
214
- style={{ background: "#ff5f57" }}
215
- />
216
- <div
217
- className="w-3 h-3 rounded-full"
218
- style={{ background: "#febc2e" }}
219
- />
220
- <div
221
- className="w-3 h-3 rounded-full"
222
- style={{ background: "#28c840" }}
223
- />
200
+ <div className="w-3 h-3 rounded-full" style={{ background: '#ff5f57' }} />
201
+ <div className="w-3 h-3 rounded-full" style={{ background: '#febc2e' }} />
202
+ <div className="w-3 h-3 rounded-full" style={{ background: '#28c840' }} />
224
203
  <span
225
204
  className="ml-2 text-xs"
226
- style={{ color: "rgba(255,255,255,0.3)", fontFamily: "monospace" }}
205
+ style={{ color: 'rgba(255,255,255,0.3)', fontFamily: 'monospace' }}
227
206
  >
228
207
  index.tsx
229
208
  </span>
@@ -231,65 +210,61 @@ function CodeEditorVisual({ accent }: { accent: string }) {
231
210
  {/* Code lines */}
232
211
  <div className="p-4 space-y-1.5 font-mono text-xs">
233
212
  <div>
234
- <span style={{ color: "#9803e2" }}>const</span>{" "}
235
- <span style={{ color: hex }}>Hero</span>{" "}
236
- <span style={{ color: "rgba(255,255,255,0.4)" }}>=</span>{" "}
237
- <span style={{ color: "#9803e2" }}>()</span>{" "}
238
- <span style={{ color: "rgba(255,255,255,0.4)" }}>=&gt;</span>{" "}
239
- <span style={{ color: "rgba(255,255,255,0.4)" }}>{"{"}</span>
213
+ <span style={{ color: '#9803e2' }}>const</span> <span style={{ color: hex }}>Hero</span>{' '}
214
+ <span style={{ color: 'rgba(255,255,255,0.4)' }}>=</span>{' '}
215
+ <span style={{ color: '#9803e2' }}>()</span>{' '}
216
+ <span style={{ color: 'rgba(255,255,255,0.4)' }}>=&gt;</span>{' '}
217
+ <span style={{ color: 'rgba(255,255,255,0.4)' }}>{'{'}</span>
240
218
  </div>
241
219
  <div className="pl-4">
242
- <span style={{ color: "#9803e2" }}>return</span>{" "}
243
- <span style={{ color: "rgba(255,255,255,0.4)" }}>(</span>
220
+ <span style={{ color: '#9803e2' }}>return</span>{' '}
221
+ <span style={{ color: 'rgba(255,255,255,0.4)' }}>(</span>
244
222
  </div>
245
223
  <div className="pl-8">
246
- <span style={{ color: "rgba(255,255,255,0.3)" }}>&lt;</span>
247
- <span style={{ color: hex }}>section</span>{" "}
248
- <span style={{ color: "#9803e2" }}>className</span>
249
- <span style={{ color: "rgba(255,255,255,0.3)" }}>=</span>
250
- <span style={{ color: "#febc2e" }}>"hero"</span>
251
- <span style={{ color: "rgba(255,255,255,0.3)" }}>&gt;</span>
224
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>&lt;</span>
225
+ <span style={{ color: hex }}>section</span>{' '}
226
+ <span style={{ color: '#9803e2' }}>className</span>
227
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>=</span>
228
+ <span style={{ color: '#febc2e' }}>"hero"</span>
229
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>&gt;</span>
252
230
  </div>
253
231
  <div className="pl-12">
254
- <span style={{ color: "rgba(255,255,255,0.3)" }}>&lt;</span>
232
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>&lt;</span>
255
233
  <span style={{ color: hex }}>h1</span>
256
- <span style={{ color: "rgba(255,255,255,0.3)" }}>&gt;</span>
257
- <span style={{ color: "rgba(255,255,255,0.6)" }}>Convert.</span>
258
- <span style={{ color: "rgba(255,255,255,0.3)" }}>&lt;/</span>
234
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>&gt;</span>
235
+ <span style={{ color: 'rgba(255,255,255,0.6)' }}>Convert.</span>
236
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>&lt;/</span>
259
237
  <span style={{ color: hex }}>h1</span>
260
- <span style={{ color: "rgba(255,255,255,0.3)" }}>&gt;</span>
238
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>&gt;</span>
261
239
  </div>
262
240
  <div className="pl-8">
263
- <span style={{ color: "rgba(255,255,255,0.3)" }}>&lt;/</span>
241
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>&lt;/</span>
264
242
  <span style={{ color: hex }}>section</span>
265
- <span style={{ color: "rgba(255,255,255,0.3)" }}>&gt;</span>
243
+ <span style={{ color: 'rgba(255,255,255,0.3)' }}>&gt;</span>
266
244
  </div>
267
245
  <div className="pl-4">
268
- <span style={{ color: "rgba(255,255,255,0.4)" }}>)</span>
246
+ <span style={{ color: 'rgba(255,255,255,0.4)' }}>)</span>
269
247
  </div>
270
248
  <div>
271
- <span style={{ color: "rgba(255,255,255,0.4)" }}>{"}"}</span>
249
+ <span style={{ color: 'rgba(255,255,255,0.4)' }}>{'}'}</span>
272
250
  </div>
273
251
  <div className="mt-2 flex items-center gap-2">
274
- <span style={{ color: "rgba(255,255,255,0.2)" }}>{"// "}</span>
275
- <span
276
- className="inline-block w-2 h-4 animate-pulse"
277
- style={{ background: hex }}
278
- />
252
+ <span style={{ color: 'rgba(255,255,255,0.2)' }}>{'// '}</span>
253
+ <span className="inline-block w-2 h-4 animate-pulse" style={{ background: hex }} />
279
254
  </div>
280
255
  </div>
281
256
  </div>
282
257
  </div>
283
- );
258
+ )
284
259
  }
285
260
 
286
261
  function PipelineVisual() {
287
262
  const steps = [
288
- { label: "Build", status: "done", Icon: Hammer },
289
- { label: "Test", status: "done", Icon: ShieldCheck },
290
- { label: "Deploy", status: "active", Icon: Rocket },
291
- { label: "Live", status: "pending", Icon: Globe },
292
- ];
263
+ { label: 'Build', status: 'done', Icon: Hammer },
264
+ { label: 'Test', status: 'done', Icon: ShieldCheck },
265
+ { label: 'Deploy', status: 'active', Icon: Rocket },
266
+ { label: 'Live', status: 'pending', Icon: Globe },
267
+ ]
293
268
  return (
294
269
  <div className="relative w-full h-full flex items-center justify-center scale-80 md:scale-100">
295
270
  <div className="relative w-64">
@@ -299,17 +274,17 @@ function PipelineVisual() {
299
274
  className="w-9 h-9 rounded-full flex items-center justify-center shrink-0"
300
275
  style={{
301
276
  background:
302
- step.status === "done"
303
- ? "rgba(132,204,22,0.15)"
304
- : step.status === "active"
305
- ? "rgba(152,3,226,0.2)"
306
- : "rgba(255,255,255,0.04)",
277
+ step.status === 'done'
278
+ ? 'rgba(132,204,22,0.15)'
279
+ : step.status === 'active'
280
+ ? 'rgba(152,3,226,0.2)'
281
+ : 'rgba(255,255,255,0.04)',
307
282
  border: `1px solid ${
308
- step.status === "done"
309
- ? "rgba(132,204,22,0.4)"
310
- : step.status === "active"
311
- ? "rgba(152,3,226,0.5)"
312
- : "rgba(255,255,255,0.1)"
283
+ step.status === 'done'
284
+ ? 'rgba(132,204,22,0.4)'
285
+ : step.status === 'active'
286
+ ? 'rgba(152,3,226,0.5)'
287
+ : 'rgba(255,255,255,0.1)'
313
288
  }`,
314
289
  }}
315
290
  >
@@ -317,11 +292,11 @@ function PipelineVisual() {
317
292
  size={14}
318
293
  style={{
319
294
  color:
320
- step.status === "done"
321
- ? "#84cc16"
322
- : step.status === "active"
323
- ? "#9803e2"
324
- : "rgba(255,255,255,0.2)",
295
+ step.status === 'done'
296
+ ? '#84cc16'
297
+ : step.status === 'active'
298
+ ? '#9803e2'
299
+ : 'rgba(255,255,255,0.2)',
325
300
  }}
326
301
  />
327
302
  </div>
@@ -331,40 +306,29 @@ function PipelineVisual() {
331
306
  className="text-sm font-semibold"
332
307
  style={{
333
308
  color:
334
- step.status === "pending"
335
- ? "rgba(255,255,255,0.2)"
336
- : "rgba(255,255,255,0.8)",
309
+ step.status === 'pending' ? 'rgba(255,255,255,0.2)' : 'rgba(255,255,255,0.8)',
337
310
  fontFamily: "'Plus Jakarta Sans', sans-serif",
338
311
  }}
339
312
  >
340
313
  {step.label}
341
314
  </span>
342
- {step.status === "done" && (
343
- <Check size={12} style={{ color: "#84cc16" }} />
344
- )}
345
- {step.status === "active" && (
346
- <span
347
- className="text-xs animate-pulse"
348
- style={{ color: "#9803e2" }}
349
- >
315
+ {step.status === 'done' && <Check size={12} style={{ color: '#84cc16' }} />}
316
+ {step.status === 'active' && (
317
+ <span className="text-xs animate-pulse" style={{ color: '#9803e2' }}>
350
318
  Running…
351
319
  </span>
352
320
  )}
353
321
  </div>
354
322
  <div
355
323
  className="h-1 rounded-full mt-1.5"
356
- style={{ background: "rgba(255,255,255,0.06)" }}
324
+ style={{ background: 'rgba(255,255,255,0.06)' }}
357
325
  >
358
326
  <div
359
327
  className="h-full rounded-full"
360
328
  style={{
361
329
  width:
362
- step.status === "done"
363
- ? "100%"
364
- : step.status === "active"
365
- ? "60%"
366
- : "0%",
367
- background: step.status === "done" ? "#84cc16" : "#9803e2",
330
+ step.status === 'done' ? '100%' : step.status === 'active' ? '60%' : '0%',
331
+ background: step.status === 'done' ? '#84cc16' : '#9803e2',
368
332
  }}
369
333
  />
370
334
  </div>
@@ -373,17 +337,17 @@ function PipelineVisual() {
373
337
  ))}
374
338
  </div>
375
339
  </div>
376
- );
340
+ )
377
341
  }
378
342
 
379
343
  function FunnelVisual({ accent }: { accent: string }) {
380
- const { hex, rgb } = getAccent(accent);
344
+ const { hex, rgb } = getAccent(accent)
381
345
  const rows = [
382
- { label: "Visitors", value: "24,800", pct: 100 },
383
- { label: "Engaged", value: "11,200", pct: 65 },
384
- { label: "Leads", value: "3,840", pct: 40 },
385
- { label: "Converted", value: "960", pct: 22 },
386
- ];
346
+ { label: 'Visitors', value: '24,800', pct: 100 },
347
+ { label: 'Engaged', value: '11,200', pct: 65 },
348
+ { label: 'Leads', value: '3,840', pct: 40 },
349
+ { label: 'Converted', value: '960', pct: 22 },
350
+ ]
387
351
  return (
388
352
  <div className="relative w-full h-full flex items-center justify-center scale-80 md:scale-100">
389
353
  <div className="relative w-64 space-y-3">
@@ -393,17 +357,10 @@ function FunnelVisual({ accent }: { accent: string }) {
393
357
  className="flex justify-between text-xs mb-1"
394
358
  style={{ fontFamily: "'Plus Jakarta Sans', sans-serif" }}
395
359
  >
396
- <span style={{ color: "rgba(255,255,255,0.4)" }}>
397
- {row.label}
398
- </span>
399
- <span style={{ color: "rgba(255,255,255,0.7)" }}>
400
- {row.value}
401
- </span>
360
+ <span style={{ color: 'rgba(255,255,255,0.4)' }}>{row.label}</span>
361
+ <span style={{ color: 'rgba(255,255,255,0.7)' }}>{row.value}</span>
402
362
  </div>
403
- <div
404
- className="h-2 rounded-full"
405
- style={{ background: "rgba(255,255,255,0.06)" }}
406
- >
363
+ <div className="h-2 rounded-full" style={{ background: 'rgba(255,255,255,0.06)' }}>
407
364
  <div
408
365
  className="h-full rounded-full"
409
366
  style={{ width: `${row.pct}%`, background: hex }}
@@ -432,7 +389,7 @@ function FunnelVisual({ accent }: { accent: string }) {
432
389
  <div
433
390
  className="text-xs"
434
391
  style={{
435
- color: "rgba(255,255,255,0.3)",
392
+ color: 'rgba(255,255,255,0.3)',
436
393
  fontFamily: "'Plus Jakarta Sans', sans-serif",
437
394
  }}
438
395
  >
@@ -442,33 +399,33 @@ function FunnelVisual({ accent }: { accent: string }) {
442
399
  </div>
443
400
  </div>
444
401
  </div>
445
- );
402
+ )
446
403
  }
447
404
 
448
405
  function AutomationVisual() {
449
406
  const flows = [
450
407
  {
451
- from: "New Lead",
452
- to: "CRM",
408
+ from: 'New Lead',
409
+ to: 'CRM',
453
410
  Icon: UserPlus,
454
- color: "#9803e2",
455
- rgb: "152,3,226",
411
+ color: '#9803e2',
412
+ rgb: '152,3,226',
456
413
  },
457
414
  {
458
- from: "CRM",
459
- to: "Email Seq.",
415
+ from: 'CRM',
416
+ to: 'Email Seq.',
460
417
  Icon: Send,
461
- color: "#84cc16",
462
- rgb: "132,204,22",
418
+ color: '#84cc16',
419
+ rgb: '132,204,22',
463
420
  },
464
421
  {
465
- from: "Email Seq.",
466
- to: "Sales Team",
422
+ from: 'Email Seq.',
423
+ to: 'Sales Team',
467
424
  Icon: Users,
468
- color: "#9803e2",
469
- rgb: "152,3,226",
425
+ color: '#9803e2',
426
+ rgb: '152,3,226',
470
427
  },
471
- ];
428
+ ]
472
429
  return (
473
430
  <div className="relative w-full h-full flex items-center justify-center scale-80 md:scale-100">
474
431
  <div className="relative w-64">
@@ -477,19 +434,16 @@ function AutomationVisual() {
477
434
  <div
478
435
  className="px-3 py-2 rounded-lg text-xs font-semibold whitespace-nowrap"
479
436
  style={{
480
- background: "rgba(255,255,255,0.05)",
481
- border: "1px solid rgba(255,255,255,0.1)",
482
- color: "rgba(255,255,255,0.6)",
437
+ background: 'rgba(255,255,255,0.05)',
438
+ border: '1px solid rgba(255,255,255,0.1)',
439
+ color: 'rgba(255,255,255,0.6)',
483
440
  fontFamily: "'Plus Jakarta Sans', sans-serif",
484
441
  }}
485
442
  >
486
443
  {flow.from}
487
444
  </div>
488
445
  <div className="flex-1 flex items-center gap-1">
489
- <div
490
- className="flex-1 h-px"
491
- style={{ background: `rgba(${flow.rgb},0.4)` }}
492
- />
446
+ <div className="flex-1 h-px" style={{ background: `rgba(${flow.rgb},0.4)` }} />
493
447
  <div
494
448
  className="w-6 h-6 rounded-full flex items-center justify-center"
495
449
  style={{
@@ -499,10 +453,7 @@ function AutomationVisual() {
499
453
  >
500
454
  <flow.Icon size={10} style={{ color: flow.color }} />
501
455
  </div>
502
- <div
503
- className="flex-1 h-px"
504
- style={{ background: `rgba(${flow.rgb},0.4)` }}
505
- />
456
+ <div className="flex-1 h-px" style={{ background: `rgba(${flow.rgb},0.4)` }} />
506
457
  </div>
507
458
  <div
508
459
  className="px-3 py-2 rounded-lg text-xs font-semibold whitespace-nowrap"
@@ -520,14 +471,14 @@ function AutomationVisual() {
520
471
  <div
521
472
  className="mt-2 p-3 rounded-xl text-center"
522
473
  style={{
523
- background: "rgba(152,3,226,0.08)",
524
- border: "1px solid rgba(152,3,226,0.2)",
474
+ background: 'rgba(152,3,226,0.08)',
475
+ border: '1px solid rgba(152,3,226,0.2)',
525
476
  }}
526
477
  >
527
478
  <div
528
479
  className="text-xs font-bold flex items-center justify-center gap-1"
529
480
  style={{
530
- color: "#9803e2",
481
+ color: '#9803e2',
531
482
  fontFamily: "'Plus Jakarta Sans', sans-serif",
532
483
  }}
533
484
  >
@@ -537,7 +488,7 @@ function AutomationVisual() {
537
488
  </div>
538
489
  </div>
539
490
  </div>
540
- );
491
+ )
541
492
  }
542
493
 
543
494
  const VISUAL_MAP: Record<string, React.ComponentType<{ accent: string }>> = {
@@ -546,47 +497,43 @@ const VISUAL_MAP: Record<string, React.ComponentType<{ accent: string }>> = {
546
497
  pipeline: PipelineVisual as React.ComponentType<{ accent: string }>,
547
498
  funnel: FunnelVisual,
548
499
  automation: AutomationVisual as React.ComponentType<{ accent: string }>,
549
- };
500
+ }
550
501
 
551
502
  /* ── Slide panel ────────────────────────────────────────────────── */
552
503
 
553
504
  interface SlidePanelProps {
554
- slide: SlideItem;
555
- index: number;
556
- total: number;
557
- panelRef: (el: HTMLDivElement | null) => void;
505
+ slide: SlideItem
506
+ index: number
507
+ total: number
508
+ panelRef: (el: HTMLDivElement | null) => void
558
509
  }
559
510
 
560
511
  function isLocalMediaSrc(src?: string | null): src is string {
561
- return typeof src === "string" && src.startsWith("/api/media/file/");
512
+ return typeof src === 'string' && src.startsWith('/api/media/file/')
562
513
  }
563
514
 
564
515
  function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
565
- const { hex, rgb } = getAccent(slide.accent);
566
- const Visual = VISUAL_MAP[slide.visualType ?? "wireframe"] ?? WireframeVisual;
516
+ const { hex, rgb } = getAccent(slide.accent)
517
+ const Visual = VISUAL_MAP[slide.visualType ?? 'wireframe'] ?? WireframeVisual
567
518
 
568
519
  // Extract media URL safely
569
520
  const mediaUrl: string =
570
- typeof slide.customMedia === "string"
571
- ? slide.customMedia
572
- : (slide.customMedia?.url ?? "");
573
- const safeMediaUrl = isLocalMediaSrc(mediaUrl) ? mediaUrl : "";
521
+ typeof slide.customMedia === 'string' ? slide.customMedia : (slide.customMedia?.url ?? '')
522
+ const safeMediaUrl = isLocalMediaSrc(mediaUrl) ? mediaUrl : ''
574
523
  const mediaAlt =
575
- slide.customMedia && typeof slide.customMedia !== "string"
576
- ? (slide.customMedia.alt ?? "")
577
- : "";
524
+ slide.customMedia && typeof slide.customMedia !== 'string' ? (slide.customMedia.alt ?? '') : ''
578
525
 
579
526
  return (
580
527
  <div
581
528
  ref={panelRef}
582
529
  className={`shs-slide shs-slide--${index} relative shrink-0 h-full flex items-center overflow-hidden`}
583
- style={{ width: "100vw" }}
530
+ style={{ width: '100vw' }}
584
531
  >
585
532
  {/* Watermark number */}
586
533
  <div
587
534
  className="absolute top-1/2 -translate-y-1/2 right-16 font-black select-none pointer-events-none text-stone-200/40 dark:text-stone-200/5"
588
535
  style={{
589
- fontSize: "clamp(120px, 18vw, 220px)",
536
+ fontSize: 'clamp(120px, 18vw, 220px)',
590
537
 
591
538
  lineHeight: 1,
592
539
  }}
@@ -601,7 +548,7 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
601
548
  className="absolute right-0 top-1/4 bottom-1/4 w-px"
602
549
  style={{
603
550
  background:
604
- "linear-gradient(to bottom, transparent, rgba(255,255,255,0.08), transparent)",
551
+ 'linear-gradient(to bottom, transparent, rgba(255,255,255,0.08), transparent)',
605
552
  }}
606
553
  />
607
554
  )}
@@ -610,7 +557,7 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
610
557
  {/* ── Left: content ── */}
611
558
  <div className="space-y-2 md:space-y-7">
612
559
  {/* Phase badge */}
613
- <div className="anim-el flex items-center gap-3">
560
+ <div className="anim-el flex items-center justify-end gap-3">
614
561
  <div
615
562
  className="w-7 h-7 md:w-8 md:h-8 rounded-full flex items-center justify-center"
616
563
  style={{
@@ -634,15 +581,13 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
634
581
  <h2
635
582
  className="anim-el font-extrabold leading-none tracking-tight text-stone-900 dark:text-stone-50"
636
583
  style={{
637
- fontSize: "clamp(42px, 5vw, 72px)",
584
+ fontSize: 'clamp(42px, 5vw, 72px)',
638
585
 
639
- whiteSpace: "pre-line",
586
+ whiteSpace: 'pre-line',
640
587
  }}
641
588
  >
642
- {" "}
643
- <span className="relative">
644
- {(slide.headline ?? "").replace(/\\n/g, "\n")}
645
- </span>
589
+ {' '}
590
+ <span className="relative">{(slide.headline ?? '').replace(/\\n/g, '\n')}</span>
646
591
  </h2>
647
592
 
648
593
  {/* Sub */}
@@ -653,7 +598,7 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
653
598
  color: hex,
654
599
  }}
655
600
  >
656
- {" "}
601
+ {' '}
657
602
  <span className="relative">{slide.sub}</span>
658
603
  </p>
659
604
  )}
@@ -666,7 +611,7 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
666
611
  // fontFamily: "'Plus Jakarta Sans', sans-serif",
667
612
  // }}
668
613
  >
669
- {" "}
614
+ {' '}
670
615
  <span className="relative"> {slide.body}</span>
671
616
  </p>
672
617
  )}
@@ -710,7 +655,7 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
710
655
  <div className="relative z-10 w-full h-full">
711
656
  {slide.customMedia && safeMediaUrl ? (
712
657
  <>
713
- {slide.customMediaType === "image" && (
658
+ {slide.customMediaType === 'image' && (
714
659
  <div className="relative w-full h-full bg-dots">
715
660
  <Image
716
661
  src={safeMediaUrl}
@@ -723,7 +668,7 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
723
668
  />
724
669
  </div>
725
670
  )}
726
- {slide.customMediaType === "video" && (
671
+ {slide.customMediaType === 'video' && (
727
672
  <video
728
673
  src={safeMediaUrl}
729
674
  autoPlay
@@ -732,10 +677,10 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
732
677
  playsInline
733
678
  preload="metadata"
734
679
  className="w-full h-full object-cover bg-dots"
735
- aria-label={mediaAlt || "Video content"}
680
+ aria-label={mediaAlt || 'Video content'}
736
681
  />
737
682
  )}
738
- {slide.customMediaType === "lottie" && (
683
+ {slide.customMediaType === 'lottie' && (
739
684
  <DotLottieReact
740
685
  src={safeMediaUrl}
741
686
  loop
@@ -745,123 +690,123 @@ function SlidePanel({ slide, index, total, panelRef }: SlidePanelProps) {
745
690
  )}
746
691
  </>
747
692
  ) : (
748
- <Visual accent={slide.accent ?? "lime"} />
693
+ <Visual accent={slide.accent ?? 'lime'} />
749
694
  )}
750
695
  </div>
751
696
  </div>
752
697
  </div>
753
698
  </div>
754
- );
699
+ )
755
700
  }
756
701
 
757
702
  /* ── Main component ─────────────────────────────────────────────── */
758
703
 
759
704
  export function DDHorizontalScroll(props: DDHorizontalScrollProps) {
760
- const { sectionLabel, slides, sectionId, enableCustomCSS, customCSS } = props;
705
+ const { sectionLabel, slides, sectionId, enableCustomCSS, customCSS } = props
761
706
 
762
- const outerRef = useRef<HTMLElement>(null);
763
- const wrapperRef = useRef<HTMLDivElement>(null);
764
- const trackRef = useRef<HTMLDivElement>(null);
765
- const slideRefs = useRef<(HTMLDivElement | null)[]>([]);
766
- const activeSlideRef = useRef<number>(-1);
707
+ const outerRef = useRef<HTMLElement>(null)
708
+ const wrapperRef = useRef<HTMLDivElement>(null)
709
+ const trackRef = useRef<HTMLDivElement>(null)
710
+ const slideRefs = useRef<(HTMLDivElement | null)[]>([])
711
+ const activeSlideRef = useRef<number>(-1)
767
712
 
768
713
  // Memoize normalized slides to avoid unnecessary re-renders
769
- const normalSlides: SlideItem[] = useMemo(() => slides ?? [], [slides]);
770
- const slideCount = normalSlides.length;
714
+ const normalSlides: SlideItem[] = useMemo(() => slides ?? [], [slides])
715
+ const slideCount = normalSlides.length
771
716
 
772
717
  /* ── GSAP horizontal scroll + pin + snap ── */
773
718
  useLayoutEffect(() => {
774
- if (typeof window === "undefined") return;
775
- if (slideCount === 0) return;
719
+ if (typeof window === 'undefined') return
720
+ if (slideCount === 0) return
776
721
 
777
722
  // Store ctx outside the timer so cleanup always reaches it
778
- let ctx: ReturnType<typeof gsap.context> | null = null;
779
- let initTimer: ReturnType<typeof setTimeout>;
780
- let resizeTimer: ReturnType<typeof setTimeout>;
781
- let rafId: number;
723
+ let ctx: ReturnType<typeof gsap.context> | null = null
724
+ let initTimer: ReturnType<typeof setTimeout>
725
+ let resizeTimer: ReturnType<typeof setTimeout>
726
+ let rafId: number
782
727
 
783
728
  const init = () => {
784
- const outerEl = outerRef.current;
785
- const wrapper = wrapperRef.current;
786
- const track = trackRef.current;
787
- if (!outerEl || !wrapper || !track) return;
729
+ const outerEl = outerRef.current
730
+ const wrapper = wrapperRef.current
731
+ const track = trackRef.current
732
+ if (!outerEl || !wrapper || !track) return
788
733
 
789
734
  // Kill any stale ScrollTriggers on this element (e.g. from Strict-Mode double mount)
790
735
  ScrollTrigger.getAll()
791
736
  .filter((st) => st.vars.trigger === outerEl)
792
- .forEach((st) => st.kill());
737
+ .forEach((st) => st.kill())
793
738
 
794
739
  ctx = gsap.context(() => {
795
740
  ScrollTrigger.create({
796
741
  trigger: outerEl,
797
- start: "top top",
742
+ start: 'top top',
798
743
  end: () => `+=${(slideCount - 1) * window.innerWidth}`,
799
744
  scrub: 0.5,
800
745
  invalidateOnRefresh: true,
801
746
  onUpdate: (self) => {
802
747
  gsap.set(track, {
803
748
  x: -(slideCount - 1) * window.innerWidth * self.progress,
804
- });
749
+ })
805
750
 
806
- const newActive = Math.round(self.progress * (slideCount - 1));
751
+ const newActive = Math.round(self.progress * (slideCount - 1))
807
752
  if (newActive !== activeSlideRef.current) {
808
- activeSlideRef.current = newActive;
809
- animateSlideIn(slideRefs.current[newActive]);
753
+ activeSlideRef.current = newActive
754
+ animateSlideIn(slideRefs.current[newActive])
810
755
  }
811
756
  },
812
- });
813
- }, outerEl);
757
+ })
758
+ }, outerEl)
814
759
 
815
760
  // Animate first slide in on mount
816
- animateSlideIn(slideRefs.current[1]);
817
- activeSlideRef.current = 1;
761
+ animateSlideIn(slideRefs.current[1])
762
+ activeSlideRef.current = 1
818
763
 
819
764
  // Refresh so ScrollTrigger measures the fully-painted layout
820
- ScrollTrigger.refresh();
821
- };
765
+ ScrollTrigger.refresh()
766
+ }
822
767
 
823
768
  // Double rAF inside a timeout: waits for SSR hydration, LocomotiveScroll init
824
769
  // and web-font paint before measuring. 200 ms covers the loco 100 ms delay too.
825
770
  initTimer = setTimeout(() => {
826
771
  rafId = requestAnimationFrame(() => {
827
- requestAnimationFrame(init);
828
- });
829
- }, 200);
772
+ requestAnimationFrame(init)
773
+ })
774
+ }, 200)
830
775
 
831
776
  // Debounced resize — recalculates pin length and slide positions
832
777
  const onResize = () => {
833
- clearTimeout(resizeTimer);
778
+ clearTimeout(resizeTimer)
834
779
  resizeTimer = setTimeout(() => {
835
- ScrollTrigger.refresh(true);
836
- }, 150);
837
- };
780
+ ScrollTrigger.refresh(true)
781
+ }, 150)
782
+ }
838
783
 
839
- window.addEventListener("resize", onResize);
784
+ window.addEventListener('resize', onResize)
840
785
  // Also respond to locomotive:resize dispatched by PageClient
841
- window.addEventListener("locomotive:resize", onResize);
786
+ window.addEventListener('locomotive:resize', onResize)
842
787
 
843
788
  return () => {
844
- clearTimeout(initTimer);
845
- clearTimeout(resizeTimer);
846
- cancelAnimationFrame(rafId);
847
- window.removeEventListener("resize", onResize);
848
- window.removeEventListener("locomotive:resize", onResize);
849
- ctx?.revert();
850
- ctx = null;
851
- };
852
- }, [slideCount, sectionId]);
789
+ clearTimeout(initTimer)
790
+ clearTimeout(resizeTimer)
791
+ cancelAnimationFrame(rafId)
792
+ window.removeEventListener('resize', onResize)
793
+ window.removeEventListener('locomotive:resize', onResize)
794
+ ctx?.revert()
795
+ ctx = null
796
+ }
797
+ }, [slideCount, sectionId])
853
798
 
854
799
  // offset next block
855
800
  const getOffset = () => {
856
- if (typeof window === "undefined") return 25;
801
+ if (typeof window === 'undefined') return 25
857
802
 
858
- if (window.matchMedia("(max-width: 420px)").matches) return 550;
859
- if (window.matchMedia("(max-width: 768px)").matches) return 300;
803
+ if (window.matchMedia('(max-width: 420px)').matches) return 550
804
+ if (window.matchMedia('(max-width: 768px)').matches) return 300
860
805
 
861
- return 25;
862
- };
806
+ return 25
807
+ }
863
808
 
864
- const offset = getOffset();
809
+ const offset = getOffset()
865
810
  return (
866
811
  <section
867
812
  ref={outerRef}
@@ -876,7 +821,7 @@ export function DDHorizontalScroll(props: DDHorizontalScrollProps) {
876
821
  ref={wrapperRef}
877
822
  className="shs-wrapper sticky overflow-hidden top-0"
878
823
  style={{
879
- height: "100svh",
824
+ height: '100svh',
880
825
  }}
881
826
  >
882
827
  {/* Custom CSS */}
@@ -885,11 +830,10 @@ export function DDHorizontalScroll(props: DDHorizontalScrollProps) {
885
830
  dangerouslySetInnerHTML={{
886
831
  __html: sectionId
887
832
  ? customCSS.replace(/:host/g, `#${sectionId}`)
888
- : customCSS.replace(/:host/g, ".shs-wrapper"),
833
+ : customCSS.replace(/:host/g, '.shs-wrapper'),
889
834
  }}
890
835
  />
891
836
  )}
892
-
893
837
  {/* Subtle grid overlay */}
894
838
  {/* <div
895
839
  className="absolute inset-0 pointer-events-none z-0"
@@ -901,38 +845,36 @@ export function DDHorizontalScroll(props: DDHorizontalScrollProps) {
901
845
  backgroundSize: '60px 60px'
902
846
  }}
903
847
  /> */}
904
-
905
848
  {/* Section eyebrow label */}
849
+ #BlockName-DDHorizontalScroll
906
850
  {sectionLabel && (
907
- <div className="section-label absolute top-16 left-4 z-20 flex items-center gap-3">
908
- <div className="w-6 h-px" style={{ background: "#84cc16" }} />
851
+ <div className="section-label absolute top-8 left-4 z-20 flex items-center gap-3">
852
+ <div className="w-6 h-px" style={{ background: '#84cc16' }} />
909
853
  <span
910
854
  className="text-[11.5px] font-semibold tracking-wide uppercase"
911
855
  style={{
912
- color: "#84cc16",
856
+ color: '#84cc16',
913
857
  }}
914
858
  >
915
859
  {sectionLabel}
916
860
  </span>
917
861
  </div>
918
862
  )}
919
-
920
863
  {/* Slide dot counter */}
921
864
  <div className="slide-counter absolute top-16 right-6 z-20 flex items-center gap-2">
922
865
  {normalSlides.map((_, i) => (
923
866
  <div
924
867
  key={i}
925
868
  className="w-1.5 h-1.5 rounded-full hidden"
926
- style={{ background: "rgba(255,255,255,0.2)" }}
869
+ style={{ background: 'rgba(255,255,255,0.2)' }}
927
870
  />
928
871
  ))}
929
872
  </div>
930
-
931
873
  {/* Horizontal track */}
932
874
  <div
933
875
  ref={trackRef}
934
876
  className="absolute top-0 left-0 flex h-full"
935
- style={{ willChange: "transform" }}
877
+ style={{ willChange: 'transform' }}
936
878
  >
937
879
  {normalSlides.map((slide, i) => (
938
880
  <SlidePanel
@@ -941,12 +883,11 @@ export function DDHorizontalScroll(props: DDHorizontalScrollProps) {
941
883
  index={i}
942
884
  total={normalSlides.length - 1}
943
885
  panelRef={(el) => {
944
- slideRefs.current[i] = el;
886
+ slideRefs.current[i] = el
945
887
  }}
946
888
  />
947
889
  ))}
948
890
  </div>
949
-
950
891
  {/* Scroll hint */}
951
892
  {/* <div
952
893
  className="scroll-hint absolute bottom-16 left-1/2 -translate-x-1/2 z-20 flex flex-col items-center gap-2"
@@ -970,15 +911,15 @@ export function DDHorizontalScroll(props: DDHorizontalScrollProps) {
970
911
  </div> */}
971
912
  </div>
972
913
  </section>
973
- );
914
+ )
974
915
  }
975
916
 
976
917
  /* ── Entrance animation helper ──────────────────────────────────── */
977
918
 
978
919
  function animateSlideIn(el: HTMLDivElement | null) {
979
- if (!el) return;
980
- const animEls = el.querySelectorAll<HTMLElement>(".anim-el");
981
- if (!animEls.length) return;
920
+ if (!el) return
921
+ const animEls = el.querySelectorAll<HTMLElement>('.anim-el')
922
+ if (!animEls.length) return
982
923
  gsap.fromTo(
983
924
  animEls,
984
925
  { opacity: 1, y: 0 },
@@ -987,9 +928,9 @@ function animateSlideIn(el: HTMLDivElement | null) {
987
928
  y: 0,
988
929
  stagger: 0.07,
989
930
  duration: 0.55,
990
- ease: "power4.out",
931
+ ease: 'power4.out',
991
932
  overwrite: true,
992
- clearProps: "transform",
933
+ clearProps: 'transform',
993
934
  },
994
- );
935
+ )
995
936
  }