@opensite/ui 2.8.7 → 2.8.9

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 (163) hide show
  1. package/dist/about-culture-tabs.cjs +174 -174
  2. package/dist/about-culture-tabs.js +174 -174
  3. package/dist/about-developer-profile.cjs +200 -200
  4. package/dist/about-developer-profile.js +198 -198
  5. package/dist/about-developer-story.cjs +142 -142
  6. package/dist/about-developer-story.js +142 -142
  7. package/dist/about-mission-dual-image.cjs +142 -142
  8. package/dist/about-mission-dual-image.js +142 -142
  9. package/dist/about-mission-features.cjs +142 -142
  10. package/dist/about-mission-features.js +142 -142
  11. package/dist/about-network-spotlight.cjs +142 -142
  12. package/dist/about-network-spotlight.js +142 -142
  13. package/dist/about-story-expertise.cjs +142 -142
  14. package/dist/about-story-expertise.js +142 -142
  15. package/dist/about-streamline-team.cjs +142 -142
  16. package/dist/about-streamline-team.js +142 -142
  17. package/dist/carousel-icon-sidebar.cjs +5 -4
  18. package/dist/carousel-icon-sidebar.js +5 -4
  19. package/dist/community-initiatives.cjs +142 -142
  20. package/dist/community-initiatives.js +142 -142
  21. package/dist/components.cjs +723 -1364
  22. package/dist/components.d.cts +0 -2
  23. package/dist/components.d.ts +0 -2
  24. package/dist/components.js +633 -1273
  25. package/dist/contact-map.cjs +14 -1069
  26. package/dist/contact-map.d.cts +13 -3
  27. package/dist/contact-map.d.ts +13 -3
  28. package/dist/contact-map.js +14 -1069
  29. package/dist/cta-feature-checklist.cjs +142 -142
  30. package/dist/cta-feature-checklist.js +142 -142
  31. package/dist/faq-numbered-grid.cjs +142 -142
  32. package/dist/faq-numbered-grid.js +142 -142
  33. package/dist/feature-animated-carousel.cjs +142 -142
  34. package/dist/feature-animated-carousel.js +142 -142
  35. package/dist/feature-bento-utilities.cjs +142 -142
  36. package/dist/feature-bento-utilities.js +142 -142
  37. package/dist/feature-capabilities-grid.cjs +142 -142
  38. package/dist/feature-capabilities-grid.js +142 -142
  39. package/dist/feature-category-image-cards.cjs +142 -142
  40. package/dist/feature-category-image-cards.js +142 -142
  41. package/dist/feature-icon-grid-bordered.cjs +142 -142
  42. package/dist/feature-icon-grid-bordered.js +142 -142
  43. package/dist/feature-icon-grid-muted.cjs +142 -142
  44. package/dist/feature-icon-grid-muted.js +142 -142
  45. package/dist/feature-numbered-cards.cjs +142 -142
  46. package/dist/feature-numbered-cards.js +142 -142
  47. package/dist/feature-three-column-values.cjs +142 -142
  48. package/dist/feature-three-column-values.js +142 -142
  49. package/dist/hero-ad-campaign-expert.cjs +142 -142
  50. package/dist/hero-ad-campaign-expert.js +142 -142
  51. package/dist/hero-adaptable-product-grid.cjs +142 -142
  52. package/dist/hero-adaptable-product-grid.js +142 -142
  53. package/dist/hero-agency-animated-images.cjs +142 -142
  54. package/dist/hero-agency-animated-images.js +142 -142
  55. package/dist/hero-announcement-badge.cjs +142 -142
  56. package/dist/hero-announcement-badge.js +142 -142
  57. package/dist/hero-badge-image-split.cjs +142 -142
  58. package/dist/hero-badge-image-split.js +142 -142
  59. package/dist/hero-business-carousel-dots.cjs +142 -142
  60. package/dist/hero-business-carousel-dots.js +142 -142
  61. package/dist/hero-business-operations-mosaic.cjs +142 -142
  62. package/dist/hero-business-operations-mosaic.js +142 -142
  63. package/dist/hero-conversation-intelligence.cjs +142 -142
  64. package/dist/hero-conversation-intelligence.js +142 -142
  65. package/dist/hero-creative-studio-stacked.cjs +142 -142
  66. package/dist/hero-creative-studio-stacked.js +142 -142
  67. package/dist/hero-crm-streamlined.cjs +142 -142
  68. package/dist/hero-crm-streamlined.js +142 -142
  69. package/dist/hero-customer-support-layered.cjs +142 -142
  70. package/dist/hero-customer-support-layered.js +142 -142
  71. package/dist/hero-design-showcase-logos.cjs +142 -142
  72. package/dist/hero-design-showcase-logos.js +142 -142
  73. package/dist/hero-design-system-3d.cjs +142 -142
  74. package/dist/hero-design-system-3d.js +142 -142
  75. package/dist/hero-developer-tools-code.cjs +142 -142
  76. package/dist/hero-developer-tools-code.js +142 -142
  77. package/dist/hero-digital-agency-fullscreen.cjs +142 -142
  78. package/dist/hero-digital-agency-fullscreen.js +142 -142
  79. package/dist/hero-ecommerce-product-showcase.cjs +174 -174
  80. package/dist/hero-ecommerce-product-showcase.js +174 -174
  81. package/dist/hero-event-registration.cjs +142 -142
  82. package/dist/hero-event-registration.js +142 -142
  83. package/dist/hero-fullscreen-background-image.cjs +142 -142
  84. package/dist/hero-fullscreen-background-image.js +142 -142
  85. package/dist/hero-gradient-avatars-rating.cjs +142 -142
  86. package/dist/hero-gradient-avatars-rating.js +142 -142
  87. package/dist/hero-gradient-client-focused.cjs +142 -142
  88. package/dist/hero-gradient-client-focused.js +142 -142
  89. package/dist/hero-hiring-animated-text.cjs +142 -142
  90. package/dist/hero-hiring-animated-text.js +142 -142
  91. package/dist/hero-image-left-content.cjs +142 -142
  92. package/dist/hero-image-left-content.js +142 -142
  93. package/dist/hero-innovation-image-grid.cjs +142 -142
  94. package/dist/hero-innovation-image-grid.js +142 -142
  95. package/dist/hero-mental-health-team.cjs +142 -142
  96. package/dist/hero-mental-health-team.js +142 -142
  97. package/dist/hero-minimal-centered-dark.cjs +174 -174
  98. package/dist/hero-minimal-centered-dark.js +174 -174
  99. package/dist/hero-presentation-platform-video.cjs +142 -142
  100. package/dist/hero-presentation-platform-video.js +142 -142
  101. package/dist/hero-product-showcase-floating.cjs +174 -174
  102. package/dist/hero-product-showcase-floating.js +174 -174
  103. package/dist/hero-shared-inbox-layered.cjs +142 -142
  104. package/dist/hero-shared-inbox-layered.js +142 -142
  105. package/dist/hero-software-growth-video-dialog.cjs +142 -142
  106. package/dist/hero-software-growth-video-dialog.js +142 -142
  107. package/dist/hero-spiral-pattern-cards.cjs +174 -174
  108. package/dist/hero-spiral-pattern-cards.js +174 -174
  109. package/dist/hero-split-geometric-shapes.cjs +142 -142
  110. package/dist/hero-split-geometric-shapes.js +142 -142
  111. package/dist/hero-startup-launch-cta.cjs +174 -174
  112. package/dist/hero-startup-launch-cta.js +174 -174
  113. package/dist/hero-stats-social-proof.cjs +174 -174
  114. package/dist/hero-stats-social-proof.js +174 -174
  115. package/dist/hero-task-timer-animated.cjs +142 -142
  116. package/dist/hero-task-timer-animated.js +142 -142
  117. package/dist/hero-testimonial-image-grid.cjs +142 -142
  118. package/dist/hero-testimonial-image-grid.js +142 -142
  119. package/dist/hero-therapy-testimonial-grid.cjs +142 -142
  120. package/dist/hero-therapy-testimonial-grid.js +142 -142
  121. package/dist/hero-ui-library-showcase.cjs +142 -142
  122. package/dist/hero-ui-library-showcase.js +142 -142
  123. package/dist/hero-video-background-dark.cjs +174 -174
  124. package/dist/hero-video-background-dark.js +174 -174
  125. package/dist/hero-video-dialog-gradient.cjs +142 -142
  126. package/dist/hero-video-dialog-gradient.js +142 -142
  127. package/dist/hero-video-overlay-stars.cjs +142 -142
  128. package/dist/hero-video-overlay-stars.js +142 -142
  129. package/dist/hero-welcome-asymmetric-images.cjs +142 -142
  130. package/dist/hero-welcome-asymmetric-images.js +142 -142
  131. package/dist/index.cjs +725 -1366
  132. package/dist/index.d.cts +0 -2
  133. package/dist/index.d.ts +0 -2
  134. package/dist/index.js +634 -1274
  135. package/dist/registry.cjs +2371 -2915
  136. package/dist/registry.js +1120 -1664
  137. package/dist/testimonials-large-quote.cjs +74 -43
  138. package/dist/testimonials-large-quote.d.cts +5 -1
  139. package/dist/testimonials-large-quote.d.ts +5 -1
  140. package/dist/testimonials-large-quote.js +74 -43
  141. package/dist/testimonials-logo-cards.cjs +8 -2
  142. package/dist/testimonials-logo-cards.js +8 -2
  143. package/dist/testimonials-masonry-grid.cjs +486 -69
  144. package/dist/testimonials-masonry-grid.d.cts +5 -1
  145. package/dist/testimonials-masonry-grid.d.ts +5 -1
  146. package/dist/testimonials-masonry-grid.js +483 -63
  147. package/dist/testimonials-mini-dividers.cjs +2 -3
  148. package/dist/testimonials-mini-dividers.js +2 -3
  149. package/dist/testimonials-minimal-numbered.cjs +5 -4
  150. package/dist/testimonials-minimal-numbered.js +5 -4
  151. package/dist/testimonials-parallax-number.cjs +5 -4
  152. package/dist/testimonials-parallax-number.js +5 -4
  153. package/dist/testimonials-scrolling-columns.cjs +7 -12
  154. package/dist/testimonials-scrolling-columns.js +7 -12
  155. package/dist/testimonials-stats-header.cjs +528 -87
  156. package/dist/testimonials-stats-header.d.cts +39 -3
  157. package/dist/testimonials-stats-header.d.ts +39 -3
  158. package/dist/testimonials-stats-header.js +523 -82
  159. package/package.json +4 -7
  160. package/dist/geo-map.cjs +0 -1103
  161. package/dist/geo-map.d.cts +0 -92
  162. package/dist/geo-map.d.ts +0 -92
  163. package/dist/geo-map.js +0 -1081
