blocks-dusted 0.1.11 → 0.1.12

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 (46) hide show
  1. package/package.json +5 -2
  2. package/src/cli.js +39 -3
  3. package/src/commands/shop.js +59 -0
  4. package/src/commands/warehouse.js +73 -0
  5. package/templates/blocks/DD-BookCall/Component.tsx +535 -534
  6. package/templates/blocks/DD-CardTemplate01/Component.tsx +140 -139
  7. package/templates/blocks/DD-Carousel-A/Component.tsx +1 -1
  8. package/templates/blocks/DD-Carousel-B/Component.tsx +1 -1
  9. package/templates/blocks/DD-Chip/Component.tsx +14 -14
  10. package/templates/blocks/DD-ComparisonTable/Component.tsx +1 -1
  11. package/templates/blocks/DD-Contact/Component.tsx +8 -7
  12. package/templates/blocks/DD-Counter/Component.tsx +10 -10
  13. package/templates/blocks/DD-FeatCat/Component.tsx +1 -1
  14. package/templates/blocks/DD-FeatStrip/Component.tsx +1 -1
  15. package/templates/blocks/DD-Hero/Component.tsx +297 -320
  16. package/templates/blocks/DD-HorizontalScroll/Component.tsx +936 -938
  17. package/templates/blocks/DD-IframeMedia/Component.tsx +14 -10
  18. package/templates/blocks/DD-MasonaryMedia/Component.tsx +8 -10
  19. package/templates/blocks/DD-Pricing/Component.tsx +369 -372
  20. package/templates/blocks/DD-Process/Component.tsx +147 -149
  21. package/templates/blocks/DD-Progress/Component.tsx +156 -141
  22. package/templates/blocks/DD-ProjSlider/config.ts +2 -2
  23. package/templates/blocks/DD-Section/Component.tsx +1 -1
  24. package/templates/blocks/DD-Services/Component.tsx +1 -1
  25. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +13 -13
  26. package/templates/blocks/DD-Slider-A/Component.tsx +237 -237
  27. package/templates/blocks/DD-StackCards/Component.tsx +473 -473
  28. package/templates/blocks/DD-Team/Component.tsx +1 -1
  29. package/templates/blocks/DD-TechStack/Component.tsx +22 -28
  30. package/templates/blocks/DD-Testimonials/Component.tsx +35 -43
  31. package/templates/blocks/DD-TextMarq/Component.tsx +157 -157
  32. package/templates/blocks/DD-Thanks/Component.tsx +501 -500
  33. package/templates/blocks/DD-Work/Component.tsx +4 -4
  34. package/templates/blocks/DD-Work-B/Component.tsx +1 -1
  35. package/templates/blocks/DDHG-CTASection2/Component.tsx +6 -6
  36. package/templates/blocks/DDHG-HighlightArc/Component.tsx +243 -243
  37. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +143 -93
  38. package/templates/blocks/DDHG-LogoMarquee/config.ts +154 -66
  39. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +227 -213
  40. package/templates/blocks/DDHG-ProcessSec/Component.tsx +38 -39
  41. package/templates/blocks/DDHG-ServiceDetails/Component.tsx +11 -11
  42. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +24 -28
  43. package/templates/blocks/DDHG-ServiceHero/Component.tsx +1 -1
  44. package/templates/blocks/DDHG-ServiceOverview/Component.tsx +23 -23
  45. package/templates/blocks/DDHG-StatsFAQ/Component.tsx +40 -40
  46. package/templates/blocks/DDHG-TeamCards/Component.tsx +169 -131
@@ -33,14 +33,14 @@ interface SpotlightCardProps {
33
33
  children: React.ReactNode
34
34
  className?: string
35
35
  spotlightColor?: string
36
- style?: React.CSSProperties
36
+ style?: React.CSSProperties
37
37
  }
38
38
 
