@opensite/ui 3.5.0 → 3.5.1

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,15 +1,13 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var React = require('react');
5
- var Autoplay = require('embla-carousel-autoplay');
4
+ var React3 = require('react');
6
5
  var clsx = require('clsx');
7
6
  var tailwindMerge = require('tailwind-merge');
8
7
  var img = require('@page-speed/img');
9
- var useEmblaCarousel = require('embla-carousel-react');
10
8
  var jsxRuntime = require('react/jsx-runtime');
11
-
12
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+ var react = require('motion/react');
10
+ var pressable = require('@page-speed/pressable');
13
11
 
14
12
  function _interopNamespace(e) {
15
13
  if (e && e.__esModule) return e;
@@ -29,143 +27,12 @@ function _interopNamespace(e) {
29
27
  return Object.freeze(n);
30
28
  }
31
29
 
32
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
33
- var Autoplay__default = /*#__PURE__*/_interopDefault(Autoplay);
34
- var useEmblaCarousel__default = /*#__PURE__*/_interopDefault(useEmblaCarousel);
30
+ var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
35
31
 
36
32
  // components/blocks/hero/hero-tech-carousel.tsx
37
33
  function cn(...inputs) {
38
34
  return tailwindMerge.twMerge(clsx.clsx(inputs));
39
35
  }
40
- var CarouselContext = React__namespace.createContext(null);
41
- function useCarousel() {
42
- const context = React__namespace.useContext(CarouselContext);
43
- if (!context) {
44
- throw new Error("useCarousel must be used within a <Carousel />");
45
- }
46
- return context;
47
- }
48
- function Carousel({
49
- orientation = "horizontal",
50
- opts,
51
- setApi,
52
- plugins,
53
- className,
54
- children,
55
- ...props
56
- }) {
57
- const [carouselRef, api] = useEmblaCarousel__default.default(
58
- {
59
- ...opts,
60
- axis: orientation === "horizontal" ? "x" : "y"
61
- },
62
- plugins
63
- );
64
- const [canScrollPrev, setCanScrollPrev] = React__namespace.useState(false);
65
- const [canScrollNext, setCanScrollNext] = React__namespace.useState(false);
66
- const onSelect = React__namespace.useCallback((api2) => {
67
- if (!api2) return;
68
- setCanScrollPrev(api2.canScrollPrev());
69
- setCanScrollNext(api2.canScrollNext());
70
- }, []);
71
- const scrollPrev = React__namespace.useCallback(() => {
72
- api?.scrollPrev();
73
- }, [api]);
74
- const scrollNext = React__namespace.useCallback(() => {
75
- api?.scrollNext();
76
- }, [api]);
77
- const handleKeyDown = React__namespace.useCallback(
78
- (event) => {
79
- if (event.key === "ArrowLeft") {
80
- event.preventDefault();
81
- scrollPrev();
82
- } else if (event.key === "ArrowRight") {
83
- event.preventDefault();
84
- scrollNext();
85
- }
86
- },
87
- [scrollPrev, scrollNext]
88
- );
89
- React__namespace.useEffect(() => {
90
- if (!api || !setApi) return;
91
- setApi(api);
92
- }, [api, setApi]);
93
- React__namespace.useEffect(() => {
94
- if (!api) return;
95
- onSelect(api);
96
- api.on("reInit", onSelect);
97
- api.on("select", onSelect);
98
- return () => {
99
- api?.off("select", onSelect);
100
- };
101
- }, [api, onSelect]);
102
- return /* @__PURE__ */ jsxRuntime.jsx(
103
- CarouselContext.Provider,
104
- {
105
- value: {
106
- carouselRef,
107
- api,
108
- opts,
109
- orientation: orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
110
- scrollPrev,
111
- scrollNext,
112
- canScrollPrev,
113
- canScrollNext
114
- },
115
- children: /* @__PURE__ */ jsxRuntime.jsx(
116
- "div",
117
- {
118
- onKeyDownCapture: handleKeyDown,
119
- className: cn("relative", className),
120
- role: "region",
121
- "aria-roledescription": "carousel",
122
- "data-slot": "carousel",
123
- ...props,
124
- children
125
- }
126
- )
127
- }
128
- );
129
- }
130
- function CarouselContent({ className, ...props }) {
131
- const { carouselRef, orientation } = useCarousel();
132
- return /* @__PURE__ */ jsxRuntime.jsx(
133
- "div",
134
- {
135
- ref: carouselRef,
136
- className: "overflow-hidden",
137
- "data-slot": "carousel-content",
138
- children: /* @__PURE__ */ jsxRuntime.jsx(
139
- "div",
140
- {
141
- className: cn(
142
- "flex",
143
- orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
144
- className
145
- ),
146
- ...props
147
- }
148
- )
149
- }
150
- );
151
- }
152
- function CarouselItem({ className, ...props }) {
153
- const { orientation } = useCarousel();
154
- return /* @__PURE__ */ jsxRuntime.jsx(
155
- "div",
156
- {
157
- role: "group",
158
- "aria-roledescription": "slide",
159
- "data-slot": "carousel-item",
160
- className: cn(
161
- "min-w-0 shrink-0 grow-0 basis-full",
162
- orientation === "horizontal" ? "pl-4" : "pt-4",
163
- className
164
- ),
165
- ...props
166
- }
167
- );
168
- }
169
36
  var maxWidthStyles = {
170
37
  sm: "max-w-screen-sm",
171
38
  md: "max-w-screen-md",
@@ -175,7 +42,7 @@ var maxWidthStyles = {
175
42
  "4xl": "max-w-[1536px]",
176
43
  full: "max-w-full"
177
44
  };
178
- var Container = React__namespace.default.forwardRef(
45
+ var Container = React3__namespace.default.forwardRef(
179
46
  ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
180
47
  const Component = as;
181
48
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -481,7 +348,7 @@ var spacingStyles = {
481
348
  };
482
349
  var predefinedSpacings = ["none", "sm", "md", "lg", "xl", "hero"];
483
350
  var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
484
- var Section = React__namespace.default.forwardRef(
351
+ var Section = React3__namespace.default.forwardRef(
485
352
  ({
486
353
  id,
487
354
  title,
@@ -542,105 +409,478 @@ var Section = React__namespace.default.forwardRef(
542
409
  }
543
410
  );
544
411
  Section.displayName = "Section";
545
- function HeroTechCarousel({
546
- sectionId = "hero-tech-carousel",
547
- heading,
548
- description,
549
- technologies,
550
- carouselSlot,
551
- autoplayDelay = 5e3,
552
- background,
553
- pattern,
554
- patternOpacity,
412
+ var slideVariants = {
413
+ initial: {
414
+ scale: 0.98,
415
+ opacity: 0,
416
+ rotateX: 12
417
+ },
418
+ visible: {
419
+ scale: 1,
420
+ rotateX: 0,
421
+ opacity: 1,
422
+ transition: {
423
+ duration: 0.55,
424
+ ease: [0.645, 0.045, 0.355, 1]
425
+ }
426
+ },
427
+ upExit: {
428
+ opacity: 0.8,
429
+ y: "-120%",
430
+ transition: {
431
+ duration: 0.8
432
+ }
433
+ },
434
+ downExit: {
435
+ opacity: 0.8,
436
+ y: "120%",
437
+ transition: {
438
+ duration: 0.8
439
+ }
440
+ }
441
+ };
442
+ var normalizeIndex = (index, length) => {
443
+ if (!length) return 0;
444
+ const safeIndex = index % length;
445
+ return safeIndex < 0 ? safeIndex + length : safeIndex;
446
+ };
447
+ var ImageSlider = ({
448
+ images,
449
+ children,
450
+ overlay = true,
451
+ overlaySlot,
452
+ overlayClassName,
555
453
  className,
556
- containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
557
- spacing = "py-32",
558
- headingClassName,
559
- descriptionClassName,
454
+ imageClassName,
455
+ contentClassName,
456
+ autoplay = true,
457
+ autoplayIntervalMs = 6e3,
458
+ direction = "up",
459
+ transition = "slide",
460
+ startIndex = 0,
461
+ enableKeyboard = true,
462
+ onSlideChange,
560
463
  optixFlowConfig
561
- }) {
562
- const plugin = React.useRef(
563
- Autoplay__default.default({ delay: autoplayDelay, stopOnInteraction: false })
464
+ }) => {
465
+ const hasImages = images.length > 0;
466
+ const [currentIndex, setCurrentIndex] = React3__namespace.useState(
467
+ () => normalizeIndex(startIndex, images.length)
564
468
  );
565
- const [api, setApi] = React.useState();
566
- const [current, setCurrent] = React.useState(0);
567
- const [fadeIn, setFadeIn] = React.useState(true);
568
- React.useEffect(() => {
569
- if (!api) return;
570
- setCurrent(api.selectedScrollSnap());
571
- const updateCurrent = () => {
572
- setFadeIn(false);
573
- setTimeout(() => {
574
- setCurrent(api.selectedScrollSnap());
575
- setFadeIn(true);
576
- }, 200);
469
+ const handleNext = React3__namespace.useCallback(() => {
470
+ if (!hasImages) return;
471
+ setCurrentIndex((prevIndex) => {
472
+ const nextIndex = prevIndex + 1 >= images.length ? 0 : prevIndex + 1;
473
+ onSlideChange?.(nextIndex);
474
+ return nextIndex;
475
+ });
476
+ }, [hasImages, images.length, onSlideChange]);
477
+ const handlePrevious = React3__namespace.useCallback(() => {
478
+ if (!hasImages) return;
479
+ setCurrentIndex((prevIndex) => {
480
+ const nextIndex = prevIndex - 1 < 0 ? images.length - 1 : prevIndex - 1;
481
+ onSlideChange?.(nextIndex);
482
+ return nextIndex;
483
+ });
484
+ }, [hasImages, images.length, onSlideChange]);
485
+ React3__namespace.useEffect(() => {
486
+ if (!hasImages) return;
487
+ setCurrentIndex(normalizeIndex(startIndex, images.length));
488
+ }, [startIndex, images.length, hasImages]);
489
+ React3__namespace.useEffect(() => {
490
+ if (!enableKeyboard || !hasImages) return;
491
+ const handleKeyDown = (event) => {
492
+ if (event.key === "ArrowRight") {
493
+ handleNext();
494
+ } else if (event.key === "ArrowLeft") {
495
+ handlePrevious();
496
+ }
577
497
  };
578
- api.on("select", updateCurrent);
579
- api.on("settle", updateCurrent);
498
+ window.addEventListener("keydown", handleKeyDown);
580
499
  return () => {
581
- api.off("select", updateCurrent);
582
- api.off("settle", updateCurrent);
500
+ window.removeEventListener("keydown", handleKeyDown);
583
501
  };
584
- }, [api]);
585
- const selectTechnology = (index) => {
586
- if (api) {
587
- api.scrollTo(index);
502
+ }, [enableKeyboard, handleNext, handlePrevious, hasImages]);
503
+ React3__namespace.useEffect(() => {
504
+ if (!autoplay || images.length < 2) return;
505
+ const interval = window.setInterval(handleNext, autoplayIntervalMs);
506
+ return () => window.clearInterval(interval);
507
+ }, [autoplay, autoplayIntervalMs, handleNext, images.length]);
508
+ const activeImage = hasImages ? images[currentIndex] : null;
509
+ const overlayContent = overlaySlot ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("absolute inset-0 z-10", overlayClassName), children: overlaySlot }) : overlay ? /* @__PURE__ */ jsxRuntime.jsx(
510
+ "div",
511
+ {
512
+ className: cn(
513
+ "absolute inset-0 z-10 bg-linear-to-b from-black/35 via-black/50 to-black/70",
514
+ overlayClassName
515
+ )
588
516
  }
589
- };
590
- const renderCarousel = React.useMemo(() => {
591
- if (carouselSlot) return carouselSlot;
592
- if (!technologies || technologies.length === 0) return null;
593
- return /* @__PURE__ */ jsxRuntime.jsx(
594
- Carousel,
595
- {
596
- setApi,
597
- plugins: [plugin.current],
598
- opts: {
599
- loop: true
600
- },
601
- className: "relative mx-auto w-full max-w-3xl before:absolute before:top-0 before:bottom-0 before:left-0 before:z-10 before:w-36 before:bg-linear-to-r before:from-background before:to-transparent after:absolute after:top-0 after:right-0 after:bottom-0 after:z-10 after:w-36 after:bg-linear-to-l after:from-background after:to-transparent",
602
- onMouseEnter: plugin.current.stop,
603
- onMouseLeave: plugin.current.reset,
604
- children: /* @__PURE__ */ jsxRuntime.jsx(CarouselContent, { children: technologies.map((technology, idx) => /* @__PURE__ */ jsxRuntime.jsx(
605
- CarouselItem,
517
+ ) : null;
518
+ return /* @__PURE__ */ jsxRuntime.jsxs(
519
+ "div",
520
+ {
521
+ className: cn(
522
+ "relative flex min-h-[420px] w-full items-center justify-center overflow-hidden rounded-3xl border border-border/40 bg-muted/30 shadow-2xl",
523
+ className
524
+ ),
525
+ style: {
526
+ perspective: "1000px"
527
+ },
528
+ children: [
529
+ transition === "fade" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0", children: images.map((image, index) => /* @__PURE__ */ jsxRuntime.jsx(
530
+ "div",
606
531
  {
607
- className: "basis-1/3 select-none sm:basis-1/4 md:basis-1/6",
608
- children: /* @__PURE__ */ jsxRuntime.jsxs(
609
- "div",
532
+ "aria-hidden": index !== currentIndex,
533
+ className: cn(
534
+ "absolute inset-0 opacity-0 transition-opacity duration-1000 ease-in-out motion-reduce:transition-none",
535
+ index === currentIndex && "opacity-100"
536
+ ),
537
+ children: /* @__PURE__ */ jsxRuntime.jsx(
538
+ img.Img,
610
539
  {
540
+ src: image.src,
541
+ alt: image.alt,
611
542
  className: cn(
612
- "flex cursor-pointer items-center justify-center gap-2 rounded-md border px-4 py-2",
613
- idx === current ? "border-input" : "border-transparent"
543
+ "h-full w-full object-cover object-center",
544
+ imageClassName,
545
+ image.className
614
546
  ),
615
- onClick: () => selectTechnology(idx),
616
- children: [
617
- technology.logo && /* @__PURE__ */ jsxRuntime.jsx(
618
- img.Img,
619
- {
620
- className: "h-4 shrink-0 md:h-7",
621
- src: technology.logo,
622
- alt: technology.name,
623
- optixFlowConfig
624
- }
625
- ),
626
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-nowrap", children: technology.name })
627
- ]
547
+ optixFlowConfig: image.optixFlowConfig ?? optixFlowConfig,
548
+ loading: "eager"
628
549
  }
629
550
  )
630
551
  },
631
- idx
632
- )) })
552
+ `${image.src ?? "image"}-${index}`
553
+ )) }) : /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { mode: "wait", initial: false, children: activeImage ? /* @__PURE__ */ jsxRuntime.jsx(
554
+ react.motion.div,
555
+ {
556
+ initial: "initial",
557
+ animate: "visible",
558
+ exit: direction === "up" ? "upExit" : "downExit",
559
+ variants: slideVariants,
560
+ className: "absolute inset-0",
561
+ children: /* @__PURE__ */ jsxRuntime.jsx(
562
+ img.Img,
563
+ {
564
+ src: activeImage.src,
565
+ alt: activeImage.alt,
566
+ className: cn(
567
+ "h-full w-full object-cover object-center",
568
+ imageClassName,
569
+ activeImage.className
570
+ ),
571
+ optixFlowConfig: activeImage.optixFlowConfig ?? optixFlowConfig,
572
+ loading: "eager"
573
+ }
574
+ )
575
+ },
576
+ `${currentIndex}-${activeImage.src ?? "image"}`
577
+ ) : null }),
578
+ overlayContent,
579
+ children ? /* @__PURE__ */ jsxRuntime.jsx(
580
+ "div",
581
+ {
582
+ className: cn(
583
+ "relative z-20 flex w-full flex-col items-center justify-center px-6 py-12 text-center text-white md:px-12",
584
+ contentClassName
585
+ ),
586
+ children
587
+ }
588
+ ) : null
589
+ ]
590
+ }
591
+ );
592
+ };
593
+ var MOBILE_CLASSES = {
594
+ "fit-left": "items-start md:items-center",
595
+ "fit-center": "items-center",
596
+ "fit-right": "items-end md:items-center",
597
+ "full-left": "items-stretch md:items-center",
598
+ "full-center": "items-stretch md:items-center",
599
+ "full-right": "items-stretch md:items-center"
600
+ };
601
+ function BlockActions({
602
+ mobileConfig,
603
+ actionsClassName,
604
+ verticalSpacing = "mt-4 md:mt-8",
605
+ actions,
606
+ actionsSlot
607
+ }) {
608
+ const width = mobileConfig?.width ?? "full";
609
+ const position = mobileConfig?.position ?? "center";
610
+ const mobileLayoutClass = MOBILE_CLASSES[`${width}-${position}`];
611
+ if (actionsSlot) {
612
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: actionsSlot });
613
+ } else if (actions && actions?.length > 0) {
614
+ return /* @__PURE__ */ jsxRuntime.jsx(
615
+ "div",
616
+ {
617
+ className: cn(
618
+ "flex flex-col md:flex-row flex-wrap gap-4",
619
+ mobileLayoutClass,
620
+ actionsClassName,
621
+ verticalSpacing
622
+ ),
623
+ children: actions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(ActionComponent, { action }, index))
633
624
  }
634
625
  );
626
+ } else {
627
+ return null;
628
+ }
629
+ }
630
+ function ActionComponent({ action }) {
631
+ const {
632
+ label,
633
+ icon,
634
+ iconAfter,
635
+ children,
636
+ href,
637
+ onClick,
638
+ asButton,
639
+ variant,
640
+ size,
641
+ className: actionClassName,
642
+ ...pressableProps
643
+ } = action;
644
+ const shouldStyleAsButton = asButton ?? true;
645
+ const resolvedVariant = shouldStyleAsButton ? variant ?? "default" : void 0;
646
+ const resolvedSize = shouldStyleAsButton ? size ?? "default" : void 0;
647
+ return /* @__PURE__ */ jsxRuntime.jsx(
648
+ pressable.Pressable,
649
+ {
650
+ href,
651
+ onClick,
652
+ asButton: shouldStyleAsButton,
653
+ variant: resolvedVariant,
654
+ size: resolvedSize,
655
+ "data-slot": shouldStyleAsButton ? "button" : void 0,
656
+ "data-variant": resolvedVariant,
657
+ "data-size": resolvedSize,
658
+ className: cn(
659
+ shouldStyleAsButton && pressable.buttonVariants({ variant: resolvedVariant, size: resolvedSize }),
660
+ actionClassName
661
+ ),
662
+ ...pressableProps,
663
+ children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
664
+ icon,
665
+ label,
666
+ iconAfter
667
+ ] })
668
+ }
669
+ );
670
+ }
671
+ var HERO_TECH_CAROUSEL_MAX_ITEMS = 4;
672
+ function resolveLogoSrc(logo) {
673
+ if (typeof logo.src === "string") return logo.src;
674
+ return logo.src?.light;
675
+ }
676
+ function HeroPanel({
677
+ item,
678
+ defaultAutoplayIntervalMs,
679
+ optixFlowConfig,
680
+ panelContentClassName
681
+ }) {
682
+ const {
683
+ logo,
684
+ logoSlot,
685
+ title,
686
+ content,
687
+ actions,
688
+ backgroundMedia,
689
+ backgroundAutoplayIntervalMs,
690
+ id,
691
+ className,
692
+ contentClassName,
693
+ logoClassName,
694
+ titleClassName,
695
+ textClassName,
696
+ actionsClassName,
697
+ overlayClassName,
698
+ optixFlowConfig: itemOptixFlowConfig
699
+ } = item;
700
+ const resolvedOptixFlow = itemOptixFlowConfig ?? optixFlowConfig;
701
+ const renderBackground = React3.useMemo(() => {
702
+ if (!backgroundMedia || backgroundMedia.length === 0) return null;
703
+ if (backgroundMedia.length === 1) {
704
+ const image = backgroundMedia[0];
705
+ if (!image?.src) return null;
706
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 z-0", children: [
707
+ /* @__PURE__ */ jsxRuntime.jsx(
708
+ img.Img,
709
+ {
710
+ src: image.src,
711
+ alt: image.alt ?? "",
712
+ className: cn(
713
+ "h-full w-full object-cover object-center",
714
+ image.className
715
+ ),
716
+ optixFlowConfig: image.optixFlowConfig ?? resolvedOptixFlow,
717
+ loading: "eager"
718
+ }
719
+ ),
720
+ /* @__PURE__ */ jsxRuntime.jsx(
721
+ "div",
722
+ {
723
+ className: cn(
724
+ "absolute inset-0 bg-linear-to-b from-black/55 via-black/45 to-black/65",
725
+ overlayClassName
726
+ )
727
+ }
728
+ )
729
+ ] });
730
+ }
731
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 z-0", children: [
732
+ /* @__PURE__ */ jsxRuntime.jsx(
733
+ ImageSlider,
734
+ {
735
+ images: backgroundMedia,
736
+ className: "h-full w-full rounded-none border-0 shadow-none",
737
+ imageClassName: "object-cover object-center",
738
+ transition: "fade",
739
+ autoplay: true,
740
+ autoplayIntervalMs: backgroundAutoplayIntervalMs ?? defaultAutoplayIntervalMs,
741
+ enableKeyboard: false,
742
+ overlay: false,
743
+ optixFlowConfig: resolvedOptixFlow
744
+ }
745
+ ),
746
+ /* @__PURE__ */ jsxRuntime.jsx(
747
+ "div",
748
+ {
749
+ className: cn(
750
+ "absolute inset-0 bg-linear-to-b from-black/55 via-black/45 to-black/65",
751
+ overlayClassName
752
+ )
753
+ }
754
+ )
755
+ ] });
635
756
  }, [
636
- carouselSlot,
637
- technologies,
638
- setApi,
639
- plugin,
640
- current,
641
- selectTechnology,
642
- optixFlowConfig
757
+ backgroundMedia,
758
+ backgroundAutoplayIntervalMs,
759
+ defaultAutoplayIntervalMs,
760
+ resolvedOptixFlow,
761
+ overlayClassName
643
762
  ]);
