blocks-dusted 0.1.12 → 0.1.13

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 (148) hide show
  1. package/package.json +1 -1
  2. package/src/installers/patchRenderBlocks.js +1 -1
  3. package/src/registry/validateTemplateManifest.js +2 -1
  4. package/templates/blocks/BannerDD790432/Component.tsx +63 -0
  5. package/templates/blocks/BannerDD790432/config.ts +54 -0
  6. package/templates/blocks/BannerDD790432/manifest.json +80 -0
  7. package/templates/blocks/Button47/Component.tsx +124 -0
  8. package/templates/blocks/Button47/config.ts +75 -0
  9. package/templates/blocks/Button47/link.ts +22 -0
  10. package/templates/blocks/Button47/manifest.json +105 -0
  11. package/templates/blocks/Button47/styles.module.scss +75 -0
  12. package/templates/blocks/DD-Accordion/Component.tsx +55 -36
  13. package/templates/blocks/DD-Accordion/config.ts +71 -59
  14. package/templates/blocks/DD-AnimText/Component.tsx +50 -32
  15. package/templates/blocks/DD-AnimText/config.ts +296 -295
  16. package/templates/blocks/DD-AsideNav/Component.tsx +323 -319
  17. package/templates/blocks/DD-Banner/Component.tsx +54 -51
  18. package/templates/blocks/DD-BookCall/Component.tsx +535 -535
  19. package/templates/blocks/DD-ButtonPop/Component.tsx +118 -117
  20. package/templates/blocks/DD-Card/Component.tsx +4 -4
  21. package/templates/blocks/DD-CardTemplate01/Component.tsx +139 -140
  22. package/templates/blocks/DD-CardsWithVariantV3/Component.tsx +472 -0
  23. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/index.tsx +203 -0
  24. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/styles.ts +87 -0
  25. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/useInjectStyle.ts +39 -0
  26. package/templates/blocks/DD-CardsWithVariantV3/config.ts +326 -0
  27. package/templates/blocks/DD-CardsWithVariantV3/manifest.json +110 -0
  28. package/templates/blocks/DD-Carousel-A/Component.tsx +251 -249
  29. package/templates/blocks/DD-Carousel-B/Component.tsx +405 -403
  30. package/templates/blocks/DD-ComparisonTable/Component.tsx +255 -256
  31. package/templates/blocks/DD-Contact/Component.tsx +433 -435
  32. package/templates/blocks/DD-Counter/Component.tsx +11 -6
  33. package/templates/blocks/DD-DeliCard/Component.tsx +303 -0
  34. package/templates/blocks/DD-DeliCard/config.ts +186 -0
  35. package/templates/blocks/DD-DeliCard/manifest.json +94 -0
  36. package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
  37. package/templates/blocks/DD-FeatProd/CircularGallery.tsx +1179 -0
  38. package/templates/blocks/DD-FeatProd/Component.tsx +484 -0
  39. package/templates/blocks/DD-FeatProd/config.ts +173 -0
  40. package/templates/blocks/DD-FeatProd/manifest.json +93 -0
  41. package/templates/blocks/DD-FeatServ/Component.tsx +607 -0
  42. package/templates/blocks/DD-FeatServ/config.ts +213 -0
  43. package/templates/blocks/DD-FeatServ/manifest.json +113 -0
  44. package/templates/blocks/DD-FeatStrip/Component.tsx +584 -585
  45. package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +627 -628
  46. package/templates/blocks/DD-FluidBtn/Component.tsx +46 -43
  47. package/templates/blocks/DD-GooeyInfo/Component.tsx +150 -149
  48. package/templates/blocks/DD-GooeyInfo/config.ts +136 -135
  49. package/templates/blocks/DD-Hero/Component.tsx +296 -297
  50. package/templates/blocks/DD-HeroPlas/Component.tsx +50 -0
  51. package/templates/blocks/DD-HeroPlas/ThreeScene.tsx +109 -0
  52. package/templates/blocks/DD-HeroPlas/config.ts +33 -0
  53. package/templates/blocks/DD-HeroPlas/manifest.json +88 -0
  54. package/templates/blocks/DD-HorizontalScroll/Component.tsx +937 -936
  55. package/templates/blocks/DD-Icon/Component.tsx +46 -81
  56. package/templates/blocks/DD-IconButton/Component.tsx +13 -20
  57. package/templates/blocks/DD-IdTag/Component.tsx +22 -21
  58. package/templates/blocks/DD-LineAnim/Component.tsx +42 -36
  59. package/templates/blocks/DD-LogoMarq/Component.tsx +38 -48
  60. package/templates/blocks/DD-Lottie/Component.tsx +11 -3
  61. package/templates/blocks/DD-MasonaryMedia/Component.tsx +544 -545
  62. package/templates/blocks/DD-MasonaryMedia/config.ts +389 -389
  63. package/templates/blocks/DD-Masonry/Component.tsx +654 -644
  64. package/templates/blocks/DD-Masonry/config.ts +475 -475
  65. package/templates/blocks/DD-MaterialSwatches/Component.tsx +85 -0
  66. package/templates/blocks/DD-MaterialSwatches/config.ts +232 -0
  67. package/templates/blocks/DD-MaterialSwatches/manifest.json +84 -0
  68. package/templates/blocks/DD-MaterialSwatches/material.ts +107 -0
  69. package/templates/blocks/DD-NineCanvas/Component.tsx +129 -0
  70. package/templates/blocks/DD-NineCanvas/RenderBlocks.tsx +244 -0
  71. package/templates/blocks/DD-NineCanvas/allowedCanvasBlocks.ts +6 -0
  72. package/templates/blocks/DD-NineCanvas/blockComponents.tsx +8 -0
  73. package/templates/blocks/DD-NineCanvas/canvas/CanvasGrid.tsx +316 -0
  74. package/templates/blocks/DD-NineCanvas/canvas/RadialNav.tsx +432 -0
  75. package/templates/blocks/DD-NineCanvas/canvas/canvas-panels.css +889 -0
  76. package/templates/blocks/DD-NineCanvas/canvas/icons/custom-icons.tsx +364 -0
  77. package/templates/blocks/DD-NineCanvas/config.ts +61 -0
  78. package/templates/blocks/DD-NineCanvas/manifest.json +165 -0
  79. package/templates/blocks/DD-NineCanvas/styles.scss +152 -0
  80. package/templates/blocks/DD-NineCanvas/tooltip/get-strict-context.tsx +26 -0
  81. package/templates/blocks/DD-NineCanvas/tooltip/primitives.tsx +178 -0
  82. package/templates/blocks/DD-NineCanvas/tooltip/tooltip.tsx +71 -0
  83. package/templates/blocks/DD-NineCanvas/tooltip/use-controlled-state.tsx +33 -0
  84. package/templates/blocks/DD-NineCanvas/utilities/FadeOnScroll.tsx +68 -0
  85. package/templates/blocks/DD-NineCanvas/utilities/ParallaxWrapper.tsx +157 -0
  86. package/templates/blocks/DD-NineCanvas/utilities/renderCustomCSS.ts +5 -0
  87. package/templates/blocks/DD-NineCanvas/utilities/webglCleanup.ts +27 -0
  88. package/templates/blocks/DD-PayloadAIAssistant/Component.tsx +78 -0
  89. package/templates/blocks/DD-PayloadAIAssistant/DustedBot.tsx +590 -0
  90. package/templates/blocks/DD-PayloadAIAssistant/config.ts +37 -0
  91. package/templates/blocks/DD-PayloadAIAssistant/demo.css +125 -0
  92. package/templates/blocks/DD-PayloadAIAssistant/manifest.json +93 -0
  93. package/templates/blocks/DD-PayloadAIAssistant/source-defaults.ts +17 -0
  94. package/templates/blocks/DD-PayloadAIAssistant/styles.ts +306 -0
  95. package/templates/blocks/DD-Pricing/Component.tsx +2 -3
  96. package/templates/blocks/DD-Pricing/config.ts +215 -215
  97. package/templates/blocks/DD-Process/Component.tsx +2 -2
  98. package/templates/blocks/DD-ProjHeader/Component.tsx +93 -91
  99. package/templates/blocks/DD-ProjSlider/Component.tsx +1 -1
  100. package/templates/blocks/DD-Section/Component.tsx +265 -266
  101. package/templates/blocks/DD-Separator/Component.tsx +66 -66
  102. package/templates/blocks/DD-Services/Component.tsx +176 -176
  103. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +306 -307
  104. package/templates/blocks/DD-Slider-A/Component.tsx +2 -2
  105. package/templates/blocks/DD-StackCards/Component.tsx +172 -171
  106. package/templates/blocks/DD-Team/Component.tsx +247 -247
  107. package/templates/blocks/DD-TeamCards/Component.tsx +251 -251
  108. package/templates/blocks/DD-TechStack/Component.tsx +173 -174
  109. package/templates/blocks/DD-TestMarq/Component.tsx +158 -132
  110. package/templates/blocks/DD-Testim/Component.tsx +247 -247
  111. package/templates/blocks/DD-Testim2/Component.tsx +265 -256
  112. package/templates/blocks/DD-Testimonials/Component.tsx +139 -139
  113. package/templates/blocks/DD-Thanks/Component.tsx +1 -1
  114. package/templates/blocks/DD-Work/Component.tsx +318 -318
  115. package/templates/blocks/DD-Work-B/Component.tsx +294 -294
  116. package/templates/blocks/DDHG-CTASection2/Component.tsx +183 -184
  117. package/templates/blocks/DDHG-HighlightArc/Component.tsx +6 -3
  118. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +45 -8
  119. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +1 -3
  120. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +9 -22
  121. package/templates/blocks/DDHG-ServiceHero/Component.tsx +10 -10
  122. package/templates/blocks/DDHG-TeamCards/Component.tsx +6 -35
  123. package/templates/blocks/Form/Checkbox/index.tsx +45 -0
  124. package/templates/blocks/Form/Component.tsx +163 -0
  125. package/templates/blocks/Form/Country/index.tsx +65 -0
  126. package/templates/blocks/Form/Country/options.ts +982 -0
  127. package/templates/blocks/Form/Email/index.tsx +38 -0
  128. package/templates/blocks/Form/Error/index.tsx +15 -0
  129. package/templates/blocks/Form/Message/index.tsx +13 -0
  130. package/templates/blocks/Form/Number/index.tsx +36 -0
  131. package/templates/blocks/Form/Select/index.tsx +63 -0
  132. package/templates/blocks/Form/State/index.tsx +64 -0
  133. package/templates/blocks/Form/State/options.ts +52 -0
  134. package/templates/blocks/Form/Text/index.tsx +32 -0
  135. package/templates/blocks/Form/Textarea/index.tsx +40 -0
  136. package/templates/blocks/Form/Width/index.tsx +13 -0
  137. package/templates/blocks/Form/config.ts +51 -0
  138. package/templates/blocks/Form/fields.tsx +21 -0
  139. package/templates/blocks/Form/manifest.json +168 -0
  140. package/templates/blocks/MediaBlock/Component.tsx +67 -0
  141. package/templates/blocks/MediaBlock/config.ts +14 -0
  142. package/templates/blocks/MediaBlock/manifest.json +92 -0
  143. package/templates/blocks/TransitionsDustedShowcase/Component.tsx +9 -0
  144. package/templates/blocks/TransitionsDustedShowcase/config.ts +13 -0
  145. package/templates/blocks/TransitionsDustedShowcase/manifest.json +80 -0
  146. package/templates/blocks/rawHtml/Component.tsx +25 -0
  147. package/templates/blocks/rawHtml/config.ts +56 -0
  148. package/templates/blocks/rawHtml/manifest.json +80 -0