39
39
  function SpotlightCard({
40
40
  children,
41
41
  className,
42
42
  spotlightColor = 'rgba(214,211,209,0.08)',
43
- style,
43
+ style,
44
44
  }: SpotlightCardProps) {
45
45
  const cardRef = useRef<HTMLDivElement>(null)
46
46
  const [pos, setPos] = useState<{ x: number; y: number } | null>(null)
@@ -62,7 +62,7 @@ function SpotlightCard({
62
62
  <div
63
63
  ref={cardRef}
64
64
  className={cn('relative overflow-hidden', className)}
65
- style={style}
65
+ style={style}
66
66
  onMouseMove={handleMouseMove}
67
67
  onMouseLeave={handleMouseLeave}
68
68
  >
@@ -132,7 +132,7 @@ export const DDWork: React.FC<DDWorkProps> = ({
132
132
 
133
133
  <div id={sectionId ?? 'work'} className="py-24">
134
134
  <div className="mx-auto max-w-6xl lg:max-w-7xl px-6">
135
- {/* Header */}
135
+ {/* Header */}#BlockName-DDWork
136
136
  <motion.div
137
137
  variants={containerVariants}
138
138
  initial="hidden"
@@ -243,7 +243,7 @@ export const DDWorkB: React.FC<DDWorkBProps> = ({
243
243
 
244
244
  <div id={sectionId ?? 'work'} className="py-24">
245
245
  <div className="max-w-6xl lg:max-w-7xl mx-auto px-4 md:px-6">
246
- {/* Header */}
246
+ {/* Header */}#BlockName-DDWorkB
247
247
  <div className="flex flex-col md:flex-row md:items-end justify-between gap-6 mb-12">
248
248
  <div>
249
249
  {eyebrow && (
@@ -84,10 +84,10 @@ function CTAButton({ link, variant }: { link?: BlockLinkField; variant: 'primary
84
84
  target={target}
85
85
  rel={rel}
86
86
  className={cn(
87
- 'inline-flex min-h-10 items-center justify-center px-5 py-2.5 text-sm font-medium transition-all duration-300',
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
88
  variant === 'primary'
89
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'
90
+ : 'border border-stone-400 text-stone-700 dark:text-stone-200 hover:bg-stone-100 dark:hover:text-stone-700',
91
91
  )}
92
92
  >
93
93
  {link?.label || 'Learn More'}
@@ -99,7 +99,7 @@ const revealMotion = {
99
99
  initial: { opacity: 0, y: 18 },
100
100
  whileInView: { opacity: 1, y: 0 },
101
101
  viewport: { once: true, amount: 0.25 },
102
- transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }
102
+ transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
103
103
  }
104
104
 
105
105
  export const DDHGCTASection2: React.FC<DDHGCTASection2Props> = ({
@@ -115,7 +115,7 @@ export const DDHGCTASection2: React.FC<DDHGCTASection2Props> = ({
115
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
116
  showDivider = true,
117
117
  primaryLink,
118
- secondaryLink
118
+ secondaryLink,
119
119
  }) => {
120
120
  const hasButtons = Boolean(getHref(primaryLink) || getHref(secondaryLink))
121
121
 
@@ -131,7 +131,7 @@ export const DDHGCTASection2: React.FC<DDHGCTASection2Props> = ({
131
131
 
132
132
  <div className="mx-auto max-w-7xl">
133
133
  <motion.div
134
- className="z-plane-2 relative overflow-hidden"
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
135
  initial={{ opacity: 0, y: 20 }}
136
136
  whileInView={{ opacity: 1, y: 0 }}
137
137
  viewport={{ once: true, amount: 0.25 }}
@@ -139,7 +139,7 @@ export const DDHGCTASection2: React.FC<DDHGCTASection2Props> = ({
139
139
  >
140
140
  <div className="relative p-8 text-center md:p-12 lg:p-16">
141
141
  {(eyebrowPrefix || eyebrowEmphasis) && (
142
- <p className="mb-4 text-[10px] uppercase tracking-[0.2em] text-stone-600 dark:text-stone-300">
142
+ <p className="mb-4 text-xs uppercase tracking-[0.2em] text-stone-600 dark:text-stone-300">
143
143
  {eyebrowPrefix && <span className="font-light">{eyebrowPrefix} </span>}
144
144
  {eyebrowEmphasis && <span className="font-medium">{eyebrowEmphasis}</span>}
145
145
  </p>
@@ -1,243 +1,243 @@
1
- 'use client'
2
-
3
- import { ArrowRight } from 'lucide-react'
4
- import Image from 'next/image'
5
- import Link from 'next/link'
6
- import React, { useState } from 'react'
7
-
8
- import { cn } from '@/utilities/ui'
9
-
10
- type PortableDoc = {
11
- slug?: string | null
12
- breadcrumbs?: Array<{ url?: string | null }> | null
13
- [key: string]: unknown
14
- }
15
-
16
- type Media = {
17
- url?: string | null
18
- alt?: string | null
19
- [key: string]: unknown
20
- }
21
-
22
- type LinkReference =
23
- | { relationTo: 'pages'; value: number | PortableDoc }
24
- | { relationTo: 'posts'; value: number | PortableDoc }
25
- | { relationTo: 'projects'; value: number | PortableDoc }
26
- | { relationTo: 'thoughts'; value: number | PortableDoc }
27
-
28
- type HighlightArchiveCardsLink = {
29
- type?: 'reference' | 'custom' | null
30
- reference?: LinkReference | null
31
- url?: string | null
32
- label?: string | null
33
- newTab?: boolean | null
34
- } | null
35
-
36
- type HighlightArchiveCardsItem = {
37
- id?: string | null
38
- code?: string | null
39
- title?: string | null
40
- type?: string | null
41
- bgImage?: number | Media | null
42
- priority?: boolean | null
43
- link?: HighlightArchiveCardsLink
44
- }
45
-
46
- export type DDHGHighlightArcProps = {
47
- gridColumns?: '1' | '2' | '3' | '4' | null
48
- highlights?: HighlightArchiveCardsItem[] | null
49
- }
50
-
51
- const getGridClassName = (gridColumns?: DDHGHighlightArcProps['gridColumns']) => {
52
- switch (gridColumns) {
53
- case '2':
54
- return 'grid-cols-1 md:grid-cols-2'
55
- case '3':
56
- return 'grid-cols-1 md:grid-cols-2 xl:grid-cols-3'
57
- case '4':
58
- return 'grid-cols-1 md:grid-cols-2 xl:grid-cols-4'
59
- case '1':
60
- default:
61
- return 'grid-cols-1'
62
- }
63
- }
64
-
65
- const getPageURLFromDoc = (doc: PortableDoc | number): string | null => {
66
- if (typeof doc === 'number') return null
67
-
68
- const crumbs = Array.isArray(doc?.breadcrumbs) ? doc.breadcrumbs : []
69
- if (crumbs.length) {
70
- const last = crumbs[crumbs.length - 1]
71
- if (typeof last?.url === 'string' && last.url) return last.url
72
- }
73
-
74
- const slug = doc?.slug
75
- if (!slug || slug === 'home') return '/'
76
- return slug.startsWith('/') ? slug : `/${slug}`
77
- }
78
-
79
- const getDocumentSlug = (doc: PortableDoc | number): string | null => {
80
- if (typeof doc === 'number') return null
81
- return doc?.slug || null
82
- }
83
-
84
- const getCollectionPath = (relationTo: LinkReference['relationTo']): string => {
85
- if (relationTo === 'posts') return 'thoughts'
86
- if (relationTo === 'thoughts') return 'thoughts'
87
- if (relationTo === 'projects') return 'archives'
88
- return ''
89
- }
90
-
91
- const resolveHref = (link: HighlightArchiveCardsLink): string | null => {
92
- if (!link) return null
93
- if (link.type === 'custom' && link.url) return link.url
94
-
95
- if (link.type === 'reference' && link.reference?.value) {
96
- const { relationTo, value } = link.reference
97
- if (relationTo === 'pages') return getPageURLFromDoc(value)
98
-
99
- const slug = getDocumentSlug(value)
100
- if (!slug) return null
101
- const basePath = getCollectionPath(relationTo)
102
- return basePath ? `/${basePath}/${slug}` : `/${slug}`
103
- }
104
-
105
- return null
106
- }
107
-
108
- const isExternalHref = (href: string): boolean =>
109
- href.startsWith('http://') ||
110
- href.startsWith('https://') ||
111
- href.startsWith('mailto:') ||
112
- href.startsWith('tel:')
113
-
114
- export const DDHGHighlightArc: React.FC<DDHGHighlightArcProps> = ({
115
- gridColumns = '1',
116
- highlights,
117
- }) => {
118
- const [hoveredIdx, setHoveredIdx] = useState<number | null>(null)
119
-
120
- if (!Array.isArray(highlights) || highlights.length === 0) return null
121
-
122
- return (
123
- <div className={cn('grid origin-top scale-100 !gap-0', getGridClassName(gridColumns))}>
124
- {highlights.map((h, i) => {
125
- const isHovered = hoveredIdx === i
126
- const bgImage = typeof h.bgImage === 'object' && h.bgImage !== null ? h.bgImage : null
127
- const href = resolveHref(h.link || null)
128
- const label = h.link?.label || 'View Archive'
129
- const target = h.link?.newTab ? '_blank' : undefined
130
- const rel = h.link?.newTab ? 'noopener noreferrer' : undefined
131
- const isExternal = href ? isExternalHref(href) : false
132
- const key = h.id != null ? `id-${h.id}` : `idx-${i}`
133
-
134
- const cardClassName = cn(
135
- 'group relative block h-[4rem] overflow-hidden rounded-[0px] border border-border px-5 py-5 !no-underline shadow-[0_18px_60px_rgba(31,31,30,0.12),inset_0_1px_0_rgba(255,255,255,0.62)] transition-all duration-500 ease-out hover:shadow-[0_24px_74px_rgba(31,31,30,0.16),inset_0_1px_0_rgba(255,255,255,0.72)]',
136
- 'before:pointer-events-none before:absolute before:inset-0 before:z-[1] --before:bg-[linear-gradient(90deg,rgba(255,255,255,0.62),rgba(255,255,255,0.22)_42%,rgba(255,255,255,0.06))] before:transition-opacity before:duration-500',
137
- 'after:pointer-events-none after:absolute after:inset-0 after:z-[2] after:opacity-[0.08] after:mix-blend-multiply --after:[background-image:radial-gradient(circle_at_20%_30%,rgba(31,31,30,0.5)_0_1px,transparent_1px),--radial-gradient(circle_at_80%_70%,rgba(255,255,255,0.8)_0_1px,transparent_1px)] after:[background-size:14px_14px,19px_19px]',
138
- href
139
- ? 'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-500/40 focus-visible:ring-offset-2'
140
- : 'cursor-default',
141
- )
142
-
143
- const buttonClassName = cn(
144
- 'inline-flex items-center gap-2 whitespace-nowrap text-xs font-medium tracking-[0.04em] text-stone-700 transition-all duration-300',
145
- 'group-hover:text-[#1F1F1E]',
146
- isHovered ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-100',
147
- !href && 'opacity-40',
148
- )
149
-
150
- const cardContent = (
151
- <>
152
- <div className="HighCardsContent absolute inset-0 z-0 bg-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
153
-
154
- {bgImage?.url && (
155
- <div className="HighCardImg absolute inset-0 z-[1] overflow-hidden opacity-0 transition-opacity duration-500 ease-out group-hover:opacity-100">
156
- <div className="absolute left-0 top-0 h-[260%] w-full transition-transform duration-700 ease-out group-hover:-translate-y-[60%]">
157
- <Image
158
- src={bgImage.url}
159
- alt={bgImage.alt ?? h.title ?? ''}
160
- fill
161
- priority={Boolean(h.priority)}
162
- className="object-cover"
163
- sizes="(max-width: 768px) 100vw, 400px"
164
- />
165
- </div>
166
-
167
- {h.title && (
168
- <p className="HighCardTitle absolute left-0 top-0 z-10 px-4 py-4 text-[10px] font-semibold leading-snug text-stone-100 dark:text-stone-100 transition-colors duration-300">
169
- {h.title}
170
- </p>
171
- )}
172
-
173
- <div className="HighCardImgBg absolute inset-0 z-[2] bg-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
174
- <div className="absolute mix-blend-darken inset-0 z-[2] bg-gradient-to-r from-[#151515bf] via-[#20202029] to-[#00000000] opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
175
- </div>
176
- )}
177
-
178
- <div className="card-info relative z-[3] translate-y-0 transition-transform duration-500 group-hover:translate-y-[-7rem]">
179
- <div className="relative z-10 flex h-full items-start justify-between gap-6">
180
- <span className="relative w-full ml-auto max-w-fit right-0 -top-2 block origin-top-right scale-80">
181
- <span className={buttonClassName}>
182
- <span className="block">{label}</span>
183
- <ArrowRight
184
- size={14}
185
- className="transition-transform duration-300 group-hover:translate-x-1"
186
- />
187
- </span>
188
- </span>
189
- </div>
190
- </div>
191
- </>
192
- )
193
-
194
- if (!href) {
195
- return (
196
- <article
197
- key={key}
198
- className={cardClassName}
199
- onMouseEnter={() => setHoveredIdx(i)}
200
- onMouseLeave={() => setHoveredIdx(null)}
201
- >
202
- {cardContent}
203
- </article>
204
- )
205
- }
206
-
207
- if (isExternal) {
208
- return (
209
- <a
210
- key={key}
211
- href={href}
212
- target={target}
213
- rel={rel}
214
- aria-label={label}
215
- className={cardClassName}
216
- onMouseEnter={() => setHoveredIdx(i)}
217
- onMouseLeave={() => setHoveredIdx(null)}
218
- >
219
- {cardContent}
220
- </a>
221
- )
222
- }
223
-
224
- return (
225
- <Link
226
- key={key}
227
- href={href}
228
- target={target}
229
- rel={rel}
230
- aria-label={label}
231
- className={cardClassName}
232
- onMouseEnter={() => setHoveredIdx(i)}
233
- onMouseLeave={() => setHoveredIdx(null)}
234
- >
235
- {cardContent}
236
- </Link>
237
- )
238
- })}
239
- </div>
240
- )
241
- }
242
-
243
- export default DDHGHighlightArc
1
+ 'use client'
2
+
3
+ import { ArrowRight } from 'lucide-react'
4
+ import Image from 'next/image'
5
+ import Link from 'next/link'
6
+ import React, { useState } from 'react'
7
+
8
+ import { cn } from '@/utilities/ui'
9
+
10
+ type PortableDoc = {
11
+ slug?: string | null
12
+ breadcrumbs?: Array<{ url?: string | null }> | null
13
+ [key: string]: unknown
14
+ }
15
+
16
+ type Media = {
17
+ url?: string | null
18
+ alt?: string | null
19
+ [key: string]: unknown
20
+ }
21
+
22
+ type LinkReference =
23
+ | { relationTo: 'pages'; value: number | PortableDoc }
24
+ | { relationTo: 'posts'; value: number | PortableDoc }
25
+ | { relationTo: 'projects'; value: number | PortableDoc }
26
+ | { relationTo: 'thoughts'; value: number | PortableDoc }
27
+
28
+ type HighlightArchiveCardsLink = {
29
+ type?: 'reference' | 'custom' | null
30
+ reference?: LinkReference | null
31
+ url?: string | null
32
+ label?: string | null
33
+ newTab?: boolean | null
34
+ } | null
35
+
36
+ type HighlightArchiveCardsItem = {
37
+ id?: string | null
38
+ code?: string | null
39
+ title?: string | null
40
+ type?: string | null
41
+ bgImage?: number | Media | null
42
+ priority?: boolean | null
43
+ link?: HighlightArchiveCardsLink
44
+ }
45
+
46
+ export type DDHGHighlightArcProps = {
47
+ gridColumns?: '1' | '2' | '3' | '4' | null
48
+ highlights?: HighlightArchiveCardsItem[] | null
49
+ }
50
+
51
+ const getGridClassName = (gridColumns?: DDHGHighlightArcProps['gridColumns']) => {
52
+ switch (gridColumns) {
53
+ case '2':
54
+ return 'grid-cols-1 md:grid-cols-2'
55
+ case '3':
56
+ return 'grid-cols-1 md:grid-cols-2 xl:grid-cols-3'
57
+ case '4':
58
+ return 'grid-cols-1 md:grid-cols-2 xl:grid-cols-4'
59
+ case '1':
60
+ default:
61
+ return 'grid-cols-1'
62
+ }
63
+ }
64
+
65
+ const getPageURLFromDoc = (doc: PortableDoc | number): string | null => {
66
+ if (typeof doc === 'number') return null
67
+
68
+ const crumbs = Array.isArray(doc?.breadcrumbs) ? doc.breadcrumbs : []
69
+ if (crumbs.length) {
70
+ const last = crumbs[crumbs.length - 1]
71
+ if (typeof last?.url === 'string' && last.url) return last.url
72
+ }
73
+
74
+ const slug = doc?.slug
75
+ if (!slug || slug === 'home') return '/'
76
+ return slug.startsWith('/') ? slug : `/${slug}`
77
+ }
78
+
79
+ const getDocumentSlug = (doc: PortableDoc | number): string | null => {
80
+ if (typeof doc === 'number') return null
81
+ return doc?.slug || null
82
+ }
83
+
84
+ const getCollectionPath = (relationTo: LinkReference['relationTo']): string => {
85
+ if (relationTo === 'posts') return 'thoughts'
86
+ if (relationTo === 'thoughts') return 'thoughts'
87
+ if (relationTo === 'projects') return 'archives'
88
+ return ''
89
+ }
90
+
91
+ const resolveHref = (link: HighlightArchiveCardsLink): string | null => {
92
+ if (!link) return null
93
+ if (link.type === 'custom' && link.url) return link.url
94
+
95
+ if (link.type === 'reference' && link.reference?.value) {
96
+ const { relationTo, value } = link.reference
97
+ if (relationTo === 'pages') return getPageURLFromDoc(value)
98
+
99
+ const slug = getDocumentSlug(value)
100
+ if (!slug) return null
101
+ const basePath = getCollectionPath(relationTo)
102
+ return basePath ? `/${basePath}/${slug}` : `/${slug}`
103
+ }
104
+
105
+ return null
106
+ }
107
+
108
+ const isExternalHref = (href: string): boolean =>
109
+ href.startsWith('http://') ||
110
+ href.startsWith('https://') ||
111
+ href.startsWith('mailto:') ||
112
+ href.startsWith('tel:')
113
+
114
+ export const DDHGHighlightArc: React.FC<DDHGHighlightArcProps> = ({
115
+ gridColumns = '1',
116
+ highlights,
117
+ }) => {
118
+ const [hoveredIdx, setHoveredIdx] = useState<number | null>(null)
119
+
120
+ if (!Array.isArray(highlights) || highlights.length === 0) return null
121
+
122
+ return (
123
+ <div className={cn('grid origin-top scale-100 gap-0!', getGridClassName(gridColumns))}>
124
+ {highlights.map((h, i) => {
125
+ const isHovered = hoveredIdx === i
126
+ const bgImage = typeof h.bgImage === 'object' && h.bgImage !== null ? h.bgImage : null
127
+ const href = resolveHref(h.link || null)
128
+ const label = h.link?.label || 'View Archive'
129
+ const target = h.link?.newTab ? '_blank' : undefined
130
+ const rel = h.link?.newTab ? 'noopener noreferrer' : undefined
131
+ const isExternal = href ? isExternalHref(href) : false
132
+ const key = h.id != null ? `id-${h.id}` : `idx-${i}`
133
+
134
+ const cardClassName = cn(
135
+ 'group relative block h-[4rem] overflow-hidden rounded-md squircle border border-border bg-stone-100/50 px-5 py-5 !no-underline shadow-[0_18px_60px_rgba(31,31,30,0.12),inset_0_1px_0_rgba(255,255,255,0.62)] transition-all duration-500 ease-out hover:-translate-y-1 hover:shadow-[0_24px_74px_rgba(31,31,30,0.16),inset_0_1px_0_rgba(255,255,255,0.72)] dark:bg-stone-900/50',
136
+ 'before:pointer-events-none before:absolute before:inset-0 before:z-[1] --before:bg-[linear-gradient(90deg,rgba(255,255,255,0.62),rgba(255,255,255,0.22)_42%,rgba(255,255,255,0.06))] before:transition-opacity before:duration-500',
137
+ 'after:pointer-events-none after:absolute after:inset-0 after:z-2 after:opacity-[0.08] after:mix-blend-multiply --after:[background-image:radial-gradient(circle_at_20%_30%,rgba(31,31,30,0.5)_0_1px,transparent_1px),--radial-gradient(circle_at_80%_70%,rgba(255,255,255,0.8)_0_1px,transparent_1px)] after:[background-size:14px_14px,19px_19px]',
138
+ href
139
+ ? 'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-500/40 focus-visible:ring-offset-2'
140
+ : 'cursor-default',
141
+ )
142
+
143
+ const buttonClassName = cn(
144
+ 'inline-flex items-center gap-2 whitespace-nowrap text-sm font-medium tracking-[0.04em] text-stone-700 transition-all duration-300',
145
+ 'group-hover:text-[#1F1F1E]',
146
+ isHovered ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-100',
147
+ !href && 'opacity-40',
148
+ )
149
+
150
+ const cardContent = (
151
+ <>
152
+ <div className="HighCardsContent absolute inset-0 z-0 bg-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
153
+
154
+ {bgImage?.url && (
155
+ <div className="HighCardImg absolute inset-0 z-1 overflow-hidden opacity-0 transition-opacity duration-500 ease-out group-hover:opacity-100">
156
+ <div className="absolute left-0 top-0 h-[260%] w-full transition-transform duration-700 ease-out group-hover:translate-y-[-60%]">
157
+ <Image
158
+ src={bgImage.url}
159
+ alt={bgImage.alt ?? h.title ?? ''}
160
+ fill
161
+ priority={Boolean(h.priority)}
162
+ className="object-cover"
163
+ sizes="(max-width: 768px) 100vw, 400px"
164
+ />
165
+ </div>
166
+
167
+ {h.title && (
168
+ <p className="HighCardTitle absolute left-0 top-0 z-10 px-4 py-4 text-sm font-semibold leading-snug text-stone-100 dark:text-stone-100 transition-colors duration-300">
169
+ {h.title}
170
+ </p>
171
+ )}
172
+
173
+ <div className="HighCardImgBg absolute inset-0 z-2 bg-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
174
+ <div className="absolute mix-blend-darken inset-0 z-2 bg-linear-to-r from-[#151515bf] via-[#20202029] to-[#00000000] opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
175
+ </div>
176
+ )}
177
+
178
+ <div className="card-info relative z-3 translate-y-0 transition-transform duration-500 group-hover:-translate-y-28">
179
+ <div className="relative z-10 flex h-full items-start justify-between gap-6">
180
+ <span className="relative w-full ml-auto max-w-fit right-0 -top-2 block origin-top-right scale-95">
181
+ <span className={buttonClassName}>
182
+ <span className="block">{label}</span>
183
+ <ArrowRight
184
+ size={14}
185
+ className="transition-transform duration-300 group-hover:translate-x-1"
186
+ />
187
+ </span>
188
+ </span>
189
+ </div>
190
+ </div>
191
+ </>
192
+ )
193
+
194
+ if (!href) {
195
+ return (
196
+ <article
197
+ key={key}
198
+ className={cardClassName}
199
+ onMouseEnter={() => setHoveredIdx(i)}
200
+ onMouseLeave={() => setHoveredIdx(null)}
201
+ >
202
+ {cardContent}
203
+ </article>
204
+ )
205
+ }
206
+
207
+ if (isExternal) {
208
+ return (
209
+ <a
210
+ key={key}
211
+ href={href}
212
+ target={target}
213
+ rel={rel}
214
+ aria-label={label}
215
+ className={cardClassName}
216
+ onMouseEnter={() => setHoveredIdx(i)}
217
+ onMouseLeave={() => setHoveredIdx(null)}
218
+ >
219
+ {cardContent}
220
+ </a>
221
+ )
222
+ }
223
+
224
+ return (
225
+ <Link
226
+ key={key}
227
+ href={href}
228
+ target={target}
229
+ rel={rel}
230
+ aria-label={label}
231
+ className={cardClassName}
232
+ onMouseEnter={() => setHoveredIdx(i)}
233
+ onMouseLeave={() => setHoveredIdx(null)}
234
+ >
235
+ {cardContent}
236
+ </Link>
237
+ )
238
+ })}
239
+ </div>
240
+ )
241
+ }
242
+
243
+ export default DDHGHighlightArc