763
+ const hasBackground = !!backgroundMedia && backgroundMedia.length > 0;
764
+ const renderLogo = React3.useMemo(() => {
765
+ if (logoSlot) return logoSlot;
766
+ if (!logo) return null;
767
+ const src = resolveLogoSrc(logo);
768
+ if (!src) return null;
769
+ return /* @__PURE__ */ jsxRuntime.jsx(
770
+ img.Img,
771
+ {
772
+ src,
773
+ alt: logo.alt,
774
+ className: cn(
775
+ "h-10 md:h-12 lg:h-14 w-auto max-w-[70%] object-contain",
776
+ logo.imgClassName,
777
+ logoClassName
778
+ ),
779
+ optixFlowConfig: resolvedOptixFlow
780
+ }
781
+ );
782
+ }, [logoSlot, logo, logoClassName, resolvedOptixFlow]);
783
+ const renderTitle = React3.useMemo(() => {
784
+ if (title === void 0 || title === null || title === "") return null;
785
+ if (typeof title === "string") {
786
+ return /* @__PURE__ */ jsxRuntime.jsx(
787
+ "h2",
788
+ {
789
+ className: cn(
790
+ "text-3xl md:text-4xl lg:text-5xl font-semibold text-balance",
791
+ hasBackground && "text-white text-shadow-lg",
792
+ titleClassName
793
+ ),
794
+ children: title
795
+ }
796
+ );
797
+ }
798
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: titleClassName, children: title });
799
+ }, [title, titleClassName, hasBackground]);
800
+ const renderContent = React3.useMemo(() => {
801
+ if (content === void 0 || content === null || content === "") return null;
802
+ if (typeof content === "string") {
803
+ return /* @__PURE__ */ jsxRuntime.jsx(
804
+ "p",
805
+ {
806
+ className: cn(
807
+ "text-base md:text-lg leading-relaxed text-balance",
808
+ hasBackground ? "text-white/90 text-shadow" : "text-muted-foreground",
809
+ textClassName
810
+ ),
811
+ children: content
812
+ }
813
+ );
814
+ }
815
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: textClassName, children: content });
816
+ }, [content, textClassName, hasBackground]);
817
+ return /* @__PURE__ */ jsxRuntime.jsxs(
818
+ "div",
819
+ {
820
+ id,
821
+ "data-slot": "hero-tech-carousel-panel",
822
+ className: cn(
823
+ // Mobile: stack vertically with content-fit height + padding.
824
+ "relative w-full overflow-hidden",
825
+ // Desktop: flex children share the row equally and fill height.
826
+ "md:h-full md:flex-1 md:basis-0 md:min-w-0",
827
+ // Provide a default panel background when no media is supplied so
828
+ // separators between panels remain visible.
829
+ !hasBackground && "bg-muted/30",
830
+ className
831
+ ),
832
+ children: [
833
+ renderBackground,
834
+ /* @__PURE__ */ jsxRuntime.jsxs(
835
+ "div",
836
+ {
837
+ className: cn(
838
+ "relative z-10 flex h-full w-full flex-col items-center justify-center gap-4 md:gap-6",
839
+ // Mobile padding keeps content readable when stacked.
840
+ "px-6 py-12 md:px-8 md:py-12 lg:px-10",
841
+ // Center content vertically; on desktop columns can be quite tall.
842
+ "text-center",
843
+ panelContentClassName,
844
+ contentClassName
845
+ ),
846
+ children: [
847
+ renderLogo,
848
+ renderTitle,
849
+ renderContent,
850
+ /* @__PURE__ */ jsxRuntime.jsx(
851
+ BlockActions,
852
+ {
853
+ actions,
854
+ actionsClassName,
855
+ verticalSpacing: "mt-2 md:mt-4",
856
+ mobileConfig: { width: "fit", position: "center" }
857
+ }
858
+ )
859
+ ]
860
+ }
861
+ )
862
+ ]
863
+ }
864
+ );
865
+ }
866
+ function HeroTechCarousel({
867
+ sectionId = "hero-tech-carousel",
868
+ items,
869
+ backgroundAutoplayIntervalMs = 5e3,
870
+ background,
871
+ spacing = "none",
872
+ pattern,
873
+ patternOpacity,
874
+ className,
875
+ containerClassName = "px-0 sm:px-0 lg:px-0 max-w-full relative z-10 h-auto md:h-dvh w-screen flex items-stretch",
876
+ panelsClassName,
877
+ panelContentClassName,
878
+ optixFlowConfig
879
+ }) {
880
+ const visibleItems = React3.useMemo(() => {
881
+ if (!items || items.length === 0) return [];
882
+ return items.slice(0, HERO_TECH_CAROUSEL_MAX_ITEMS);
883
+ }, [items]);
644
884
  return /* @__PURE__ */ jsxRuntime.jsx(
645
885
  Section,
646
886
  {
@@ -649,80 +889,37 @@ function HeroTechCarousel({
649
889
  spacing,
650
890
  pattern,
651
891
  patternOpacity,
652
- className,
892
+ className: cn(
893
+ "relative w-screen overflow-hidden",
894
+ // Desktop fills the viewport; on mobile content drives the height.
895
+ "h-auto md:h-dvh",
896
+ "px-0 pt-0 pb-0",
897
+ className
898
+ ),
653
899
  containerClassName,
654
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
655
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-center", children: [
656
- heading && (typeof heading === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
657
- "h1",
658
- {
659
- className: cn(
660
- "mx-auto mb-4 max-w-2xl text-center text-4xl font-bold md:text-6xl text-balance",
661
- headingClassName
662
- ),
663
- children: heading
664
- }
665
- ) : /* @__PURE__ */ jsxRuntime.jsx(
666
- "h1",
667
- {
668
- className: cn(
669
- "mx-auto mb-4 max-w-2xl text-center text-4xl font-bold md:text-6xl text-balance",
670
- headingClassName
671
- ),
672
- children: heading
673
- }
674
- )),
675
- description && (typeof description === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
676
- "p",
677
- {
678
- className: cn(
679
- "mx-auto mt-4 max-w-xl text-center text-lg text-balance",
680
- descriptionClassName
681
- ),
682
- children: description
683
- }
684
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: descriptionClassName, children: description })),
685
- technologies && technologies.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
686
- "div",
900
+ children: visibleItems.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(
901
+ "div",
902
+ {
903
+ "data-slot": "hero-tech-carousel-panels",
904
+ className: cn(
905
+ // Mobile: vertical stack with auto height per panel.
906
+ "flex w-full flex-col",
907
+ // Desktop: equal-width row that fills section height.
908
+ "md:h-full md:flex-row md:items-stretch",
909
+ panelsClassName
910
+ ),
911
+ children: visibleItems.map((item, idx) => /* @__PURE__ */ jsxRuntime.jsx(
912
+ HeroPanel,
687
913
  {
688
- className: cn(
689
- "mx-auto bg-muted mt-8 mb-12 flex h-[60px] w-fit items-center gap-2 rounded-md px-4 py-2 text-center"
690
- ),
691
- children: /* @__PURE__ */ jsxRuntime.jsxs(
692
- "div",
693
- {
694
- className: cn(
695
- "flex items-center gap-2 transition-opacity duration-300",
696
- fadeIn ? "opacity-100" : "opacity-0"
697
- ),
698
- children: [
699
- technologies && current && technologies[current] && technologies[current].logo ? /* @__PURE__ */ jsxRuntime.jsx(
700
- img.Img,
701
- {
702
- src: technologies[current]?.logo,
703
- alt: technologies[current]?.name,
704
- className: "h-4 md:h-7",
705
- optixFlowConfig
706
- }
707
- ) : null,
708
- /* @__PURE__ */ jsxRuntime.jsx(
709
- "p",
710
- {
711
- className: cn(
712
- "px-2 font-mono text-sm",
713
- technologies && technologies[current] && technologies[current].logo ? "border-l" : ""
714
- ),
715
- children: technologies[current]?.command
716
- }
717
- )
718
- ]
719
- }
720
- )
721
- }
722
- )
723
- ] }),
724
- renderCarousel
725
- ] })
914
+ item,
915
+ defaultAutoplayIntervalMs: backgroundAutoplayIntervalMs,
916
+ optixFlowConfig,
917
+ panelContentClassName
918
+ },
919
+ item.id ?? idx
920
+ ))
921
+ }
922
+ )
726
923
  }
727
924
  );
728
925
  }