@opensite/ui 1.8.6 → 1.8.8

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.
@@ -1,251 +1,392 @@
1
1
  "use client";
2
- import * as React from 'react';
3
- import React__default, { useMemo } from 'react';
2
+ import * as React3 from 'react';
3
+ import React3__default, { useMemo } from 'react';
4
4
  import { motion } from 'framer-motion';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { Img } from '@page-speed/img';
8
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
9
  import { cva } from 'class-variance-authority';
9
- import { jsx, jsxs } from 'react/jsx-runtime';
10
10
 
11
11
  // components/blocks/about/about-story-expertise.tsx
12
12
  function cn(...inputs) {
13
13
  return twMerge(clsx(inputs));
14
14
  }
15
- function getNestedCardBg(parentBg, variant = "muted", options) {
16
- const isDark = parentBg === "dark" || parentBg === "secondary" || parentBg === "primary";
17
- if (isDark) {
18
- switch (variant) {
19
- case "muted":
20
- return "bg-background";
21
- case "card":
22
- return "bg-card";
23
- case "accent":
24
- return "bg-accent";
25
- case "subtle":
26
- return "bg-background/50";
27
- }
28
- } else {
29
- switch (variant) {
30
- case "muted":
31
- return "bg-muted";
32
- case "card":
33
- return "bg-card";
34
- case "accent":
35
- return "bg-accent";
36
- case "subtle":
37
- return "bg-muted/50";
38
- }
15
+ var maxWidthStyles = {
16
+ sm: "max-w-screen-sm",
17
+ md: "max-w-screen-md",
18
+ lg: "max-w-screen-lg",
19
+ xl: "max-w-7xl",
20
+ "2xl": "max-w-screen-2xl",
21
+ "4xl": "max-w-[1536px]",
22
+ full: "max-w-full"
23
+ };
24
+ var Container = React3__default.forwardRef(
25
+ ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
26
+ const Component = as;
27
+ return /* @__PURE__ */ jsx(
28
+ Component,
29
+ {
30
+ ref,
31
+ className: cn(
32
+ "mx-auto w-full px-2 sm:px-4 lg:px-8",
33
+ maxWidthStyles[maxWidth],
34
+ className
35
+ ),
36
+ ...props,
37
+ children
38
+ }
39
+ );
39
40
  }
40
- }
41
- function getTextColor(parentBg, variant = "default", options) {
42
- const isDark = parentBg === "dark" || parentBg === "secondary" || parentBg === "primary";
43
- if (isDark) {
44
- switch (variant) {
45
- case "default":
46
- return "text-foreground";
47
- case "muted":
48
- return "text-foreground/80";
49
- case "subtle":
50
- return "text-foreground/60";
51
- case "accent":
52
- return "text-accent-foreground";
53
- }
54
- } else {
55
- switch (variant) {
56
- case "default":
57
- return "text-foreground";
58
- case "muted":
59
- return "text-muted-foreground";
60
- case "subtle":
61
- return "text-muted-foreground/70";
62
- case "accent":
63
- return "text-primary";
64
- }
41
+ );
42
+ Container.displayName = "Container";
43
+
44
+ // lib/patternSvgs.ts
45
+ var patternSvgs = {
46
+ squareAltGrid: "https://cdn.ing/assets/files/record/286187/4gpn0yq2ptra8iwlvmwwv860ggwv",
47
+ grid1: "https://cdn.ing/assets/files/record/286186/nbdflpgp4ostrno079hygibsflp3",
48
+ noise: "https://cdn.ing/assets/i/r/286188/zrqcp9hynh3j7p2laihwzfbujgrl/noise.png",
49
+ dots: "https://cdn.ing/assets/files/record/286198/yfsjx9thvtxzhl2qtshxyhkrm524",
50
+ dotPattern: "https://cdn.ing/assets/files/record/286192/7ig0cku8aqbboiza8nuk6hw0nnsr",
51
+ dotPattern2: "https://cdn.ing/assets/files/record/286189/arez6gd2s7isn9i1o6c7sexdq7bl",
52
+ circles: "https://cdn.ing/assets/files/record/286190/gtmia3sncjtzetdshc20zf1d3c17",
53
+ waves: "https://cdn.ing/assets/files/record/286191/mqlb33fzxz9cdth1bx7if0wmpkp1",
54
+ crossPattern: "https://cdn.ing/assets/files/record/286193/9yfqwdbnqaipbp7fsb3wbzzmq472",
55
+ architect: "https://cdn.ing/assets/files/record/286194/vgs88ugpvyhxu13wqgy0acvae6re",
56
+ tinyCheckers: "https://cdn.ing/assets/files/record/286195/65efaknsw8kcpf9o3c2gybytsl5b",
57
+ p6: "https://cdn.ing/assets/i/r/286196/6kl0rqnd6mjk8j7e525fo8fo0vkc/p6.webp"
58
+ };
59
+ var maskTop = "radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)";
60
+ var maskBottom = "radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)";
61
+ var maskCenter = "radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)";
62
+ var maskTopLeft = "radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)";
63
+ var maskTopRight = "radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)";
64
+ var maskBottomLeft = "radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)";
65
+ var maskBottomRight = "radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)";
66
+ var circuitBoardPattern = (id, mask) => /* @__PURE__ */ jsxs(
67
+ "svg",
68
+ {
69
+ className: "h-full w-full",
70
+ xmlns: "http://www.w3.org/2000/svg",
71
+ style: mask ? {
72
+ maskImage: mask,
73
+ WebkitMaskImage: mask
74
+ } : void 0,
75
+ children: [
76
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
77
+ "pattern",
78
+ {
79
+ id,
80
+ x: "0",
81
+ y: "0",
82
+ width: "100",
83
+ height: "100",
84
+ patternUnits: "userSpaceOnUse",
85
+ children: [
86
+ /* @__PURE__ */ jsx(
87
+ "path",
88
+ {
89
+ d: "M0 50h40M60 50h40M50 0v40M50 60v40",
90
+ stroke: "hsl(var(--muted))",
91
+ strokeWidth: "1",
92
+ fill: "none"
93
+ }
94
+ ),
95
+ /* @__PURE__ */ jsx("circle", { cx: "50", cy: "50", r: "3", fill: "hsl(var(--muted))" }),
96
+ /* @__PURE__ */ jsx("circle", { cx: "0", cy: "50", r: "2", fill: "hsl(var(--muted))" }),
97
+ /* @__PURE__ */ jsx("circle", { cx: "100", cy: "50", r: "2", fill: "hsl(var(--muted))" }),
98
+ /* @__PURE__ */ jsx("circle", { cx: "50", cy: "0", r: "2", fill: "hsl(var(--muted))" }),
99
+ /* @__PURE__ */ jsx("circle", { cx: "50", cy: "100", r: "2", fill: "hsl(var(--muted))" })
100
+ ]
101
+ }
102
+ ) }),
103
+ /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: `url(#${id})` })
104
+ ]
65
105
  }
66
- }
67
- function getAccentColor(parentBg, options) {
68
- const isDark = parentBg === "dark" || parentBg === "secondary" || parentBg === "primary";
69
- return isDark ? "text-accent-foreground" : "text-primary";
70
- }
71
- function normalizePhoneNumber(input) {
72
- const trimmed = input.trim();
73
- if (trimmed.toLowerCase().startsWith("tel:")) {
74
- return trimmed;
106
+ );
107
+ var gridDotsPattern = (id, mask) => /* @__PURE__ */ jsxs(
108
+ "svg",
109
+ {
110
+ className: "h-full w-full",
111
+ xmlns: "http://www.w3.org/2000/svg",
112
+ style: mask ? {
113
+ maskImage: mask,
114
+ WebkitMaskImage: mask
115
+ } : void 0,
116
+ children: [
117
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
118
+ "pattern",
119
+ {
120
+ id,
121
+ x: "0",
122
+ y: "0",
123
+ width: "40",
124
+ height: "40",
125
+ patternUnits: "userSpaceOnUse",
126
+ children: [
127
+ /* @__PURE__ */ jsx(
128
+ "path",
129
+ {
130
+ d: "M0 20h40M20 0v40",
131
+ stroke: "hsl(var(--muted))",
132
+ strokeWidth: "0.5",
133
+ fill: "none"
134
+ }
135
+ ),
136
+ /* @__PURE__ */ jsx("circle", { cx: "20", cy: "20", r: "2", fill: "hsl(var(--muted))" })
137
+ ]
138
+ }
139
+ ) }),
140
+ /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: `url(#${id})` })
141
+ ]
75
142
  }