@@ -1,185 +1,184 @@
1
- 'use client'
2
-
3
- import { motion } from 'framer-motion'
4
- import React from 'react'
5
-
6
- import { cn } from '@/utilities/ui'
7
-
8
- type PortableDoc = {
9
- slug?: string | null
10
- breadcrumbs?: Array<{ url?: string | null }> | null
11
- [key: string]: unknown
12
- }
13
-
14
- type LinkReference =
15
- | { relationTo: 'pages'; value: number | PortableDoc }
16
- | { relationTo: 'posts'; value: number | PortableDoc }
17
- | { relationTo: 'projects'; value: number | PortableDoc }
18
- | { relationTo: 'thoughts'; value: number | PortableDoc }
19
-
20
- type BlockLinkField = {
21
- type?: 'reference' | 'custom' | null
22
- reference?: LinkReference | null
23
- url?: string | null
24
- label?: string | null
25
- newTab?: boolean | null
26
- } | null
27
-
28
- export type DDHGCTASection2Props = {
29
- sectionId?: string | null
30
- customClassName?: string | null
31
- customCSS?: string | null
32
- blockId?: string | null
33
- scrollSpeed?: string | number | null
34
- eyebrowPrefix?: string | null
35
- eyebrowEmphasis?: string | null
36
- headingLineOne?: string | null
37
- headingLineTwo?: string | null
38
- description?: string | null
39
- showDivider?: boolean | null
40
- primaryLink?: BlockLinkField
41
- secondaryLink?: BlockLinkField
42
- }
43
-
44
- function getHref(link?: BlockLinkField): string | null {
45
- if (!link) return null
46
-
47
- if (link.type === 'custom' && link.url) return link.url
48
-
49
- if (link.type === 'reference' && link.reference?.value) {
50
- const { relationTo, value } = link.reference
51
- if (typeof value === 'number') return null
52
-
53
- if (relationTo === 'pages') {
54
- const crumbs = Array.isArray(value.breadcrumbs) ? value.breadcrumbs : []
55
- const last = crumbs[crumbs.length - 1]
56
- if (last?.url) return last.url
57
-
58
- const slug = value.slug
59
- if (!slug || slug === 'home') return '/'
60
- return slug.startsWith('/') ? slug : `/${slug}`
61
- }
62
-
63
- const slug = value.slug
64
- if (!slug) return null
65
-
66
- if (relationTo === 'projects') return `/archives/${slug}`
67
- if (relationTo === 'posts' || relationTo === 'thoughts') return `/thoughts/${slug}`
68
- return `/${slug}`
69
- }
70
-
71
- return null
72
- }
73
-
74
- function CTAButton({ link, variant }: { link?: BlockLinkField; variant: 'primary' | 'secondary' }) {
75
- const href = getHref(link)
76
- if (!href) return null
77
-
78
- const target = link?.newTab ? '_blank' : undefined
79
- const rel = link?.newTab ? 'noopener noreferrer' : undefined
80
-
81
- return (
82
- <a
83
- href={href}
84
- target={target}
85
- rel={rel}
86
- className={cn(
87
- 'inline-flex min-h-10 items-center justify-center rounded-md squircle px-5 py-2.5 text-sm font-medium transition-all duration-300 0 hover:shadow-xl hover:-translate-y-0.5 hover:transition-all hover:duration-300',
88
- variant === 'primary'
89
- ? 'bg-stone-800 text-white hover:bg-stone-700'
90
- : 'border border-stone-400 text-stone-700 dark:text-stone-200 hover:bg-stone-100 dark:hover:text-stone-700',
91
- )}
92
- >
93
- {link?.label || 'Learn More'}
94
- </a>
95
- )
96
- }
97
-
98
- const revealMotion = {
99
- initial: { opacity: 0, y: 18 },
100
- whileInView: { opacity: 1, y: 0 },
101
- viewport: { once: true, amount: 0.25 },
102
- transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
103
- }
104
-
105
- export const DDHGCTASection2: React.FC<DDHGCTASection2Props> = ({
106
- sectionId,
107
- customClassName,
108
- customCSS,
109
- blockId,
110
- scrollSpeed,
111
- eyebrowPrefix = 'ready to',
112
- eyebrowEmphasis = 'begin?',
113
- headingLineOne = "LET'S BUILD YOUR",
114
- headingLineTwo = 'NEXT SPACE',
115
- description = 'Every great space begins with a conversation. Share your vision, and we will map the path from where you are to where you want to be.',
116
- showDivider = true,
117
- primaryLink,
118
- secondaryLink,
119
- }) => {
120
- const hasButtons = Boolean(getHref(primaryLink) || getHref(secondaryLink))
121
-
122
- return (
123
- <div
124
- id={sectionId ?? undefined}
125
- data-block-id={blockId ?? undefined}
126
- data-scroll={scrollSpeed ? true : undefined}
127
- data-scroll-speed={scrollSpeed ? String(scrollSpeed) : undefined}
128
- className={cn('mb-10 px-5 py-20 md:px-8', customClassName)}
129
- >
130
- {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
131
-
132
- <div className="mx-auto max-w-7xl">
133
- <motion.div
134
- className="z-plane-2 relative overflow-hidden rounded-md squircle border border-stone-300/10 bg-stone-100/50 transition-all duration-300 dark:border-stone-700/10 dark:bg-stone-900/50 shadow-xl"
135
- initial={{ opacity: 0, y: 20 }}
136
- whileInView={{ opacity: 1, y: 0 }}
137
- viewport={{ once: true, amount: 0.25 }}
138
- transition={{ duration: 0.9, ease: [0.22, 1, 0.36, 1] }}
139
- >
140
- <div className="relative p-8 text-center md:p-12 lg:p-16">
141
- {(eyebrowPrefix || eyebrowEmphasis) && (
142
- <p className="mb-4 text-xs uppercase tracking-[0.2em] text-stone-600 dark:text-stone-300">
143
- {eyebrowPrefix && <span className="font-light">{eyebrowPrefix} </span>}
144
- {eyebrowEmphasis && <span className="font-medium">{eyebrowEmphasis}</span>}
145
- </p>
146
- )}
147
-
148
- <motion.div className="heading" {...revealMotion}>
149
- {(headingLineOne || headingLineTwo) && (
150
- <h2 className="mb-4 text-3xl font-medium leading-none tracking-tight text-purple-700 dark:text-purple-500 md:text-5xl">
151
- {headingLineOne}
152
- {headingLineOne && headingLineTwo && <br />}
153
- <span className="font-bold">{headingLineTwo}</span>
154
- </h2>
155
- )}
156
- </motion.div>
157
-
158
- {showDivider && <div className="mx-auto mb-6 h-px w-12 bg-stone-500" />}
159
-
160
- <motion.div
161
- className="description"
162
- {...revealMotion}
163
- transition={{ ...revealMotion.transition, delay: 0.08 }}
164
- >
165
- {description && (
166
- <p className="mx-auto mb-8 max-w-lg text-[14px] font-light leading-relaxed text-stone-700 dark:text-stone-200">
167
- {description}
168
- </p>
169
- )}
170
- </motion.div>
171
-
172
- {hasButtons && (
173
- <div className="flex flex-wrap items-center justify-center gap-3">
174
- <CTAButton link={primaryLink} variant="primary" />
175
- <CTAButton link={secondaryLink} variant="secondary" />
176
- </div>
177
- )}
178
- </div>
179
- </motion.div>
180
- </div>
181
- </div>
182
- )
183
- }
184
-
1
+ 'use client'
2
+
3
+ import { motion } from 'framer-motion'
4
+ import React from 'react'
5
+
6
+ import { cn } from '@/utilities/ui'
7
+
8
+ type PortableDoc = {
9
+ slug?: string | null
10
+ breadcrumbs?: Array<{ url?: string | null }> | null
11
+ [key: string]: unknown
12
+ }
13
+
14
+ type LinkReference =
15
+ | { relationTo: 'pages'; value: number | PortableDoc }
16
+ | { relationTo: 'projects'; value: number | PortableDoc }
17
+ | { relationTo: 'thoughts'; value: number | PortableDoc }
18
+
19
+ type BlockLinkField = {
20
+ type?: 'reference' | 'custom' | null
21
+ reference?: LinkReference | null
22
+ url?: string | null
23
+ label?: string | null
24
+ newTab?: boolean | null
25
+ } | null
26
+
27
+ export type DDHGCTASection2Props = {
28
+ sectionId?: string | null
29
+ customClassName?: string | null
30
+ customCSS?: string | null
31
+ blockId?: string | null
32
+ scrollSpeed?: string | number | null
33
+ eyebrowPrefix?: string | null
34
+ eyebrowEmphasis?: string | null
35
+ headingLineOne?: string | null
36
+ headingLineTwo?: string | null
37
+ description?: string | null
38
+ showDivider?: boolean | null
39
+ primaryLink?: BlockLinkField
40
+ secondaryLink?: BlockLinkField
41
+ }
42
+
43
+ function getHref(link?: BlockLinkField): string | null {
44
+ if (!link) return null
45
+
46
+ if (link.type === 'custom' && link.url) return link.url
47
+
48
+ if (link.type === 'reference' && link.reference?.value) {
49
+ const { relationTo, value } = link.reference
50
+ if (typeof value === 'number') return null
51
+
52
+ if (relationTo === 'pages') {
53
+ const crumbs = Array.isArray(value.breadcrumbs) ? value.breadcrumbs : []
54
+ const last = crumbs[crumbs.length - 1]
55
+ if (last?.url) return last.url
56
+
57
+ const slug = value.slug
58
+ if (!slug || slug === 'home') return '/'
59
+ return slug.startsWith('/') ? slug : `/${slug}`
60
+ }
61
+
62
+ const slug = value.slug
63
+ if (!slug) return null
64
+
65
+ if (relationTo === 'projects') return `/archives/${slug}`
66
+ if (relationTo === 'thoughts') return `/thoughts/${slug}`
67
+ return `/${slug}`
68
+ }
69
+
70
+ return null
71
+ }
72
+
73
+ function CTAButton({ link, variant }: { link?: BlockLinkField; variant: 'primary' | 'secondary' }) {
74
+ const href = getHref(link)
75
+ if (!href) return null
76
+
77
+ const target = link?.newTab ? '_blank' : undefined
78
+ const rel = link?.newTab ? 'noopener noreferrer' : undefined
79
+
80
+ return (
81
+ <a
82
+ href={href}
83
+ target={target}
84
+ rel={rel}
85
+ className={cn(
86
+ 'inline-flex min-h-10 items-center justify-center rounded-md squircle px-5 py-2.5 text-sm font-medium transition-all duration-300 0 hover:shadow-xl hover:-translate-y-0.5 hover:transition-all hover:duration-300',
87
+ variant === 'primary'
88
+ ? 'bg-stone-800 text-white hover:bg-stone-700'
89
+ : 'border border-stone-400 text-stone-700 dark:text-stone-200 hover:bg-stone-100 dark:hover:text-stone-700',
90
+ )}
91
+ >
92
+ {link?.label || 'Learn More'}
93
+ </a>
94
+ )
95
+ }
96
+
97
+ const revealMotion = {
98
+ initial: { opacity: 0, y: 18 },
99
+ whileInView: { opacity: 1, y: 0 },
100
+ viewport: { once: true, amount: 0.25 },
101
+ transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
102
+ }
103
+
104
+ export const DDHGCTASection2: React.FC<DDHGCTASection2Props> = ({
105
+ sectionId,
106
+ customClassName,
107
+ customCSS,
108
+ blockId,
109
+ scrollSpeed,
110
+ eyebrowPrefix = 'ready to',
111
+ eyebrowEmphasis = 'begin?',
112
+ headingLineOne = "LET'S BUILD YOUR",
113
+ headingLineTwo = 'NEXT SPACE',
114
+ description = 'Every great space begins with a conversation. Share your vision, and we will map the path from where you are to where you want to be.',
115
+ showDivider = true,
116
+ primaryLink,
117
+ secondaryLink,
118
+ }) => {
119
+ const hasButtons = Boolean(getHref(primaryLink) || getHref(secondaryLink))
120
+
121
+ return (
122
+ <div
123
+ id={sectionId ?? undefined}
124
+ data-block-id={blockId ?? undefined}
125
+ data-scroll={scrollSpeed ? true : undefined}
126
+ data-scroll-speed={scrollSpeed ? String(scrollSpeed) : undefined}
127
+ className={cn('mb-10 px-5 py-20 md:px-8', customClassName)}
128
+ >
129
+ {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
130
+
131
+ <div className="mx-auto max-w-7xl">
132
+ <motion.div
133
+ className="z-plane-2 relative overflow-hidden rounded-md squircle border border-stone-300/10 bg-stone-100/50 transition-all duration-300 dark:border-stone-700/10 dark:bg-stone-900/50 shadow-xl"
134
+ initial={{ opacity: 0, y: 20 }}
135
+ whileInView={{ opacity: 1, y: 0 }}
136
+ viewport={{ once: true, amount: 0.25 }}
137
+ transition={{ duration: 0.9, ease: [0.22, 1, 0.36, 1] }}
138
+ >
139
+ <div className="relative p-8 text-center md:p-12 lg:p-16">
140
+ {(eyebrowPrefix || eyebrowEmphasis) && (
141
+ <p className="mb-4 text-xs uppercase tracking-[0.2em] text-stone-600 dark:text-stone-300">
142
+ {eyebrowPrefix && <span className="font-light">{eyebrowPrefix} </span>}
143
+ {eyebrowEmphasis && <span className="font-medium">{eyebrowEmphasis}</span>}
144
+ </p>
145
+ )}
146
+
147
+ <motion.div className="heading" {...revealMotion}>
148
+ {(headingLineOne || headingLineTwo) && (
149
+ <h2 className="mb-4 text-3xl font-medium leading-none tracking-tight text-purple-700 dark:text-purple-500 md:text-5xl">
150
+ {headingLineOne}
151
+ {headingLineOne && headingLineTwo && <br />}
152
+ <span className="font-bold">{headingLineTwo}</span>
153
+ </h2>
154
+ )}
155
+ </motion.div>
156
+
157
+ {showDivider && <div className="mx-auto mb-6 h-px w-12 bg-stone-500" />}
158
+
159
+ <motion.div
160
+ className="description"
161
+ {...revealMotion}
162
+ transition={{ ...revealMotion.transition, delay: 0.08 }}
163
+ >
164
+ {description && (
165
+ <p className="mx-auto mb-8 max-w-lg text-[14px] font-light leading-relaxed text-stone-700 dark:text-stone-200">
166
+ {description}
167
+ </p>
168
+ )}
169
+ </motion.div>
170
+
171
+ {hasButtons && (
172
+ <div className="flex flex-wrap items-center justify-center gap-3">
173
+ <CTAButton link={primaryLink} variant="primary" />
174
+ <CTAButton link={secondaryLink} variant="secondary" />
175
+ </div>
176
+ )}
177
+ </div>
178
+ </motion.div>
179
+ </div>
180
+ </div>
181
+ )
182
+ }
183
+
185
184
  export default DDHGCTASection2
@@ -21,7 +21,6 @@ type Media = {
21
21
 
22
22
  type LinkReference =
23
23
  | { relationTo: 'pages'; value: number | PortableDoc }
24
- | { relationTo: 'posts'; value: number | PortableDoc }
25
24
  | { relationTo: 'projects'; value: number | PortableDoc }
26
25
  | { relationTo: 'thoughts'; value: number | PortableDoc }
27
26
 
@@ -82,7 +81,6 @@ const getDocumentSlug = (doc: PortableDoc | number): string | null => {
82
81
  }
83
82
 
84
83
  const getCollectionPath = (relationTo: LinkReference['relationTo']): string => {
85
- if (relationTo === 'posts') return 'thoughts'
86
84
  if (relationTo === 'thoughts') return 'thoughts'
87
85
  if (relationTo === 'projects') return 'archives'
88
86
  return ''
@@ -120,7 +118,12 @@ export const DDHGHighlightArc: React.FC<DDHGHighlightArcProps> = ({
120
118
  if (!Array.isArray(highlights) || highlights.length === 0) return null
121
119
 
122
120
  return (
123
- <div className={cn('grid origin-top scale-100 gap-0!', getGridClassName(gridColumns))}>
121
+ <div
122
+ className={cn(
123
+ 'mx-auto grid w-full max-w-7xl origin-top scale-100 gap-0!',
124
+ getGridClassName(gridColumns),
125
+ )}
126
+ >
124
127
  {highlights.map((h, i) => {
125
128
  const isHovered = hoveredIdx === i
126
129
  const bgImage = typeof h.bgImage === 'object' && h.bgImage !== null ? h.bgImage : null
@@ -3,6 +3,7 @@
3
3
  import React, { useState } from 'react'
4
4
  import { motion } from 'framer-motion'
5
5
  import Image from 'next/image'
6
+ import * as LucideIcons from 'lucide-react'
6
7
  import { cn } from '@/utilities/ui'
7
8
  // DDHG TODO: Re-enable after reviewing the project-specific separator/custom CSS bundle.
8
9
  // import { Separator } from '@/components/ui/separator'
@@ -19,8 +20,21 @@ type LogoItem = {
19
20
  imageClass?: string | null
20
21
  imageStyle?: React.CSSProperties
21
22
  imageAltClass?: string | null
23
+ icon?: string | null
24
+ label?: string | null
25
+ showLabel?: boolean | null
22
26
  }
23
27
 
28
+ function DynamicIcon({
29
+ name,
30
+ size = 36,
31
+ ...props
32
+ }: { name?: string | null } & Omit<LucideIcons.LucideProps, 'name'>) {
33
+ if (!name) return null
34
+ const Icon = (LucideIcons as unknown as Record<string, React.FC<LucideIcons.LucideProps>>)[name]
35
+ if (!Icon) return null
36
+ return <Icon size={size} {...props} />
37
+ }
24
38
  export type DDHGLogoMarqueeProps = {
25
39
  sectionId?: string | null
26
40
  logos?: LogoItem[] | null
@@ -79,6 +93,8 @@ export const DDHGLogoMarquee: React.FC<DDHGLogoMarqueeProps> = ({
79
93
  const validLogos = Array.isArray(logos) ? logos : []
80
94
  if (validLogos.length === 0) return null
81
95
  const calculatedItemWidth = itemWidth || 100
96
+ const defaultIcons = ['Code2', 'Database', 'PanelsTopLeft', 'Braces', 'Workflow', 'GitBranch']
97
+ const defaultLabels = ['Interface', 'Data', 'Layout', 'Types', 'Workflow', 'Collaboration']
82
98
  const totalWidth = validLogos.length * (calculatedItemWidth + (spacing || 0))
83
99
 
84
100
  // DDHG TODO: Re-enable with extractCustomCSS when that bundle is reviewed.
@@ -105,14 +121,35 @@ export const DDHGLogoMarquee: React.FC<DDHGLogoMarqueeProps> = ({
105
121
  : { minWidth: `${calculatedItemWidth}px` }),
106
122
  }}
107
123
  >
108
- <LogoImage
109
- src={logo.image?.url || placeholderBlur}
110
- alt={logo.image?.alt}
111
- customClass={logo.imageClass}
112
- imageStyle={logo.imageStyle}
113
- priority={Boolean(priority)}
114
- />
115
- <p className={cn('text-base text-center', logo.imageAltClass)}>{logo.image?.alt || null}</p>
124
+ {true ? (
125
+ <>
126
+ <div className="flex h-20 w-20 items-center justify-center rounded-2xl border border-[#2A2A30] bg-linear-to-b from-[#1a1a1f] to-[#0f0f12] p-4 invert-[1] transition-all duration-300 hover:scale-105 hover:shadow-[0_0_30px_rgba(140,138,132,0.15)] dark:invert-[0]">
127
+ <DynamicIcon
128
+ className="text-[#9A9790]"
129
+ name={logo.icon || defaultIcons[index % defaultIcons.length]}
130
+ size={36}
131
+ />
132
+ </div>
133
+ {logo.showLabel !== false && (
134
+ <span className="mt-2 block whitespace-nowrap text-center text-sm text-[#9A9790]">
135
+ {logo.label || defaultLabels[index % defaultLabels.length]}
136
+ </span>
137
+ )}
138
+ </>
139
+ ) : (
140
+ <>
141
+ <LogoImage
142
+ src={logo.image?.url || placeholderBlur}
143
+ alt={logo.image?.alt}
144
+ customClass={logo.imageClass}
145
+ imageStyle={logo.imageStyle}
146
+ priority={Boolean(priority)}
147
+ />
148
+ <p className={cn('text-center text-base', logo.imageAltClass)}>
149
+ {logo.label || logo.image?.alt || null}
150
+ </p>
151
+ </>
152
+ )}
116
153
  </div>
117
154
  ))
118
155
 
@@ -22,7 +22,6 @@ type Media = {
22
22
 
23
23
  type LinkReference =
24
24
  | { relationTo: 'pages'; value: number | PortableDoc }
25
- | { relationTo: 'posts'; value: number | PortableDoc }
26
25
  | { relationTo: 'projects'; value: number | PortableDoc }
27
26
  | { relationTo: 'thoughts'; value: number | PortableDoc }
28
27
 
@@ -70,7 +69,6 @@ const getDocumentSlug = (doc: PortableDoc | number): string | null => {
70
69
  }
71
70
 
72
71
  const getCollectionPath = (relationTo: LinkReference['relationTo']): string => {
73
- if (relationTo === 'posts') return 'internal-docs'
74
72
  if (relationTo === 'thoughts') return 'thoughts'
75
73
  if (relationTo === 'projects') return 'archives'
76
74
  return ''
@@ -163,7 +161,7 @@ export const DDHGNavCodeGrid: React.FC<DDHGNavCodeGridProps> = ({
163
161
  const target = link?.newTab ? '_blank' : undefined
164
162
  const rel = link?.newTab ? 'noopener noreferrer' : undefined
165
163
  const className = cn(
166
- 'menu-cell-hover group relative flex items-center justify-center gap-2 bg-stone-200/50 py-2.5 text-xs font-normal uppercase tracking-[0.1em] text-sm [text-shadow:0px_2px_4px_#ffffff45] backdrop-blur-2xl transition-all duration-300 hover:-translate-y-0.5 hover:bg-stone-200/80 hover:text-stone-700 dark:bg-stone-900/50 dark:text-stone-200 dark:hover:bg-stone-800/70 sm:py-3 sm:text-xs md:py-4 hover:shadow-2xl',
164
+ 'menu-cell-hover group relative flex items-center justify-center gap-2 bg-stone-200/50 py-2.5 text-xs font-normal uppercase tracking-[0.1em] text-sm !text-stone-900 [text-shadow:0px_2px_4px_#ffffff45] backdrop-blur-2xl transition-all duration-300 hover:-translate-y-0.5 hover:bg-stone-200/80 hover:!text-stone-700 dark:bg-stone-900/50 dark:!text-stone-100 dark:hover:bg-stone-800/70 dark:hover:!text-white sm:py-3 sm:text-xs md:py-4 hover:shadow-2xl',
167
165
  'border-r border-b border-border',
168
166
  (index + 1) % 4 === 0 ? 'sm:border-r-0' : ' ',
169
167
  index >= 4 ? 'sm:border-b-0' : '',
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { AnimatePresence, motion } from 'framer-motion'
4
4
  import { ChevronDown, ChevronUp, CircleDot } from 'lucide-react'
5
+ import Image from 'next/image'
5
6
  import React from 'react'
6
- import RichText from '@/components/RichText/index'
7
7
  import { cn } from '@/utilities/ui'
8
8
 
9
9
  export type ServiceDetailLayout = 'left' | 'right' | 'center' | 'split' | 'asym' | 'bleed'
@@ -161,12 +161,6 @@ type ServiceDetailCardProps = {
161
161
  onToggle: () => void
162
162
  }
163
163
 
164
- function hasRichTextContent(value: unknown): boolean {
165
- if (!value || typeof value !== 'object') return false
166
- const root = (value as any)?.root
167
- return Array.isArray(root?.children) && root.children.length > 0
168
- }
169
-
170
164
  function getDescription(service: ServiceDetailItem) {
171
165
  return service.description || service.desc || ''
172
166
  }
@@ -180,22 +174,15 @@ function getPoints(service: ServiceDetailItem) {
180
174
  }
181
175
 
182
176
  function VisualContent({ service }: { service: ServiceDetailItem }) {
183
- const content = service.visualContent || service.mediaContent || null
184
- if (hasRichTextContent(content)) {
185
- return (
186
- <div className="relative h-full min-h-75 w-full overflow-hidden rounded-md squircle border border-stone-300/10 bg-stone-100/50 p-4 dark:border-stone-700/10 dark:bg-stone-900/50 hover:shadow-xl transition-all duration-300">
187
- <RichText
188
- data={content}
189
- enableGutter={false}
190
- enableProse={false}
191
- className="h-full min-h-75 w-full *:m-0 [&_.media-block]:m-0 [&_img]:m-0"
192
- />
193
- </div>
194
- )
195
- }
196
177
  return (
197
- <div className="min-h-75 w-full rounded-md squircle bg-linear-to-br from-[#838383] to-[#8e887c] m-auto flex items-center justify-center">
198
- <InlineLogoIcon />
178
+ <div className="relative h-full min-h-75 w-full overflow-hidden rounded-md squircle border border-stone-300/10 bg-stone-100/50 dark:border-stone-700/10 dark:bg-stone-900/50 hover:shadow-xl transition-all duration-300">
179
+ <Image
180
+ src="/media/placeholder-2-1920x1080.webp"
181
+ alt={`${service.title || 'Service detail'} visual`}
182
+ fill
183
+ className="object-cover"
184
+ sizes="(max-width: 768px) 100vw, 50vw"
185
+ />
199
186
  </div>
200
187
  )
201
188
  }
@@ -24,7 +24,7 @@ const minHeightClassMap: Record<NonNullable<DDHGServiceHeroProps['minHeight']>,
24
24
  screen: 'min-h-screen',
25
25
  large: 'min-h-[70vh]',
26
26
  medium: 'min-h-[56vh]',
27
- compact: 'min-h-[42vh]'
27
+ compact: 'min-h-[42vh]',
28
28
  }
29
29
 
30
30
  let globalInstanceCounter = 0
@@ -32,7 +32,7 @@ let globalInstanceCounter = 0
32
32
  function FadeUpOnInView({
33
33
  children,
34
34
  className = '',
35
- delay = 0
35
+ delay = 0,
36
36
  }: {
37
37
  children: React.ReactNode
38
38
  className?: string
@@ -54,7 +54,7 @@ function FadeUpOnInView({
54
54
  node.style.transitionDelay = '0s'
55
55
  }
56
56
  },
57
- { threshold: 0.25 }
57
+ { threshold: 0.25 },
58
58
  )
59
59
  observer.observe(node)
60
60
  return () => observer.disconnect()
@@ -67,7 +67,7 @@ function FadeUpOnInView({
67
67
  display: 'inline-block',
68
68
  opacity: 0,
69
69
  transform: 'translateY(24px)',
70
- transition: 'opacity 700ms ease, transform 700ms ease'
70
+ transition: 'opacity 700ms ease, transform 700ms ease',
71
71
  }}
72
72
  >
73
73
  {children}
@@ -78,7 +78,7 @@ function FadeUpOnInView({
78
78
  function RevealText({
79
79
  text,
80
80
  className = '',
81
- instanceIndex
81
+ instanceIndex,
82
82
  }: {
83
83
  text: string[]
84
84
  className?: string
@@ -101,7 +101,7 @@ function RevealText({
101
101
  setAnimationKey((key) => key + 1)
102
102
  } else setIsVisible(false)
103
103
  },
104
- { threshold: 0.3 }
104
+ { threshold: 0.3 },
105
105
  )
106
106
  observer.observe(node)
107
107
  return () => observer.disconnect()
@@ -117,7 +117,7 @@ function RevealText({
117
117
  ref={textRef}
118
118
  className={cn(
119
119
  'inline-block h-auto bg-transparent text-left font-bold uppercase leading-none tracking-[1px]',
120
- className
120
+ className,
121
121
  )}
122
122
  >
123
123
  {words.map((word, wordIndex) => {
@@ -140,7 +140,7 @@ function RevealText({
140
140
  animationName: isVisible ? 'ddhgServiceHeroReveal' : 'none',
141
141
  animationDuration: '1s',
142
142
  animationFillMode: 'forwards',
143
- animationDelay: `${baseDelay + index * 0.05}s`
143
+ animationDelay: `${baseDelay + index * 0.05}s`,
144
144
  }}
145
145
  >
146
146
  {char}
@@ -169,7 +169,7 @@ export const DDHGServiceHero: React.FC<DDHGServiceHeroProps> = ({
169
169
  description = 'From initial vision to final handover, our services span the full spectrum of interior architecture. Each engagement is shaped by the same principle: spaces should serve the people inside them.',
170
170
  minHeight = 'large',
171
171
  showDivider = true,
172
- showScrollIndicator = true
172
+ showScrollIndicator = true,
173
173
  }) => {
174
174
  const minHeightClass = minHeightClassMap[minHeight || 'large']
175
175
  return (
@@ -182,7 +182,7 @@ export const DDHGServiceHero: React.FC<DDHGServiceHeroProps> = ({
182
182
  >
183
183
  {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
184
184
  <motion.div
185
- className="ServiceHeroCard relative z-10 mx-auto max-w-4xl overflow-clip rounded-md squircle bg-stone-100/50 p-6 dark:bg-stone-900/50 md:p-8"
185
+ className="ServiceHeroCard relative z-10 mx-auto w-full max-w-7xl overflow-clip rounded-md squircle bg-stone-100/50 p-6 dark:bg-stone-900/50 md:p-8"
186
186
  initial={{ opacity: 0, y: 20 }}
187
187
  whileInView={{ opacity: 1, y: 0 }}
188
188
  viewport={{ once: true, amount: 0.35 }}