@@ -72,6 +72,10 @@ interface TestimonialsMasonryGridProps {
72
72
  * Pattern overlay opacity (0-1)
73
73
  */
74
74
  patternOpacity?: number;
75
+ /**
76
+ * Additional CSS classes for the container
77
+ */
78
+ containerClassName?: string;
75
79
  }
76
80
  /**
77
81
  * TestimonialsMasonryGrid - A masonry-style grid layout for testimonials with varying
@@ -98,6 +102,6 @@ interface TestimonialsMasonryGridProps {
98
102
  * />
99
103
  * ```
100
104
  */
101
- declare function TestimonialsMasonryGrid({ testimonials, testimonialsSlot, heading, description, className, headerClassName, headingClassName, descriptionClassName, gridClassName, cardClassName, quoteClassName, authorClassName, background, spacing, pattern, patternOpacity, }: TestimonialsMasonryGridProps): React.JSX.Element;
105
+ declare function TestimonialsMasonryGrid({ testimonials, testimonialsSlot, heading, description, className, headerClassName, headingClassName, descriptionClassName, gridClassName, cardClassName, quoteClassName, authorClassName, background, containerClassName, spacing, pattern, patternOpacity, }: TestimonialsMasonryGridProps): React.JSX.Element;
102
106
 
103
107
  export { TestimonialsMasonryGrid, type TestimonialsMasonryGridProps };