76
- const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
77
- if (match) {
78
- const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
79
- const extension = match[3];
80
- const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
81
- const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
82
- return `tel:${withExtension}`;
143
+ );
144
+ var gridPattern = (size, mask) => /* @__PURE__ */ jsx(
145
+ "div",
146
+ {
147
+ className: "h-full w-full bg-[linear-gradient(to_right,_hsl(var(--muted))_1px,_transparent_1px),linear-gradient(to_bottom,_hsl(var(--muted))_1px,_transparent_1px)]",
148
+ style: {
149
+ backgroundSize: `${size}px ${size}px`,
150
+ ...mask ? {
151
+ maskImage: mask,
152
+ WebkitMaskImage: mask
153
+ } : {}
154
+ }
83
155
  }
84
- const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
85
- return `tel:${cleaned}`;
86
- }
87
- function normalizeEmail(input) {
88
- const trimmed = input.trim();
89
- if (trimmed.toLowerCase().startsWith("mailto:")) {
90
- return trimmed;
156
+ );
157
+ var diagonalCrossPattern = (mask) => /* @__PURE__ */ jsx(
158
+ "div",
159
+ {
160
+ className: "h-full w-full",
161
+ style: {
162
+ backgroundImage: "repeating-linear-gradient(45deg, transparent, transparent 32px, hsl(var(--muted)) 32px, hsl(var(--muted)) 33px), repeating-linear-gradient(135deg, transparent, transparent 32px, hsl(var(--muted)) 32px, hsl(var(--muted)) 33px)",
163
+ ...mask ? {
164
+ maskImage: mask,
165
+ WebkitMaskImage: mask
166
+ } : {}
167
+ }
91
168
  }
92
- return `mailto:${trimmed}`;
93
- }
94
- function isEmail(input) {
95
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
96
- return emailRegex.test(input.trim());
97
- }
98
- function isPhoneNumber(input) {
99
- const trimmed = input.trim();
100
- if (trimmed.toLowerCase().startsWith("tel:")) {
101
- return true;
169
+ );
170
+ var dashedGridMaskBase = "repeating-linear-gradient(to right, black 0px, black 3px, transparent 3px, transparent 8px), repeating-linear-gradient(to bottom, black 0px, black 3px, transparent 3px, transparent 8px)";
171
+ var dashedGridPattern = (fadeMask) => {
172
+ const mask = fadeMask ? `${dashedGridMaskBase}, ${fadeMask}` : dashedGridMaskBase;
173
+ return /* @__PURE__ */ jsx(
174
+ "div",
175
+ {
176
+ className: "h-full w-full",
177
+ style: {
178
+ backgroundImage: "linear-gradient(to right, hsl(var(--muted)) 1px, transparent 1px), linear-gradient(to bottom, hsl(var(--muted)) 1px, transparent 1px)",
179
+ backgroundSize: "20px 20px",
180
+ backgroundPosition: "0 0, 0 0",
181
+ maskImage: mask,
182
+ WebkitMaskImage: mask,
183
+ maskComposite: "intersect",
184
+ WebkitMaskComposite: "source-in"
185
+ }
186
+ }
187
+ );
188
+ };
189
+ var gradientGlow = (position) => /* @__PURE__ */ jsx(
190
+ "div",
191
+ {
192
+ className: cn(
193
+ "pointer-events-none absolute left-1/2 z-0 aspect-square w-3/4 -translate-x-1/2 rounded-full opacity-50 blur-3xl",
194
+ position === "top" ? "-top-1/4" : "-bottom-1/4"
195
+ ),
196
+ style: {
197
+ background: "radial-gradient(circle, hsl(var(--primary)) 0%, transparent 70%)"
198
+ }
102
199
  }
103
- const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
104
- return phoneRegex.test(trimmed);
105
- }
106
- function isInternalUrl(href) {
107
- if (typeof window === "undefined") {
108
- return href.startsWith("/") && !href.startsWith("//");
200
+ );
201
+ var spotlight = (position) => /* @__PURE__ */ jsx(
202
+ "div",
203
+ {
204
+ className: cn(
205
+ "pointer-events-none absolute top-1/2 z-0 aspect-square w-3/4 -translate-y-1/2 rounded-full opacity-40 blur-3xl",
206
+ position === "left" ? "-left-1/4" : "-right-1/4"
207
+ ),
208
+ style: {
209
+ background: "radial-gradient(circle, hsl(var(--primary)) 0%, transparent 70%)"
210
+ }
109
211
  }
110
- const trimmed = href.trim();
111
- if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
112
- return true;
212
+ );
213
+ var patternOverlays = {
214
+ circuitBoardBasic: () => circuitBoardPattern("circuit-board-basic"),
215
+ circuitBoardFadeTop: () => circuitBoardPattern("circuit-board-fade-top", maskTop),
216
+ circuitBoardFadeBottom: () => circuitBoardPattern("circuit-board-fade-bottom", maskBottom),
217
+ circuitBoardFadeCenter: () => circuitBoardPattern("circuit-board-fade-center", maskCenter),
218
+ circuitBoardFadeTopLeft: () => circuitBoardPattern("circuit-board-fade-top-left", maskTopLeft),
219
+ circuitBoardFadeTopRight: () => circuitBoardPattern("circuit-board-fade-top-right", maskTopRight),
220
+ circuitBoardFadeBottomLeft: () => circuitBoardPattern("circuit-board-fade-bottom-left", maskBottomLeft),
221
+ circuitBoardFadeBottomRight: () => circuitBoardPattern("circuit-board-fade-bottom-right", maskBottomRight),
222
+ dashedGridBasic: () => dashedGridPattern(),
223
+ dashedGridFadeTop: () => dashedGridPattern(maskTop),
224
+ dashedGridFadeBottom: () => dashedGridPattern(maskBottom),
225
+ dashedGridFadeCenter: () => dashedGridPattern(maskCenter),
226
+ dashedGridFadeTopLeft: () => dashedGridPattern(maskTopLeft),
227
+ dashedGridFadeTopRight: () => dashedGridPattern(maskTopRight),
228
+ dashedGridFadeBottomLeft: () => dashedGridPattern(maskBottomLeft),
229
+ dashedGridFadeBottomRight: () => dashedGridPattern(maskBottomRight),
230
+ diagonalCrossBasic: () => diagonalCrossPattern(),
231
+ diagonalCrossFadeTop: () => diagonalCrossPattern(maskTop),
232
+ diagonalCrossFadeBottom: () => diagonalCrossPattern(maskBottom),
233
+ diagonalCrossFadeCenter: () => diagonalCrossPattern(maskCenter),
234
+ diagonalCrossFadeTopLeft: () => diagonalCrossPattern(maskTopLeft),
235
+ diagonalCrossFadeTopRight: () => diagonalCrossPattern(maskTopRight),
236
+ diagonalCrossFadeBottomLeft: () => diagonalCrossPattern(maskBottomLeft),
237
+ diagonalCrossFadeBottomRight: () => diagonalCrossPattern(maskBottomRight),
238
+ gridBasic: () => gridPattern(40),
239
+ gridFadeTop: () => gridPattern(32, maskTop),
240
+ gridFadeBottom: () => gridPattern(32, maskBottom),
241
+ gridFadeCenter: () => gridPattern(40, maskCenter),
242
+ gridFadeTopLeft: () => gridPattern(32, maskTopLeft),
243
+ gridFadeTopRight: () => gridPattern(32, maskTopRight),
244
+ gridFadeBottomLeft: () => gridPattern(32, maskBottomLeft),
245
+ gridFadeBottomRight: () => gridPattern(32, maskBottomRight),
246
+ gridDotsBasic: () => gridDotsPattern("grid-dots-basic"),
247
+ gridDotsFadeCenter: () => gridDotsPattern("grid-dots-fade-center", maskCenter),
248
+ gradientGlowTop: () => gradientGlow("top"),
249
+ gradientGlowBottom: () => gradientGlow("bottom"),
250
+ spotlightLeft: () => spotlight("left"),
251
+ spotlightRight: () => spotlight("right")
252
+ };
253
+ var inlinePatternStyles = {
254
+ radialGradientTop: {
255
+ background: "radial-gradient(125% 125% at 50% 10%, hsl(var(--background)) 40%, hsl(var(--primary)) 100%)"
256
+ },
257
+ radialGradientBottom: {
258
+ background: "radial-gradient(125% 125% at 50% 90%, hsl(var(--background)) 40%, hsl(var(--primary)) 100%)"
113
259
  }
114
- try {
115
- const url = new URL(trimmed, window.location.href);
116
- const currentOrigin = window.location.origin;
117
- const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
118
- return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
119
- } catch {
120
- return false;
260
+ };
261
+ function PatternBackground({
262
+ pattern,
263
+ opacity = 0.08,
264
+ className,
265
+ style
266
+ }) {
267
+ if (!pattern) {
268
+ return null;
121
269
  }
122
- }
123
- function toRelativePath(href) {
124
- if (typeof window === "undefined") {
125
- return href;
270
+ if (pattern in inlinePatternStyles) {
271
+ const inlineStyle = inlinePatternStyles[pattern];
272
+ return /* @__PURE__ */ jsx(
273
+ "div",
274
+ {
275
+ className: cn("pointer-events-none absolute inset-0 z-0", className),
276
+ style: { ...inlineStyle, opacity, ...style },
277
+ "aria-hidden": "true"
278
+ }
279
+ );
126
280
  }
127
- const trimmed = href.trim();
128
- if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
129
- return trimmed;
281
+ if (pattern in patternOverlays) {
282
+ const Overlay = patternOverlays[pattern];
283
+ return /* @__PURE__ */ jsx(
284
+ "div",
285
+ {
286
+ className: cn("pointer-events-none absolute inset-0 z-0", className),
287
+ style: { opacity, ...style },
288
+ "aria-hidden": "true",
289
+ children: Overlay()
290
+ }
291
+ );
130
292
  }
131
- try {
132
- const url = new URL(trimmed, window.location.href);
133
- const currentOrigin = window.location.origin;
134
- const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
135
- if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
136
- return url.pathname + url.search + url.hash;
293
+ const patternUrl = pattern in patternSvgs ? patternSvgs[pattern] : pattern;
294
+ return /* @__PURE__ */ jsx(
295
+ "div",
296
+ {
297
+ className: cn("pointer-events-none absolute inset-0 z-0", className),
298
+ style: {
299
+ backgroundImage: `url(${patternUrl})`,
300
+ backgroundRepeat: "repeat",
301
+ backgroundSize: "auto",
302
+ opacity,
303
+ ...style
304
+ },
305
+ "aria-hidden": "true"
137
306
  }
138
- } catch {
139
- }
140
- return trimmed;
307
+ );
141
308
  }
142
- function useNavigation({
143
- href,
144
- onClick
145
- } = {}) {
146
- const linkType = React.useMemo(() => {
147
- if (!href || href.trim() === "") {
148
- return onClick ? "none" : "none";
149
- }
150
- const trimmed = href.trim();
151
- if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
152
- return "mailto";
153
- }
154
- if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
155
- return "tel";
156
- }
157
- if (isInternalUrl(trimmed)) {
158
- return "internal";
159
- }
160
- try {
161
- new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
162
- return "external";
163
- } catch {
164
- return "internal";
165
- }
166
- }, [href, onClick]);
167
- const normalizedHref = React.useMemo(() => {
168
- if (!href || href.trim() === "") {
169
- return void 0;
170
- }
171
- const trimmed = href.trim();
172
- switch (linkType) {
173
- case "tel":
174
- return normalizePhoneNumber(trimmed);
175
- case "mailto":
176
- return normalizeEmail(trimmed);
177
- case "internal":
178
- return toRelativePath(trimmed);
179
- case "external":
180
- return trimmed;
181
- default:
182
- return trimmed;
183
- }
184
- }, [href, linkType]);
185
- const target = React.useMemo(() => {
186
- switch (linkType) {
187
- case "external":
188
- return "_blank";
189
- case "internal":
190
- return "_self";
191
- case "mailto":
192
- case "tel":
193
- return void 0;
194
- default:
195
- return void 0;
196
- }
197
- }, [linkType]);
198
- const rel = React.useMemo(() => {
199
- if (linkType === "external") {
200
- return "noopener noreferrer";
201
- }
202
- return void 0;
203
- }, [linkType]);
204
- const isExternal = linkType === "external";
205
- const isInternal = linkType === "internal";
206
- const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
207
- const handleClick = React.useCallback(
208
- (event) => {
209
- if (onClick) {
210
- try {
211
- onClick(event);
212
- } catch (error) {
213
- console.error("Error in user onClick handler:", error);
214
- }
215
- }
216
- if (event.defaultPrevented) {
217
- return;
218
- }
219
- if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
220
- !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
221
- if (typeof window !== "undefined") {
222
- const handler = window.__opensiteNavigationHandler;
223
- if (typeof handler === "function") {
224
- try {
225
- const handled = handler(normalizedHref, event.nativeEvent || event);
226
- if (handled !== false) {
227
- event.preventDefault();
228
- }
229
- } catch (error) {
230
- console.error("Error in navigation handler:", error);
309
+ var backgroundStyles = {
310
+ default: "bg-background text-foreground",
311
+ white: "bg-white text-dark",
312
+ gray: "bg-muted/30 text-foreground",
313
+ dark: "bg-foreground text-background",
314
+ transparent: "bg-transparent text-foreground",
315
+ gradient: "bg-linear-to-br from-primary via-primary/90 to-foreground text-primary-foreground",
316
+ primary: "bg-primary text-primary-foreground",
317
+ secondary: "bg-secondary text-secondary-foreground",
318
+ muted: "bg-muted text-muted-foreground"
319
+ };
320
+ var spacingStyles = {
321
+ none: "py-0 md:py-0",
322
+ sm: "py-12 md:py-16",
323
+ md: "py-16 md:py-24",
324
+ lg: "py-20 md:py-32",
325
+ xl: "py-24 md:py-40"
326
+ };
327
+ var predefinedSpacings = ["none", "sm", "md", "lg", "xl"];
328
+ var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
329
+ var Section = React3__default.forwardRef(
330
+ ({
331
+ id,
332
+ title,
333
+ subtitle,
334
+ children,
335
+ className,
336
+ style,
337
+ background = "default",
338
+ spacing = "lg",
339
+ pattern,
340
+ patternOpacity,
341
+ patternClassName,
342
+ containerClassName,
343
+ containerMaxWidth = "xl",
344
+ ...props
345
+ }, ref) => {
346
+ const effectivePatternOpacity = patternOpacity !== void 0 ? patternOpacity : pattern ? 1 : 0;
347
+ return /* @__PURE__ */ jsxs(
348
+ "section",
349
+ {
350
+ ref,
351
+ id,
352
+ className: cn(
353
+ "relative",
354
+ pattern ? "overflow-hidden" : null,
355
+ backgroundStyles[background],
356
+ isPredefinedSpacing(spacing) ? spacingStyles[spacing] : spacing,
357
+ className
358
+ ),
359
+ style,
360
+ ...props,
361
+ children: [
362
+ /* @__PURE__ */ jsx(
363
+ PatternBackground,
364
+ {
365
+ pattern,
366
+ opacity: effectivePatternOpacity,
367
+ className: patternClassName
231
368
  }
232
- }
233
- }
369
+ ),
370
+ /* @__PURE__ */ jsxs(
371
+ Container,
372
+ {
373
+ maxWidth: containerMaxWidth,
374
+ className: cn("relative z-10", containerClassName),
375
+ children: [
376
+ (title || subtitle) && /* @__PURE__ */ jsxs("div", { className: "mb-6 text-center md:mb-16", children: [
377
+ subtitle && /* @__PURE__ */ jsx("p", { className: "mb-2 text-sm font-semibold uppercase tracking-wider", children: subtitle }),
378
+ title && /* @__PURE__ */ jsx("h2", { className: "text-3xl font-bold tracking-tight md:text-4xl lg:text-5xl", children: title })
379
+ ] }),
380
+ children
381
+ ]
382
+ }
383
+ )
384
+ ]
234
385
  }
235
- },
236
- [onClick, shouldUseRouter, normalizedHref]
237
- );
238
- return {
239
- linkType,
240
- normalizedHref,
241
- target,
242
- rel,
243
- isExternal,
244
- isInternal,
245
- shouldUseRouter,
246
- handleClick
247
- };
248
- }
386
+ );
387
+ }
388
+ );
389
+ Section.displayName = "Section";
249
390
  var baseStyles = [
250
391
  // Layout
251
392
  "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
@@ -388,479 +529,350 @@ var buttonVariants = cva(baseStyles, {
388
529
  size: "default"
389
530
  }
390
531
  });
391
- var Pressable = React.forwardRef(
392
- ({
393
- children,
394
- className,
395
- href,
396
- onClick,
397
- variant,
398
- size,
399
- asButton = false,
400
- fallbackComponentType = "span",
401
- componentType,
402
- "aria-label": ariaLabel,
403
- "aria-describedby": ariaDescribedby,
404
- id,
405
- ...props
406
- }, ref) => {
407
- const navigation = useNavigation({ href, onClick });
408
- const {
409
- normalizedHref,
410
- target,
411
- rel,
412
- linkType,
413
- isInternal,
414
- handleClick
415
- } = navigation;
416
- const shouldRenderLink = normalizedHref && linkType !== "none";
417
- const shouldRenderButton = !shouldRenderLink && onClick;
418
- const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
419
- const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
420
- const shouldApplyButtonStyles = asButton || variant || size;
421
- const combinedClassName = cn(
422
- shouldApplyButtonStyles && buttonVariants({ variant, size }),
423
- className
424
- );
425
- const dataProps = Object.fromEntries(
426
- Object.entries(props).filter(([key]) => key.startsWith("data-"))
427
- );
428
- const buttonDataAttributes = shouldApplyButtonStyles ? {
429
- "data-slot": "button",
430
- "data-variant": variant ?? "default",
431
- "data-size": size ?? "default"
432
- } : {};
433
- const commonProps = {
434
- className: combinedClassName,
435
- onClick: handleClick,
436
- "aria-label": ariaLabel,
437
- "aria-describedby": ariaDescribedby,
438
- id,
439
- ...dataProps,
440
- ...buttonDataAttributes
441
- };
442
- if (finalComponentType === "a" && shouldRenderLink) {
443
- return /* @__PURE__ */ jsx(
444
- "a",
445
- {
446
- ref,
447
- href: normalizedHref,
448
- target,
449
- rel,
450
- ...commonProps,
451
- ...props,
452
- children
453
- }
454
- );
455
- }
456
- if (finalComponentType === "button") {
457
- return /* @__PURE__ */ jsx(
458
- "button",
459
- {
460
- ref,
461
- type: props.type || "button",
462
- ...commonProps,
463
- ...props,
464
- children
465
- }
466
- );
467
- }
468
- if (finalComponentType === "div") {
469
- return /* @__PURE__ */ jsx(
470
- "div",
471
- {
472
- ref,
473
- ...commonProps,
474
- children
475
- }
476
- );
477
- }
478
- return /* @__PURE__ */ jsx(
479
- "span",
480
- {
481
- ref,
482
- ...commonProps,
483
- children
484
- }
485
- );
532
+ function normalizePhoneNumber(input) {
533
+ const trimmed = input.trim();
534
+ if (trimmed.toLowerCase().startsWith("tel:")) {
535
+ return trimmed;
486
536
  }
487
- );
488
- Pressable.displayName = "Pressable";
489
- var maxWidthStyles = {
490
- sm: "max-w-screen-sm",
491
- md: "max-w-screen-md",
492
- lg: "max-w-screen-lg",
493
- xl: "max-w-7xl",
494
- "2xl": "max-w-screen-2xl",
495
- "4xl": "max-w-[1536px]",
496
- full: "max-w-full"
497
- };
498
- var Container = React__default.forwardRef(
499
- ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
500
- const Component = as;
501
- return /* @__PURE__ */ jsx(
502
- Component,
503
- {
504
- ref,
505
- className: cn(
506
- "mx-auto w-full px-2 sm:px-4 lg:px-8",
507
- maxWidthStyles[maxWidth],
508
- className
509
- ),
510
- ...props,
511
- children
512
- }
513
- );
537
+ const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
538
+ if (match) {
539
+ const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
540
+ const extension = match[3];
541
+ const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
542
+ const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
543
+ return `tel:${withExtension}`;
514
544
  }
515
- );
516
- Container.displayName = "Container";
517
-
518
- // lib/patternSvgs.ts
519
- var patternSvgs = {
520
- squareAltGrid: "https://cdn.ing/assets/files/record/286187/4gpn0yq2ptra8iwlvmwwv860ggwv",
521
- grid1: "https://cdn.ing/assets/files/record/286186/nbdflpgp4ostrno079hygibsflp3",
522
- noise: "https://cdn.ing/assets/i/r/286188/zrqcp9hynh3j7p2laihwzfbujgrl/noise.png",
523
- dots: "https://cdn.ing/assets/files/record/286198/yfsjx9thvtxzhl2qtshxyhkrm524",
524
- dotPattern: "https://cdn.ing/assets/files/record/286192/7ig0cku8aqbboiza8nuk6hw0nnsr",
525
- dotPattern2: "https://cdn.ing/assets/files/record/286189/arez6gd2s7isn9i1o6c7sexdq7bl",
526
- circles: "https://cdn.ing/assets/files/record/286190/gtmia3sncjtzetdshc20zf1d3c17",
527
- waves: "https://cdn.ing/assets/files/record/286191/mqlb33fzxz9cdth1bx7if0wmpkp1",
528
- crossPattern: "https://cdn.ing/assets/files/record/286193/9yfqwdbnqaipbp7fsb3wbzzmq472",
529
- architect: "https://cdn.ing/assets/files/record/286194/vgs88ugpvyhxu13wqgy0acvae6re",
530
- tinyCheckers: "https://cdn.ing/assets/files/record/286195/65efaknsw8kcpf9o3c2gybytsl5b",
531
- p6: "https://cdn.ing/assets/i/r/286196/6kl0rqnd6mjk8j7e525fo8fo0vkc/p6.webp"
532
- };
533
- var maskTop = "radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)";
534
- var maskBottom = "radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)";
535
- var maskCenter = "radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)";
536
- var maskTopLeft = "radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)";
537
- var maskTopRight = "radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)";
538
- var maskBottomLeft = "radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)";
539
- var maskBottomRight = "radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)";
540
- var circuitBoardPattern = (id, mask) => /* @__PURE__ */ jsxs(
541
- "svg",
542
- {
543
- className: "h-full w-full",
544
- xmlns: "http://www.w3.org/2000/svg",
545
- style: mask ? {
546
- maskImage: mask,
547
- WebkitMaskImage: mask
548
- } : void 0,
549
- children: [
550
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
551
- "pattern",
552
- {
553
- id,
554
- x: "0",
555
- y: "0",
556
- width: "100",
557
- height: "100",
558
- patternUnits: "userSpaceOnUse",
559
- children: [
560
- /* @__PURE__ */ jsx(
561
- "path",
562
- {
563
- d: "M0 50h40M60 50h40M50 0v40M50 60v40",
564
- stroke: "hsl(var(--muted))",
565
- strokeWidth: "1",
566
- fill: "none"
567
- }
568
- ),
569
- /* @__PURE__ */ jsx("circle", { cx: "50", cy: "50", r: "3", fill: "hsl(var(--muted))" }),
570
- /* @__PURE__ */ jsx("circle", { cx: "0", cy: "50", r: "2", fill: "hsl(var(--muted))" }),
571
- /* @__PURE__ */ jsx("circle", { cx: "100", cy: "50", r: "2", fill: "hsl(var(--muted))" }),
572
- /* @__PURE__ */ jsx("circle", { cx: "50", cy: "0", r: "2", fill: "hsl(var(--muted))" }),
573
- /* @__PURE__ */ jsx("circle", { cx: "50", cy: "100", r: "2", fill: "hsl(var(--muted))" })
574
- ]
575
- }
576
- ) }),
577
- /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: `url(#${id})` })
578
- ]
545
+ const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
546
+ return `tel:${cleaned}`;
547
+ }
548
+ function normalizeEmail(input) {
549
+ const trimmed = input.trim();
550
+ if (trimmed.toLowerCase().startsWith("mailto:")) {
551
+ return trimmed;
552
+ }
553
+ return `mailto:${trimmed}`;
554
+ }
555
+ function isEmail(input) {
556
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
557
+ return emailRegex.test(input.trim());
558
+ }
559
+ function isPhoneNumber(input) {
560
+ const trimmed = input.trim();
561
+ if (trimmed.toLowerCase().startsWith("tel:")) {
562
+ return true;
579
563
  }
580
- );
581
- var gridDotsPattern = (id, mask) => /* @__PURE__ */ jsxs(
582
- "svg",
583
- {
584
- className: "h-full w-full",
585
- xmlns: "http://www.w3.org/2000/svg",
586
- style: mask ? {
587
- maskImage: mask,
588
- WebkitMaskImage: mask
589
- } : void 0,
590
- children: [
591
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
592
- "pattern",
593
- {
594
- id,
595
- x: "0",
596
- y: "0",
597
- width: "40",
598
- height: "40",
599
- patternUnits: "userSpaceOnUse",
600
- children: [
601
- /* @__PURE__ */ jsx(
602
- "path",
603
- {
604
- d: "M0 20h40M20 0v40",
605
- stroke: "hsl(var(--muted))",
606
- strokeWidth: "0.5",
607
- fill: "none"
608
- }
609
- ),
610
- /* @__PURE__ */ jsx("circle", { cx: "20", cy: "20", r: "2", fill: "hsl(var(--muted))" })
611
- ]
612
- }
613
- ) }),
614
- /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: `url(#${id})` })
615
- ]
564
+ const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
565
+ return phoneRegex.test(trimmed);
566
+ }
567
+ function isInternalUrl(href) {
568
+ if (typeof window === "undefined") {
569
+ return href.startsWith("/") && !href.startsWith("//");
616
570
  }
617
- );
618
- var gridPattern = (size, mask) => /* @__PURE__ */ jsx(
619
- "div",
620
- {
621
- className: "h-full w-full bg-[linear-gradient(to_right,_hsl(var(--muted))_1px,_transparent_1px),linear-gradient(to_bottom,_hsl(var(--muted))_1px,_transparent_1px)]",
622
- style: {
623
- backgroundSize: `${size}px ${size}px`,
624
- ...mask ? {
625
- maskImage: mask,
626
- WebkitMaskImage: mask
627
- } : {}
628
- }
571
+ const trimmed = href.trim();
572
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
573
+ return true;
629
574
  }
630
- );
631
- var diagonalCrossPattern = (mask) => /* @__PURE__ */ jsx(
632
- "div",
633
- {
634
- className: "h-full w-full",
635
- style: {
636
- backgroundImage: "repeating-linear-gradient(45deg, transparent, transparent 32px, hsl(var(--muted)) 32px, hsl(var(--muted)) 33px), repeating-linear-gradient(135deg, transparent, transparent 32px, hsl(var(--muted)) 32px, hsl(var(--muted)) 33px)",
637
- ...mask ? {
638
- maskImage: mask,
639
- WebkitMaskImage: mask
640
- } : {}
575
+ try {
576
+ const url = new URL(trimmed, window.location.href);
577
+ const currentOrigin = window.location.origin;
578
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
579
+ return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
580
+ } catch {
581
+ return false;
582
+ }
583
+ }
584
+ function toRelativePath(href) {
585
+ if (typeof window === "undefined") {
586
+ return href;
587
+ }
588
+ const trimmed = href.trim();
589
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
590
+ return trimmed;
591
+ }
592
+ try {
593
+ const url = new URL(trimmed, window.location.href);
594
+ const currentOrigin = window.location.origin;
595
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
596
+ if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
597
+ return url.pathname + url.search + url.hash;
641
598
  }
599
+ } catch {
642
600
  }
643
- );
644
- var dashedGridMaskBase = "repeating-linear-gradient(to right, black 0px, black 3px, transparent 3px, transparent 8px), repeating-linear-gradient(to bottom, black 0px, black 3px, transparent 3px, transparent 8px)";
645
- var dashedGridPattern = (fadeMask) => {
646
- const mask = fadeMask ? `${dashedGridMaskBase}, ${fadeMask}` : dashedGridMaskBase;
647
- return /* @__PURE__ */ jsx(
648
- "div",
649
- {
650
- className: "h-full w-full",
651
- style: {
652
- backgroundImage: "linear-gradient(to right, hsl(var(--muted)) 1px, transparent 1px), linear-gradient(to bottom, hsl(var(--muted)) 1px, transparent 1px)",
653
- backgroundSize: "20px 20px",
654
- backgroundPosition: "0 0, 0 0",
655
- maskImage: mask,
656
- WebkitMaskImage: mask,
657
- maskComposite: "intersect",
658
- WebkitMaskComposite: "source-in"
659
- }
601
+ return trimmed;
602
+ }
603
+ function useNavigation({
604
+ href,
605
+ onClick
606
+ } = {}) {
607
+ const linkType = React3.useMemo(() => {
608
+ if (!href || href.trim() === "") {
609
+ return onClick ? "none" : "none";
660
610
  }
661
- );
662
- };
663
- var gradientGlow = (position) => /* @__PURE__ */ jsx(
664
- "div",
665
- {
666
- className: cn(
667
- "pointer-events-none absolute left-1/2 z-0 aspect-square w-3/4 -translate-x-1/2 rounded-full opacity-50 blur-3xl",
668
- position === "top" ? "-top-1/4" : "-bottom-1/4"
669
- ),
670
- style: {
671
- background: "radial-gradient(circle, hsl(var(--primary)) 0%, transparent 70%)"
611
+ const trimmed = href.trim();
612
+ if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
613
+ return "mailto";
672
614
  }
673
- }
674
- );
675
- var spotlight = (position) => /* @__PURE__ */ jsx(
676
- "div",
677
- {
678
- className: cn(
679
- "pointer-events-none absolute top-1/2 z-0 aspect-square w-3/4 -translate-y-1/2 rounded-full opacity-40 blur-3xl",
680
- position === "left" ? "-left-1/4" : "-right-1/4"
681
- ),
682
- style: {
683
- background: "radial-gradient(circle, hsl(var(--primary)) 0%, transparent 70%)"
615
+ if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
616
+ return "tel";
684
617
  }
685
- }
686
- );
687
- var patternOverlays = {
688
- circuitBoardBasic: () => circuitBoardPattern("circuit-board-basic"),
689
- circuitBoardFadeTop: () => circuitBoardPattern("circuit-board-fade-top", maskTop),
690
- circuitBoardFadeBottom: () => circuitBoardPattern("circuit-board-fade-bottom", maskBottom),
691
- circuitBoardFadeCenter: () => circuitBoardPattern("circuit-board-fade-center", maskCenter),
692
- circuitBoardFadeTopLeft: () => circuitBoardPattern("circuit-board-fade-top-left", maskTopLeft),
693
- circuitBoardFadeTopRight: () => circuitBoardPattern("circuit-board-fade-top-right", maskTopRight),
694
- circuitBoardFadeBottomLeft: () => circuitBoardPattern("circuit-board-fade-bottom-left", maskBottomLeft),
695
- circuitBoardFadeBottomRight: () => circuitBoardPattern("circuit-board-fade-bottom-right", maskBottomRight),
696
- dashedGridBasic: () => dashedGridPattern(),
697
- dashedGridFadeTop: () => dashedGridPattern(maskTop),
698
- dashedGridFadeBottom: () => dashedGridPattern(maskBottom),
699
- dashedGridFadeCenter: () => dashedGridPattern(maskCenter),
700
- dashedGridFadeTopLeft: () => dashedGridPattern(maskTopLeft),
701
- dashedGridFadeTopRight: () => dashedGridPattern(maskTopRight),
702
- dashedGridFadeBottomLeft: () => dashedGridPattern(maskBottomLeft),
703
- dashedGridFadeBottomRight: () => dashedGridPattern(maskBottomRight),
704
- diagonalCrossBasic: () => diagonalCrossPattern(),
705
- diagonalCrossFadeTop: () => diagonalCrossPattern(maskTop),
706
- diagonalCrossFadeBottom: () => diagonalCrossPattern(maskBottom),
707
- diagonalCrossFadeCenter: () => diagonalCrossPattern(maskCenter),
708
- diagonalCrossFadeTopLeft: () => diagonalCrossPattern(maskTopLeft),
709
- diagonalCrossFadeTopRight: () => diagonalCrossPattern(maskTopRight),
710
- diagonalCrossFadeBottomLeft: () => diagonalCrossPattern(maskBottomLeft),
711
- diagonalCrossFadeBottomRight: () => diagonalCrossPattern(maskBottomRight),
712
- gridBasic: () => gridPattern(40),
713
- gridFadeTop: () => gridPattern(32, maskTop),
714
- gridFadeBottom: () => gridPattern(32, maskBottom),
715
- gridFadeCenter: () => gridPattern(40, maskCenter),
716
- gridFadeTopLeft: () => gridPattern(32, maskTopLeft),
717
- gridFadeTopRight: () => gridPattern(32, maskTopRight),
718
- gridFadeBottomLeft: () => gridPattern(32, maskBottomLeft),
719
- gridFadeBottomRight: () => gridPattern(32, maskBottomRight),
720
- gridDotsBasic: () => gridDotsPattern("grid-dots-basic"),
721
- gridDotsFadeCenter: () => gridDotsPattern("grid-dots-fade-center", maskCenter),
722
- gradientGlowTop: () => gradientGlow("top"),
723
- gradientGlowBottom: () => gradientGlow("bottom"),
724
- spotlightLeft: () => spotlight("left"),
725
- spotlightRight: () => spotlight("right")
726
- };
727
- var inlinePatternStyles = {
728
- radialGradientTop: {
729
- background: "radial-gradient(125% 125% at 50% 10%, hsl(var(--background)) 40%, hsl(var(--primary)) 100%)"
730
- },
731
- radialGradientBottom: {
732
- background: "radial-gradient(125% 125% at 50% 90%, hsl(var(--background)) 40%, hsl(var(--primary)) 100%)"
733
- }
734
- };
735
- function PatternBackground({
736
- pattern,
737
- opacity = 0.08,
738
- className,
739
- style
740
- }) {
741
- if (!pattern) {
742
- return null;
743
- }
744
- if (pattern in inlinePatternStyles) {
745
- const inlineStyle = inlinePatternStyles[pattern];
746
- return /* @__PURE__ */ jsx(
747
- "div",
748
- {
749
- className: cn("pointer-events-none absolute inset-0 z-0", className),
750
- style: { ...inlineStyle, opacity, ...style },
751
- "aria-hidden": "true"
618
+ if (isInternalUrl(trimmed)) {
619
+ return "internal";
620
+ }
621
+ try {
622
+ new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
623
+ return "external";
624
+ } catch {
625
+ return "internal";
626
+ }
627
+ }, [href, onClick]);
628
+ const normalizedHref = React3.useMemo(() => {
629
+ if (!href || href.trim() === "") {
630
+ return void 0;
631
+ }
632
+ const trimmed = href.trim();
633
+ switch (linkType) {
634
+ case "tel":
635
+ return normalizePhoneNumber(trimmed);
636
+ case "mailto":
637
+ return normalizeEmail(trimmed);
638
+ case "internal":
639
+ return toRelativePath(trimmed);
640
+ case "external":
641
+ return trimmed;
642
+ default:
643
+ return trimmed;
644
+ }
645
+ }, [href, linkType]);
646
+ const target = React3.useMemo(() => {
647
+ switch (linkType) {
648
+ case "external":
649
+ return "_blank";
650
+ case "internal":
651
+ return "_self";
652
+ case "mailto":
653
+ case "tel":
654
+ return void 0;
655
+ default:
656
+ return void 0;
657
+ }
658
+ }, [linkType]);
659
+ const rel = React3.useMemo(() => {
660
+ if (linkType === "external") {
661
+ return "noopener noreferrer";
662
+ }
663
+ return void 0;
664
+ }, [linkType]);
665
+ const isExternal = linkType === "external";
666
+ const isInternal = linkType === "internal";
667
+ const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
668
+ const handleClick = React3.useCallback(
669
+ (event) => {
670
+ if (onClick) {
671
+ try {
672
+ onClick(event);
673
+ } catch (error) {
674
+ console.error("Error in user onClick handler:", error);
675
+ }
752
676
  }
753
- );
754
- }
755
- if (pattern in patternOverlays) {
756
- const Overlay = patternOverlays[pattern];
757
- return /* @__PURE__ */ jsx(
758
- "div",
759
- {
760
- className: cn("pointer-events-none absolute inset-0 z-0", className),
761
- style: { opacity, ...style },
762
- "aria-hidden": "true",
763
- children: Overlay()
677
+ if (event.defaultPrevented) {
678
+ return;
764
679
  }
765
- );
766
- }
767
- const patternUrl = pattern in patternSvgs ? patternSvgs[pattern] : pattern;
768
- return /* @__PURE__ */ jsx(
769
- "div",
770
- {
771
- className: cn("pointer-events-none absolute inset-0 z-0", className),
772
- style: {
773
- backgroundImage: `url(${patternUrl})`,
774
- backgroundRepeat: "repeat",
775
- backgroundSize: "auto",
776
- opacity,
777
- ...style
778
- },
779
- "aria-hidden": "true"
780
- }
680
+ if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
681
+ !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
682
+ if (typeof window !== "undefined") {
683
+ const handler = window.__opensiteNavigationHandler;
684
+ if (typeof handler === "function") {
685
+ try {
686
+ const handled = handler(normalizedHref, event.nativeEvent || event);
687
+ if (handled !== false) {
688
+ event.preventDefault();
689
+ }
690
+ } catch (error) {
691
+ console.error("Error in navigation handler:", error);
692
+ }
693
+ }
694
+ }
695
+ }
696
+ },
697
+ [onClick, shouldUseRouter, normalizedHref]
781
698
  );
699
+ return {
700
+ linkType,
701
+ normalizedHref,
702
+ target,
703
+ rel,
704
+ isExternal,
705
+ isInternal,
706
+ shouldUseRouter,
707
+ handleClick
708
+ };
782
709
  }
783
- var backgroundStyles = {
784
- default: "bg-background text-foreground",
785
- white: "bg-white text-dark",
786
- gray: "bg-muted/30 text-foreground",
787
- dark: "bg-foreground text-background",
788
- transparent: "bg-transparent text-foreground",
789
- gradient: "bg-linear-to-br from-primary via-primary/90 to-foreground text-primary-foreground",
790
- primary: "bg-primary text-primary-foreground",
791
- secondary: "bg-secondary text-secondary-foreground",
792
- muted: "bg-muted text-muted-foreground"
793
- };
794
- var spacingStyles = {
795
- none: "py-0 md:py-0",
796
- sm: "py-12 md:py-16",
797
- md: "py-16 md:py-24",
798
- lg: "py-20 md:py-32",
799
- xl: "py-24 md:py-40"
800
- };
801
- var predefinedSpacings = ["none", "sm", "md", "lg", "xl"];
802
- var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
803
- var Section = React__default.forwardRef(
710
+ var Pressable = React3.forwardRef(
804
711
  ({
805
- id,
806
- title,
807
- subtitle,
808
712
  children,
809
713
  className,
810
- style,
811
- background = "default",
812
- spacing = "lg",
813
- pattern,
814
- patternOpacity,
815
- patternClassName,
816
- containerClassName,
817
- containerMaxWidth = "xl",
714
+ href,
715
+ onClick,
716
+ variant,
717
+ size,
718
+ asButton = false,
719
+ fallbackComponentType = "span",
720
+ componentType,
721
+ "aria-label": ariaLabel,
722
+ "aria-describedby": ariaDescribedby,
723
+ id,
818
724
  ...props
819
725
  }, ref) => {
820
- const effectivePatternOpacity = patternOpacity !== void 0 ? patternOpacity : pattern ? 1 : 0;
821
- return /* @__PURE__ */ jsxs(
822
- "section",
726
+ const navigation = useNavigation({ href, onClick });
727
+ const {
728
+ normalizedHref,
729
+ target,
730
+ rel,
731
+ linkType,
732
+ isInternal,
733
+ handleClick
734
+ } = navigation;
735
+ const shouldRenderLink = normalizedHref && linkType !== "none";
736
+ const shouldRenderButton = !shouldRenderLink && onClick;
737
+ const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
738
+ const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
739
+ const shouldApplyButtonStyles = asButton || variant || size;
740
+ const combinedClassName = cn(
741
+ shouldApplyButtonStyles && buttonVariants({ variant, size }),
742
+ className
743
+ );
744
+ const dataProps = Object.fromEntries(
745
+ Object.entries(props).filter(([key]) => key.startsWith("data-"))
746
+ );
747
+ const buttonDataAttributes = shouldApplyButtonStyles ? {
748
+ "data-slot": "button",
749
+ "data-variant": variant ?? "default",
750
+ "data-size": size ?? "default"
751
+ } : {};
752
+ const commonProps = {
753
+ className: combinedClassName,
754
+ onClick: handleClick,
755
+ "aria-label": ariaLabel,
756
+ "aria-describedby": ariaDescribedby,
757
+ id,
758
+ ...dataProps,
759
+ ...buttonDataAttributes
760
+ };
761
+ if (finalComponentType === "a" && shouldRenderLink) {
762
+ return /* @__PURE__ */ jsx(
763
+ "a",
764
+ {
765
+ ref,
766
+ href: normalizedHref,
767
+ target,
768
+ rel,
769
+ ...commonProps,
770
+ ...props,
771
+ children
772
+ }
773
+ );
774
+ }
775
+ if (finalComponentType === "button") {
776
+ return /* @__PURE__ */ jsx(
777
+ "button",
778
+ {
779
+ ref,
780
+ type: props.type || "button",
781
+ ...commonProps,
782
+ ...props,
783
+ children
784
+ }
785
+ );
786
+ }
787
+ if (finalComponentType === "div") {
788
+ return /* @__PURE__ */ jsx(
789
+ "div",
790
+ {
791
+ ref,
792
+ ...commonProps,
793
+ children
794
+ }
795
+ );
796
+ }
797
+ return /* @__PURE__ */ jsx(
798
+ "span",
823
799
  {
824
800
  ref,
825
- id,
801
+ ...commonProps,
802
+ children
803
+ }
804
+ );
805
+ }
806
+ );
807
+ Pressable.displayName = "Pressable";
808
+ var MOBILE_CLASSES = {
809
+ "fit-left": "items-start md:items-center",
810
+ "fit-center": "items-center",
811
+ "fit-right": "items-end md:items-center",
812
+ "full-left": "items-stretch md:items-center",
813
+ "full-center": "items-stretch md:items-center",
814
+ "full-right": "items-stretch md:items-center"
815
+ };
816
+ function BlockActions({
817
+ mobileConfig,
818
+ actionsClassName,
819
+ verticalSpacing = "mt-4 md:mt-8",
820
+ actions,
821
+ actionsSlot
822
+ }) {
823
+ const renderAction = React3.useCallback(
824
+ (action, idx) => {
825
+ const {
826
+ label,
827
+ icon,
828
+ iconAfter,
829
+ children,
830
+ href,
831
+ onClick,
832
+ className: actionClassName,
833
+ ...pressableProps
834
+ } = action;
835
+ return /* @__PURE__ */ jsx(
836
+ Pressable,
837
+ {
838
+ href,
839
+ onClick,
840
+ asButton: action.asButton || true,
841
+ className: actionClassName,
842
+ ...pressableProps,
843
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
844
+ icon,
845
+ label,
846
+ iconAfter
847
+ ] })
848
+ },
849
+ idx
850
+ );
851
+ },
852
+ []
853
+ );
854
+ const width = mobileConfig?.width ?? "fit";
855
+ const position = mobileConfig?.position ?? "left";
856
+ const mobileLayoutClass = MOBILE_CLASSES[`${width}-${position}`];
857
+ if (actionsSlot) {
858
+ return /* @__PURE__ */ jsx("div", { children: actionsSlot });
859
+ } else if (actions && actions?.length > 0) {
860
+ return /* @__PURE__ */ jsx(
861
+ "div",
862
+ {
826
863
  className: cn(
827
- "relative",
828
- pattern ? "overflow-hidden" : null,
829
- backgroundStyles[background],
830
- isPredefinedSpacing(spacing) ? spacingStyles[spacing] : spacing,
831
- className
864
+ "flex flex-col md:flex-row flex-wrap gap-4",
865
+ mobileLayoutClass,
866
+ actionsClassName,
867
+ verticalSpacing
832
868
  ),
833
- style,
834
- ...props,
835
- children: [
836
- /* @__PURE__ */ jsx(
837
- PatternBackground,
838
- {
839
- pattern,
840
- opacity: effectivePatternOpacity,
841
- className: patternClassName
842
- }
843
- ),
844
- /* @__PURE__ */ jsxs(
845
- Container,
846
- {
847
- maxWidth: containerMaxWidth,
848
- className: cn("relative z-10", containerClassName),
849
- children: [
850
- (title || subtitle) && /* @__PURE__ */ jsxs("div", { className: "mb-6 text-center md:mb-16", children: [
851
- subtitle && /* @__PURE__ */ jsx("p", { className: "mb-2 text-sm font-semibold uppercase tracking-wider", children: subtitle }),
852
- title && /* @__PURE__ */ jsx("h2", { className: "text-3xl font-bold tracking-tight md:text-4xl lg:text-5xl", children: title })
853
- ] }),
854
- children
855
- ]
856
- }
857
- )
858
- ]
869
+ children: actions.map((action, index) => renderAction(action, index))
859
870
  }
860
871
  );
872
+ } else {
873
+ return null;
861
874
  }
862
- );
863
- Section.displayName = "Section";
875
+ }
864
876
  function AboutStoryExpertise({
865
877
  eyebrow,
866
878
  eyebrowClassName,
@@ -895,25 +907,10 @@ function AboutStoryExpertise({
895
907
  const storyContent = useMemo(() => {
896
908
  if (storySlot) return storySlot;
897
909
  if (!storyParagraphs || storyParagraphs.length === 0) return null;
898
- return /* @__PURE__ */ jsx("div", { className: cn("space-y-4", getTextColor(background, "muted"), storyClassName), children: storyParagraphs.map(
910
+ return /* @__PURE__ */ jsx("div", { className: cn("space-y-4", storyClassName), children: storyParagraphs.map(
899
911
  (paragraph, idx) => typeof paragraph === "string" ? /* @__PURE__ */ jsx("p", { children: paragraph }, idx) : /* @__PURE__ */ jsx("div", { children: paragraph }, idx)
900
912
  ) });
901
913
  }, [storySlot, storyParagraphs, storyClassName]);
902
- const actionsContent = useMemo(() => {
903
- if (actionsSlot) return actionsSlot;
904
- if (!actions || actions.length === 0) return null;
905
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-4", actionsClassName), children: actions.map((action, idx) => /* @__PURE__ */ jsx(
906
- Pressable,
907
- {
908
- href: action.href,
909
- onClick: action.onClick,
910
- size: action.size || "lg",
911
- variant: action.variant || "default",
912
- children: action.label
913
- },
914
- idx
915
- )) });
916
- }, [actionsSlot, actions, actionsClassName]);
917
914
  const highlightContent = useMemo(() => {
918
915
  if (highlightSlot) return highlightSlot;
919
916
  if (!highlight) return null;
@@ -921,18 +918,26 @@ function AboutStoryExpertise({
921
918
  "div",
922
919
  {
923
920
  className: cn(
924
- "rounded-2xl border border-border/60 bg-background/90 p-6 shadow-xl",
921
+ "rounded-2xl border border-border/60 p-6 shadow-xl",
925
922
  highlightClassName
926
923
  ),
927
924
  children: [
928
925
  /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center gap-4", children: [
929
926
  /* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-primary text-primary-foreground", children: highlight.icon }),
930
927
  /* @__PURE__ */ jsxs("div", { children: [
931
- highlight.label && (typeof highlight.label === "string" ? /* @__PURE__ */ jsx("p", { className: cn("text-xs font-semibold uppercase tracking-[0.2em]", getTextColor(background, "muted")), children: highlight.label }) : highlight.label),
928
+ highlight.label && (typeof highlight.label === "string" ? /* @__PURE__ */ jsx(
929
+ "p",
930
+ {
931
+ className: cn(
932
+ "text-xs font-semibold uppercase tracking-[0.2em]"
933
+ ),
934
+ children: highlight.label
935
+ }
936
+ ) : highlight.label),
932
937
  highlight.title && (typeof highlight.title === "string" ? /* @__PURE__ */ jsx("h3", { className: "text-lg font-bold", children: highlight.title }) : highlight.title)
933
938
  ] })
934
939
  ] }),
935
- highlight.description && (typeof highlight.description === "string" ? /* @__PURE__ */ jsx("p", { className: cn("text-sm", getTextColor(background, "muted")), children: highlight.description }) : highlight.description)
940
+ highlight.description && (typeof highlight.description === "string" ? /* @__PURE__ */ jsx("p", { className: cn("text-sm"), children: highlight.description }) : highlight.description)
936
941
  ]
937
942
  }
938
943
  );
@@ -956,14 +961,18 @@ function AboutStoryExpertise({
956
961
  transition: { duration: 0.3, delay: idx * 0.05 },
957
962
  className: "flex items-start gap-4 rounded-2xl bg-background p-6 shadow-sm transition-all duration-300 hover:-translate-y-1 hover:shadow-lg",
958
963
  children: [
959
- /* @__PURE__ */ jsx("div", { className: cn(
960
- "flex h-12 w-12 items-center justify-center rounded-xl",
961
- getNestedCardBg(background, "muted"),
962
- getAccentColor(background)
963
- ), children: area.icon }),
964
+ /* @__PURE__ */ jsx(
965
+ "div",
966
+ {
967
+ className: cn(
968
+ "flex h-12 w-12 items-center justify-center rounded-xl"
969
+ ),
970
+ children: area.icon
971
+ }
972
+ ),
964
973
  /* @__PURE__ */ jsxs("div", { children: [
965
974
  area.title && (typeof area.title === "string" ? /* @__PURE__ */ jsx("h4", { className: "text-lg font-bold", children: area.title }) : area.title),
966
- area.description && (typeof area.description === "string" ? /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-sm", getTextColor(background, "muted")), children: area.description }) : /* @__PURE__ */ jsx("div", { className: "mt-1", children: area.description }))
975
+ area.description && (typeof area.description === "string" ? /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-sm"), children: area.description }) : /* @__PURE__ */ jsx("div", { className: "mt-1", children: area.description }))
967
976
  ] })
968
977
  ]
969
978
  },
@@ -997,7 +1006,6 @@ function AboutStoryExpertise({
997
1006
  {
998
1007
  className: cn(
999
1008
  "text-sm font-semibold uppercase tracking-[0.2em]",
1000
- getAccentColor(background),
1001
1009
  eyebrowClassName
1002
1010
  ),
1003
1011
  children: eyebrow
@@ -1015,7 +1023,14 @@ function AboutStoryExpertise({
1015
1023
  ) : /* @__PURE__ */ jsx("div", { className: cn("mt-2", headingClassName), children: heading }))
1016
1024
  ] }),
1017
1025
  storyContent,
1018
- actionsContent
1026
+ /* @__PURE__ */ jsx(
1027
+ BlockActions,
1028
+ {
1029
+ actions,
1030
+ actionsClassName,
1031
+ actionsSlot
1032
+ }
1033
+ )
1019
1034
  ]
1020
1035
  }
1021
1036
  ),
@@ -1057,12 +1072,11 @@ function AboutStoryExpertise({
1057
1072
  {
1058
1073
  className: cn(
1059
1074
  "mt-20 rounded-3xl p-8 md:p-12",
1060
- getNestedCardBg(background, "subtle"),
1061
1075
  expertiseSectionClassName
1062
1076
  ),
1063
1077
  children: [
1064
1078
  /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1065
- /* @__PURE__ */ jsx("p", { className: cn("text-sm font-semibold uppercase tracking-[0.2em]", getAccentColor(background)), children: "Our Expertise" }),
1079
+ /* @__PURE__ */ jsx("p", { className: cn("text-sm font-semibold uppercase tracking-[0.2em]"), children: "Our Expertise" }),
1066
1080
  expertiseHeading && (typeof expertiseHeading === "string" ? /* @__PURE__ */ jsx(
1067
1081
  "h3",
1068
1082
  {
@@ -1078,7 +1092,6 @@ function AboutStoryExpertise({
1078
1092
  {
1079
1093
  className: cn(
1080
1094
  "mx-auto mt-3 max-w-2xl",
1081
- getTextColor(background, "muted"),
1082
1095
  expertiseDescriptionClassName
1083
1096
  ),
1084
1097
  children: expertiseDescription