@@ -1,9 +1,11 @@
1
1
  "use client";
2
- import React, { useCallback, useMemo } from 'react';
2
+ import * as React3 from 'react';
3
+ import React3__default, { useCallback, useMemo } from 'react';
3
4
  import { clsx } from 'clsx';
4
5
  import { twMerge } from 'tailwind-merge';
5
6
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
6
7
  import { jsx, jsxs } from 'react/jsx-runtime';
8
+ import { cva } from 'class-variance-authority';
7
9
 
8
10
  // components/blocks/testimonials/testimonials-masonry-grid.tsx
9
11
  function cn(...inputs) {
@@ -54,29 +56,6 @@ function AvatarFallback({
54
56
  }
55
57
  );
56
58
  }
57
- function Card({ className, ...props }) {
58
- return /* @__PURE__ */ jsx(
59
- "div",
60
- {
61
- "data-slot": "card",
62
- className: cn(
63
- "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
64
- className
65
- ),
66
- ...props
67
- }
68
- );
69
- }
70
- function CardContent({ className, ...props }) {
71
- return /* @__PURE__ */ jsx(
72
- "div",
73
- {
74
- "data-slot": "card-content",
75
- className: cn("px-6", className),
76
- ...props
77
- }
78
- );
79
- }
80
59
  var maxWidthStyles = {
81
60
  sm: "max-w-screen-sm",
82
61
  md: "max-w-screen-md",
@@ -86,7 +65,7 @@ var maxWidthStyles = {
86
65
  "4xl": "max-w-[1536px]",
87
66
  full: "max-w-full"
88
67
  };
89
- var Container = React.forwardRef(
68
+ var Container = React3__default.forwardRef(
90
69
  ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
91
70
  const Component = as;
92
71
  return /* @__PURE__ */ jsx(
@@ -392,7 +371,7 @@ var spacingStyles = {
392
371
  };
393
372
  var predefinedSpacings = ["none", "sm", "md", "lg", "xl", "hero"];
394
373
  var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
395
- var Section = React.forwardRef(
374
+ var Section = React3__default.forwardRef(
396
375
  ({
397
376
  id,
398
377
  title,
@@ -453,6 +432,424 @@ var Section = React.forwardRef(
453
432
  }
454
433
  );
455
434
  Section.displayName = "Section";
435
+ var baseStyles = [
436
+ // Layout
437
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
438
+ // Typography - using CSS variables with sensible defaults
439
+ "font-[var(--button-font-family,inherit)]",
440
+ "font-[var(--button-font-weight,500)]",
441
+ "tracking-[var(--button-letter-spacing,0)]",
442
+ "leading-[var(--button-line-height,1.25)]",
443
+ "[text-transform:var(--button-text-transform,none)]",
444
+ "text-sm",
445
+ // Border radius
446
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
447
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
448
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
449
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
450
+ "[box-shadow:var(--button-shadow,none)]",
451
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
452
+ // Disabled state
453
+ "disabled:pointer-events-none disabled:opacity-50",
454
+ // SVG handling
455
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
456
+ // Focus styles
457
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
458
+ // Invalid state
459
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
460
+ ].join(" ");
461
+ var buttonVariants = cva(baseStyles, {
462
+ variants: {
463
+ variant: {
464
+ // Default (Primary) variant - full customization
465
+ default: [
466
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
467
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
468
+ "border-[length:var(--button-default-border-width,0px)]",
469
+ "border-[color:var(--button-default-border,transparent)]",
470
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
471
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
472
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
473
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
474
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
475
+ ].join(" "),
476
+ // Destructive variant - full customization
477
+ destructive: [
478
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
479
+ "text-[var(--button-destructive-fg,white)]",
480
+ "border-[length:var(--button-destructive-border-width,0px)]",
481
+ "border-[color:var(--button-destructive-border,transparent)]",
482
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
483
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
484
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
485
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
486
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
487
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
488
+ "dark:bg-destructive/60"
489
+ ].join(" "),
490
+ // Outline variant - full customization with proper border handling
491
+ outline: [
492
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
493
+ "text-[var(--button-outline-fg,inherit)]",
494
+ "border-[length:var(--button-outline-border-width,1px)]",
495
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
496
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
497
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
498
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
499
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
500
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
501
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
502
+ ].join(" "),
503
+ // Secondary variant - full customization
504
+ secondary: [
505
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
506
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
507
+ "border-[length:var(--button-secondary-border-width,0px)]",
508
+ "border-[color:var(--button-secondary-border,transparent)]",
509
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
510
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
511
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
512
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
513
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
514
+ ].join(" "),
515
+ // Ghost variant - full customization
516
+ ghost: [
517
+ "bg-[var(--button-ghost-bg,transparent)]",
518
+ "text-[var(--button-ghost-fg,inherit)]",
519
+ "border-[length:var(--button-ghost-border-width,0px)]",
520
+ "border-[color:var(--button-ghost-border,transparent)]",
521
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
522
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
523
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
524
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
525
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
526
+ "dark:hover:bg-accent/50"
527
+ ].join(" "),
528
+ // Link variant - full customization
529
+ link: [
530
+ "bg-[var(--button-link-bg,transparent)]",
531
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
532
+ "border-[length:var(--button-link-border-width,0px)]",
533
+ "border-[color:var(--button-link-border,transparent)]",
534
+ "[box-shadow:var(--button-link-shadow,none)]",
535
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
536
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
537
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
538
+ "underline-offset-4 hover:underline"
539
+ ].join(" ")
540
+ },
541
+ size: {
542
+ default: [
543
+ "h-[var(--button-height-md,2.25rem)]",
544
+ "px-[var(--button-padding-x-md,1rem)]",
545
+ "py-[var(--button-padding-y-md,0.5rem)]",
546
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
547
+ ].join(" "),
548
+ sm: [
549
+ "h-[var(--button-height-sm,2rem)]",
550
+ "px-[var(--button-padding-x-sm,0.75rem)]",
551
+ "py-[var(--button-padding-y-sm,0.25rem)]",
552
+ "gap-1.5",
553
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
554
+ ].join(" "),
555
+ md: [
556
+ "h-[var(--button-height-md,2.25rem)]",
557
+ "px-[var(--button-padding-x-md,1rem)]",
558
+ "py-[var(--button-padding-y-md,0.5rem)]",
559
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
560
+ ].join(" "),
561
+ lg: [
562
+ "h-[var(--button-height-lg,2.5rem)]",
563
+ "px-[var(--button-padding-x-lg,1.5rem)]",
564
+ "py-[var(--button-padding-y-lg,0.5rem)]",
565
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
566
+ ].join(" "),
567
+ icon: "size-[var(--button-height-md,2.25rem)]",
568
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
569
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
570
+ }
571
+ },
572
+ defaultVariants: {
573
+ variant: "default",
574
+ size: "default"
575
+ }
576
+ });
577
+ function normalizePhoneNumber(input) {
578
+ const trimmed = input.trim();
579
+ if (trimmed.toLowerCase().startsWith("tel:")) {
580
+ return trimmed;
581
+ }
582
+ const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
583
+ if (match) {
584
+ const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
585
+ const extension = match[3];
586
+ const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
587
+ const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
588
+ return `tel:${withExtension}`;
589
+ }
590
+ const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
591
+ return `tel:${cleaned}`;
592
+ }
593
+ function normalizeEmail(input) {
594
+ const trimmed = input.trim();
595
+ if (trimmed.toLowerCase().startsWith("mailto:")) {
596
+ return trimmed;
597
+ }
598
+ return `mailto:${trimmed}`;
599
+ }
600
+ function isEmail(input) {
601
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
602
+ return emailRegex.test(input.trim());
603
+ }
604
+ function isPhoneNumber(input) {
605
+ const trimmed = input.trim();
606
+ if (trimmed.toLowerCase().startsWith("tel:")) {
607
+ return true;
608
+ }
609
+ const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
610
+ return phoneRegex.test(trimmed);
611
+ }
612
+ function isInternalUrl(href) {
613
+ if (typeof window === "undefined") {
614
+ return href.startsWith("/") && !href.startsWith("//");
615
+ }
616
+ const trimmed = href.trim();
617
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
618
+ return true;
619
+ }
620
+ try {
621
+ const url = new URL(trimmed, window.location.href);
622
+ const currentOrigin = window.location.origin;
623
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
624
+ return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
625
+ } catch {
626
+ return false;
627
+ }
628
+ }
629
+ function toRelativePath(href) {
630
+ if (typeof window === "undefined") {
631
+ return href;
632
+ }
633
+ const trimmed = href.trim();
634
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
635
+ return trimmed;
636
+ }
637
+ try {
638
+ const url = new URL(trimmed, window.location.href);
639
+ const currentOrigin = window.location.origin;
640
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
641
+ if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
642
+ return url.pathname + url.search + url.hash;
643
+ }
644
+ } catch {
645
+ }
646
+ return trimmed;
647
+ }
648
+ function useNavigation({
649
+ href,
650
+ onClick
651
+ } = {}) {
652
+ const linkType = React3.useMemo(() => {
653
+ if (!href || href.trim() === "") {
654
+ return onClick ? "none" : "none";
655
+ }
656
+ const trimmed = href.trim();
657
+ if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
658
+ return "mailto";
659
+ }
660
+ if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
661
+ return "tel";
662
+ }
663
+ if (isInternalUrl(trimmed)) {
664
+ return "internal";
665
+ }
666
+ try {
667
+ new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
668
+ return "external";
669
+ } catch {
670
+ return "internal";
671
+ }
672
+ }, [href, onClick]);
673
+ const normalizedHref = React3.useMemo(() => {
674
+ if (!href || href.trim() === "") {
675
+ return void 0;
676
+ }
677
+ const trimmed = href.trim();
678
+ switch (linkType) {
679
+ case "tel":
680
+ return normalizePhoneNumber(trimmed);
681
+ case "mailto":
682
+ return normalizeEmail(trimmed);
683
+ case "internal":
684
+ return toRelativePath(trimmed);
685
+ case "external":
686
+ return trimmed;
687
+ default:
688
+ return trimmed;
689
+ }
690
+ }, [href, linkType]);
691
+ const target = React3.useMemo(() => {
692
+ switch (linkType) {
693
+ case "external":
694
+ return "_blank";
695
+ case "internal":
696
+ return "_self";
697
+ case "mailto":
698
+ case "tel":
699
+ return void 0;
700
+ default:
701
+ return void 0;
702
+ }
703
+ }, [linkType]);
704
+ const rel = React3.useMemo(() => {
705
+ if (linkType === "external") {
706
+ return "noopener noreferrer";
707
+ }
708
+ return void 0;
709
+ }, [linkType]);
710
+ const isExternal = linkType === "external";
711
+ const isInternal = linkType === "internal";
712
+ const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
713
+ const handleClick = React3.useCallback(
714
+ (event) => {
715
+ if (onClick) {
716
+ try {
717
+ onClick(event);
718
+ } catch (error) {
719
+ console.error("Error in user onClick handler:", error);
720
+ }
721
+ }
722
+ if (event.defaultPrevented) {
723
+ return;
724
+ }
725
+ if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
726
+ !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
727
+ if (typeof window !== "undefined") {
728
+ const handler = window.__opensiteNavigationHandler;
729
+ if (typeof handler === "function") {
730
+ try {
731
+ const handled = handler(normalizedHref, event.nativeEvent || event);
732
+ if (handled !== false) {
733
+ event.preventDefault();
734
+ }
735
+ } catch (error) {
736
+ console.error("Error in navigation handler:", error);
737
+ }
738
+ }
739
+ }
740
+ }
741
+ },
742
+ [onClick, shouldUseRouter, normalizedHref]
743
+ );
744
+ return {
745
+ linkType,
746
+ normalizedHref,
747
+ target,
748
+ rel,
749
+ isExternal,
750
+ isInternal,
751
+ shouldUseRouter,
752
+ handleClick
753
+ };
754
+ }
755
+ var Pressable = React3.forwardRef(
756
+ ({
757
+ children,
758
+ className,
759
+ href,
760
+ onClick,
761
+ variant,
762
+ size,
763
+ asButton = false,
764
+ fallbackComponentType = "span",
765
+ componentType,
766
+ "aria-label": ariaLabel,
767
+ "aria-describedby": ariaDescribedby,
768
+ id,
769
+ ...props
770
+ }, ref) => {
771
+ const navigation = useNavigation({ href, onClick });
772
+ const {
773
+ normalizedHref,
774
+ target,
775
+ rel,
776
+ linkType,
777
+ isInternal,
778
+ handleClick
779
+ } = navigation;
780
+ const shouldRenderLink = normalizedHref && linkType !== "none";
781
+ const shouldRenderButton = !shouldRenderLink && onClick;
782
+ const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
783
+ const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
784
+ const shouldApplyButtonStyles = asButton || variant || size;
785
+ const combinedClassName = cn(
786
+ shouldApplyButtonStyles && buttonVariants({ variant, size }),
787
+ className
788
+ );
789
+ const dataProps = Object.fromEntries(
790
+ Object.entries(props).filter(([key]) => key.startsWith("data-"))
791
+ );
792
+ const buttonDataAttributes = shouldApplyButtonStyles ? {
793
+ "data-slot": "button",
794
+ "data-variant": variant ?? "default",
795
+ "data-size": size ?? "default"
796
+ } : {};
797
+ const commonProps = {
798
+ className: combinedClassName,
799
+ onClick: handleClick,
800
+ "aria-label": ariaLabel,
801
+ "aria-describedby": ariaDescribedby,
802
+ id,
803
+ ...dataProps,
804
+ ...buttonDataAttributes
805
+ };
806
+ if (finalComponentType === "a" && shouldRenderLink) {
807
+ return /* @__PURE__ */ jsx(
808
+ "a",
809
+ {
810
+ ref,
811
+ href: normalizedHref,
812
+ target,
813
+ rel,
814
+ ...commonProps,
815
+ ...props,
816
+ children
817
+ }
818
+ );
819
+ }
820
+ if (finalComponentType === "button") {
821
+ return /* @__PURE__ */ jsx(
822
+ "button",
823
+ {
824
+ ref,
825
+ type: props.type || "button",
826
+ ...commonProps,
827
+ ...props,
828
+ children
829
+ }
830
+ );
831
+ }
832
+ if (finalComponentType === "div") {
833
+ return /* @__PURE__ */ jsx(
834
+ "div",
835
+ {
836
+ ref,
837
+ ...commonProps,
838
+ children
839
+ }
840
+ );
841
+ }
842
+ return /* @__PURE__ */ jsx(
843
+ "span",
844
+ {
845
+ ref,
846
+ ...commonProps,
847
+ children
848
+ }
849
+ );
850
+ }
851
+ );
852
+ Pressable.displayName = "Pressable";
456
853
  function TestimonialsMasonryGrid({
457
854
  testimonials,
458
855
  testimonialsSlot,
@@ -467,7 +864,8 @@ function TestimonialsMasonryGrid({
467
864
  quoteClassName,
468
865
  authorClassName,
469
866
  background,
470
- spacing,
867
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
868
+ spacing = "xl",
471
869
  pattern,
472
870
  patternOpacity
473
871
  }) {
@@ -480,9 +878,12 @@ function TestimonialsMasonryGrid({
480
878
  if (typeof testimonial.author === "string") return testimonial.author;
481
879
  return "";
482
880
  }, []);
483
- const getAvatarSrc = useCallback((testimonial) => {
484
- return testimonial.avatarSrc || testimonial.avatar?.src;
485
- }, []);
881
+ const getAvatarSrc = useCallback(
882
+ (testimonial) => {
883
+ return testimonial.avatarSrc || testimonial.avatar?.src;
884
+ },
885
+ []
886
+ );
486
887
  const getInitials = useCallback((name) => {
487
888
  return name.split(" ").map((n) => n[0]).join("");
488
889
  }, []);
@@ -499,42 +900,60 @@ function TestimonialsMasonryGrid({
499
900
  children: columns.map((column, columnIndex) => /* @__PURE__ */ jsx("div", { className: "space-y-4", children: column.map((testimonial, index) => {
500
901
  const authorName = getAuthorName(testimonial);
501
902
  const avatarSrc = getAvatarSrc(testimonial);
502
- return /* @__PURE__ */ jsx(Card, { className: cardClassName, children: /* @__PURE__ */ jsxs(CardContent, { className: "p-6", children: [
503
- testimonial.quote && (typeof testimonial.quote === "string" ? /* @__PURE__ */ jsxs(
504
- "p",
505
- {
506
- className: cn(
507
- "mb-4 text-sm leading-relaxed",
508
- quoteClassName
509
- ),
510
- children: [
903
+ return /* @__PURE__ */ jsx(
904
+ "div",
905
+ {
906
+ className: cn(
907
+ cardClassName,
908
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border shadow-sm"
909
+ ),
910
+ children: /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col justify-between p-6 gap-8", children: [
911
+ testimonial.quote && (typeof testimonial.quote === "string" ? /* @__PURE__ */ jsxs("blockquote", { className: "text-sm leading-relaxed line-clamp-3", children: [
511
912
  "\u201C",
512
913
  testimonial.quote,
513
914
  "\u201D"
514
- ]
515
- }
516
- ) : /* @__PURE__ */ jsx("div", { className: cn("mb-4", quoteClassName), children: testimonial.quote })),
517
- /* @__PURE__ */ jsxs(
518
- "div",
519
- {
520
- className: cn("flex items-center gap-3", authorClassName),
521
- children: [
522
- /* @__PURE__ */ jsxs(Avatar, { className: "size-9", children: [
915
+ ] }) : /* @__PURE__ */ jsx("div", { className: "line-clamp-3", children: testimonial.quote })),
916
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-3", children: [
917
+ /* @__PURE__ */ jsxs(Avatar, { className: "relative flex shrink-0 overflow-hidden rounded-full size-12 ring-4 ring-primary shadow-lg", children: [
523
918
  /* @__PURE__ */ jsx(AvatarImage, { src: avatarSrc, alt: authorName }),
524
919
  /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: getInitials(authorName) })
525
920
  ] }),
526
- /* @__PURE__ */ jsxs("div", { children: [
921
+ /* @__PURE__ */ jsxs("div", { className: "space-y-0 leading-tight", children: [
527
922
  testimonial.author && (typeof testimonial.author === "string" ? /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: testimonial.author }) : testimonial.author),
528
- testimonial.role && (typeof testimonial.role === "string" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: testimonial.role }) : testimonial.role)
923
+ testimonial.role && (typeof testimonial.role === "string" ? /* @__PURE__ */ jsx("p", { className: "text-xs", children: testimonial.role }) : testimonial.role),
924
+ testimonial.linkConfig?.href && /* @__PURE__ */ jsx(
925
+ Pressable,
926
+ {
927
+ href: testimonial.linkConfig.href,
928
+ className: cn(
929
+ "text-sm transition-all duration-300",
930
+ "underline underline-offset-4",
931
+ testimonial.linkConfig.className
932
+ ),
933
+ children: testimonial.linkConfig.label
934
+ }
935
+ )
529
936
  ] })
530
- ]
531
- }
532
- )
533
- ] }) }, index);
937
+ ] })
938
+ ] })
939
+ },
940
+ index
941
+ );
534
942
  }) }, columnIndex))
535
943
  }
536
944
  );
537
- }, [testimonialsSlot, gridClassName, columns, cardClassName, quoteClassName, authorClassName, testimonials, getAuthorName, getAvatarSrc, getInitials]);
945
+ }, [
946
+ testimonialsSlot,
947
+ gridClassName,
948
+ columns,
949
+ cardClassName,
950
+ quoteClassName,
951
+ authorClassName,
952
+ testimonials,
953
+ getAuthorName,
954
+ getAvatarSrc,
955
+ getInitials
956
+ ]);
538
957
  return /* @__PURE__ */ jsxs(
539
958
  Section,
540
959
  {
@@ -543,32 +962,33 @@ function TestimonialsMasonryGrid({
543
962
  pattern,
544
963
  patternOpacity,
545
964
  className,
965
+ containerClassName,
546
966
  children: [
547
967
  /* @__PURE__ */ jsxs(
548
968
  "div",
549
969
  {
550
- className: cn("mx-auto mb-12 max-w-2xl text-center", headerClassName),
970
+ className: cn(
971
+ "mx-auto mb-12 max-w-full md:max-w-2xl text-center",
972
+ headerClassName
973
+ ),
551
974
  children: [
552
975
  heading && (typeof heading === "string" ? /* @__PURE__ */ jsx(
553
976
  "h2",
554
977
  {
555
978
  className: cn(
556
- "text-3xl font-semibold tracking-tight md:text-4xl",
979
+ "text-3xl font-semibold tracking-tight md:text-4xl lg:text-6xl text-balance",
557
980
  headingClassName
558
981
  ),
559
982
  children: heading
560
983
  }
561
- ) : /* @__PURE__ */ jsx("div", { className: headingClassName, children: heading })),
984
+ ) : heading),
562
985
  description && (typeof description === "string" ? /* @__PURE__ */ jsx(
563
986
  "p",
564
987
  {
565
- className: cn(
566
- "mt-4 text-lg text-muted-foreground",
567
- descriptionClassName
568
- ),
988
+ className: cn("mt-4 text-lg text-balance", descriptionClassName),
569
989
  children: description
570
990
  }
571
- ) : /* @__PURE__ */ jsx("div", { className: cn("mt-4", descriptionClassName), children: description }))
991
+ ) : description)
572
992
  ]
573
993
  }
574
994
  ),
@@ -521,7 +521,7 @@ function TestimonialsMiniDividers({
521
521
  "div",
522
522
  {
523
523
  className: cn(
524
- "relative grid border-x border-dashed md:grid-cols-3",
524
+ "relative grid border-x border-b border-dashed md:grid-cols-3",
525
525
  gridClassName
526
526
  ),
527
527
  children: [
@@ -549,8 +549,7 @@ function TestimonialsMiniDividers({
549
549
  {
550
550
  className: cn(
551
551
  "group flex flex-col gap-4 border-t border-dashed p-4 transition-colors duration-300 hover:bg-muted/50 lg:p-8",
552
- index === 1 && "md:border-x md:border-dashed",
553
- index === 2 ? "border-b" : "md:border-b",
552
+ index % 3 === 1 && "md:border-x md:border-dashed",
554
553
  itemClassName
555
554
  ),
556
555
  children: [