@morphika/andami 0.5.1 → 0.5.2

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 (117) hide show
  1. package/app/admin/assets/page.tsx +6 -6
  2. package/app/admin/database/page.tsx +302 -302
  3. package/app/admin/error.tsx +53 -53
  4. package/app/admin/layout.tsx +320 -320
  5. package/app/admin/navigation/page.tsx +255 -255
  6. package/app/admin/pages/[slug]/page.tsx +6 -6
  7. package/app/admin/pages/page.tsx +11 -11
  8. package/app/admin/projects/page.tsx +14 -14
  9. package/app/admin/setup/page.tsx +1 -1
  10. package/app/admin/styles/page.tsx +1 -1
  11. package/components/admin/MetadataEditor.tsx +6 -6
  12. package/components/admin/nav-builder/NavBuilder.tsx +1 -1
  13. package/components/admin/nav-builder/NavBuilderGrid.tsx +3 -3
  14. package/components/admin/nav-builder/NavGridCell.tsx +48 -48
  15. package/components/admin/nav-builder/NavGridItem.tsx +4 -4
  16. package/components/admin/nav-builder/NavItemSettings.tsx +331 -331
  17. package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -102
  18. package/components/admin/nav-builder/NavLivePreview.tsx +1 -1
  19. package/components/admin/nav-builder/NavMobileLivePreview.tsx +226 -226
  20. package/components/admin/nav-builder/NavMobileSettings.tsx +242 -242
  21. package/components/admin/nav-builder/NavSettingsFields.tsx +514 -514
  22. package/components/admin/setup-wizard/BrandingStep.tsx +3 -3
  23. package/components/admin/setup-wizard/DatabaseStep.tsx +2 -2
  24. package/components/admin/setup-wizard/DoneStep.tsx +1 -1
  25. package/components/admin/setup-wizard/SetupWizard.tsx +4 -4
  26. package/components/admin/setup-wizard/StorageStep.tsx +2 -2
  27. package/components/admin/setup-wizard/WelcomeStep.tsx +2 -2
  28. package/components/admin/styles/ColorsEditor.tsx +2 -2
  29. package/components/admin/styles/FontsEditor.tsx +6 -6
  30. package/components/admin/styles/GridLayoutEditor.tsx +9 -9
  31. package/components/admin/styles/LinksButtonsEditor.tsx +5 -5
  32. package/components/admin/styles/TypographyEditor.tsx +6 -6
  33. package/components/admin/styles/shared.tsx +68 -68
  34. package/components/blocks/AudioBlockRenderer.tsx +286 -286
  35. package/components/blocks/MarqueeBlockRenderer.tsx +316 -0
  36. package/components/blocks/ProjectCarouselBlockRenderer.tsx +1 -1
  37. package/components/builder/BlockCardIcons.tsx +316 -316
  38. package/components/builder/BlockTypePicker.tsx +1 -1
  39. package/components/builder/BubbleIcons.tsx +90 -0
  40. package/components/builder/BuilderCanvas.tsx +2 -0
  41. package/components/builder/CanvasMinimap.tsx +2 -2
  42. package/components/builder/CoverSectionCanvas.tsx +363 -363
  43. package/components/builder/DeviceFrame.tsx +1 -1
  44. package/components/builder/DndWrapper.tsx +3 -3
  45. package/components/builder/InsertionLines.tsx +1 -1
  46. package/components/builder/SectionCardIcons.tsx +421 -320
  47. package/components/builder/SectionEditorBar.tsx +1 -1
  48. package/components/builder/SectionTypePicker.tsx +4 -4
  49. package/components/builder/SectionV2Canvas.tsx +1 -1
  50. package/components/builder/SectionV2Column.tsx +69 -67
  51. package/components/builder/SortableBlock.tsx +93 -73
  52. package/components/builder/SortableRow.tsx +27 -26
  53. package/components/builder/VirtualAssetGrid.tsx +2 -2
  54. package/components/builder/asset-browser/R2BrowserContent.tsx +11 -11
  55. package/components/builder/blockStyles.tsx +192 -185
  56. package/components/builder/color-picker/AlphaSlider.tsx +141 -141
  57. package/components/builder/color-picker/ColorInputs.tsx +105 -105
  58. package/components/builder/color-picker/EyedropperButton.tsx +74 -74
  59. package/components/builder/color-picker/HueSlider.tsx +124 -124
  60. package/components/builder/color-picker/SaturationCanvas.tsx +142 -142
  61. package/components/builder/color-picker/SwatchBar.tsx +93 -93
  62. package/components/builder/editors/AudioBlockEditor.tsx +242 -242
  63. package/components/builder/editors/BeforeAfterBlockEditor.tsx +360 -360
  64. package/components/builder/editors/ButtonBlockEditor.tsx +4 -4
  65. package/components/builder/editors/EnterAnimationPicker.tsx +2 -2
  66. package/components/builder/editors/HoverEffectPicker.tsx +2 -2
  67. package/components/builder/editors/ImageBlockEditor.tsx +2 -2
  68. package/components/builder/editors/ImageGridBlockEditor.tsx +4 -4
  69. package/components/builder/editors/MarqueeBlockEditor.tsx +621 -0
  70. package/components/builder/editors/ProjectCarouselBlockEditor.tsx +443 -443
  71. package/components/builder/editors/ProjectGridEditor.tsx +9 -9
  72. package/components/builder/editors/SpacerBlockEditor.tsx +5 -5
  73. package/components/builder/editors/StaggerSettings.tsx +109 -109
  74. package/components/builder/editors/TextBlockEditor.tsx +3 -3
  75. package/components/builder/editors/TextStylePicker.tsx +1 -1
  76. package/components/builder/editors/VideoBlockEditor.tsx +2 -2
  77. package/components/builder/editors/index.ts +11 -10
  78. package/components/builder/editors/shared.tsx +6 -6
  79. package/components/builder/live-preview/LiveAudioPreview.tsx +120 -120
  80. package/components/builder/live-preview/LiveBeforeAfterPreview.tsx +1 -1
  81. package/components/builder/live-preview/LiveImageGridPreview.tsx +10 -2
  82. package/components/builder/live-preview/LiveImagePreview.tsx +1 -1
  83. package/components/builder/live-preview/LiveMarqueePreview.tsx +39 -0
  84. package/components/builder/live-preview/LiveProjectCarouselPreview.tsx +1 -1
  85. package/components/builder/live-preview/LiveVideoPreview.tsx +1 -1
  86. package/components/builder/live-preview/ProjectCardWrapper.tsx +291 -291
  87. package/components/builder/settings-panel/AnimationTab.tsx +138 -138
  88. package/components/builder/settings-panel/BlockLayoutTab.tsx +7 -7
  89. package/components/builder/settings-panel/CardEntranceSection.tsx +114 -114
  90. package/components/builder/settings-panel/ColumnV2Settings.tsx +5 -5
  91. package/components/builder/settings-panel/CoverSectionLayoutTab.tsx +71 -71
  92. package/components/builder/settings-panel/CoverSectionSettings.tsx +335 -335
  93. package/components/builder/settings-panel/PageSettings.tsx +3 -3
  94. package/components/builder/settings-panel/ParallaxSlideSettings.tsx +2 -2
  95. package/components/builder/settings-panel/SectionV2AnimationTab.tsx +4 -4
  96. package/components/builder/settings-panel/SectionV2LayoutTab.tsx +356 -356
  97. package/components/builder/settings-panel/SectionV2Settings.tsx +14 -14
  98. package/components/builder/settings-panel/TRBLInputs.tsx +1 -1
  99. package/lib/animation/enter-types.ts +1 -0
  100. package/lib/animation/hover-effect-presets.ts +210 -210
  101. package/lib/animation/hover-effect-types.ts +1 -0
  102. package/lib/builder/block-registrations.ts +468 -417
  103. package/lib/builder/constants.ts +111 -111
  104. package/lib/builder/store-sections.ts +2 -2
  105. package/lib/builder/types-slices.ts +414 -414
  106. package/lib/builder/types.ts +4 -1
  107. package/lib/config/index.ts +27 -27
  108. package/lib/sanity/types.ts +98 -1
  109. package/lib/version.ts +1 -1
  110. package/package.json +1 -1
  111. package/sanity/schemas/blocks/audioBlock.ts +69 -69
  112. package/sanity/schemas/blocks/index.ts +12 -11
  113. package/sanity/schemas/blocks/marqueeBlock.ts +292 -0
  114. package/sanity/schemas/index.ts +120 -117
  115. package/styles/admin.css +85 -85
  116. package/styles/animations.css +237 -237
  117. package/styles/base.css +114 -114
@@ -1,120 +1,120 @@
1
- "use client";
2
-
3
- import { adminAssetUrl, adminThumbUrl } from "../../../lib/assets";
4
- import type { AudioBlock } from "../../../lib/sanity/types";
5
-
6
- /**
7
- * LiveAudioPreview — Static preview for builder canvas.
8
- *
9
- * Same layout as the runtime renderer but no audio element / no playback —
10
- * a frozen snapshot with a 0% progress bar, a play glyph, and a dummy
11
- * `0:00 / 0:00` time label. Metadata (title / artist) and cover art
12
- * render when present.
13
- */
14
-
15
- const widthStyleMap: Record<string, { width: string; margin?: string }> = {
16
- full: { width: "100%" },
17
- contained: { width: "75%", margin: "0 auto" },
18
- small: { width: "50%", margin: "0 auto" },
19
- };
20
-
21
- export default function LiveAudioPreview({ block }: { block: AudioBlock }) {
22
- const accent = block.accent_color || "#4794E2";
23
- const coverSrc = block.cover_path ? (adminThumbUrl(block.cover_path) || adminAssetUrl(block.cover_path)) : null;
24
-
25
- const isFill = block.width === "fill";
26
- const widthStyle = isFill ? { width: "100%" } : (widthStyleMap[block.width ?? "contained"] || widthStyleMap.contained);
27
-
28
- const rawRadius = block.border_radius ? String(block.border_radius).replace(/[a-z%]+$/i, "") : "";
29
- const borderRadius = rawRadius && !isNaN(Number(rawRadius)) ? `${rawRadius}px` : "12px";
30
-
31
- const hasMetaText = !!(block.title || block.artist);
32
- const hasAsset = !!block.asset_path;
33
-
34
- const containerStyle: React.CSSProperties = {
35
- ...widthStyle,
36
- display: "flex",
37
- alignItems: "center",
38
- gap: 14,
39
- padding: "12px 16px",
40
- background: "#fafafa",
41
- border: "1px solid #ececec",
42
- borderRadius,
43
- boxShadow: block.shadow ? "0 8px 24px -12px rgba(0,0,0,0.25)" : undefined,
44
- overflow: "hidden",
45
- opacity: hasAsset ? 1 : 0.75,
46
- };
47
-
48
- return (
49
- <div style={containerStyle}>
50
- {coverSrc ? (
51
- <div style={{ width: 52, height: 52, flexShrink: 0, borderRadius: 8, overflow: "hidden", background: "#eee" }}>
52
- {/* eslint-disable-next-line @next/next/no-img-element */}
53
- <img src={coverSrc} alt={block.alt || block.title || ""} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
54
- </div>
55
- ) : null}
56
-
57
- <div
58
- aria-hidden
59
- style={{
60
- width: 40,
61
- height: 40,
62
- flexShrink: 0,
63
- borderRadius: "50%",
64
- background: accent,
65
- color: "#fff",
66
- display: "flex",
67
- alignItems: "center",
68
- justifyContent: "center",
69
- }}
70
- >
71
- <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{ marginLeft: 2 }}>
72
- <path d="M8 5v14l11-7z" />
73
- </svg>
74
- </div>
75
-
76
- <div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 4 }}>
77
- {hasMetaText ? (
78
- <div style={{ display: "flex", alignItems: "baseline", gap: 6, minWidth: 0 }}>
79
- {block.title && (
80
- <span style={{ fontSize: 13, fontWeight: 600, color: "#111", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
81
- {block.title}
82
- </span>
83
- )}
84
- {block.artist && (
85
- <span style={{ fontSize: 12, color: "#777", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
86
- {block.artist}
87
- </span>
88
- )}
89
- </div>
90
- ) : (
91
- !hasAsset && (
92
- <span style={{ fontSize: 11, color: "#8a8f98" }}>Audio — pick a file</span>
93
- )
94
- )}
95
- <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
96
- <div style={{ flex: 1, height: 4, background: "#e5e5e5", borderRadius: 999, position: "relative" }}>
97
- <div style={{ position: "absolute", inset: 0, width: "0%", background: accent, borderRadius: 999 }} />
98
- <div
99
- style={{
100
- position: "absolute",
101
- top: "50%",
102
- left: "0%",
103
- width: 10,
104
- height: 10,
105
- marginTop: -5,
106
- marginLeft: -5,
107
- borderRadius: "50%",
108
- background: "#fff",
109
- boxShadow: `0 0 0 2px ${accent}`,
110
- }}
111
- />
112
- </div>
113
- <span style={{ fontSize: 11, color: "#777", fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" }}>
114
- 0:00 / 0:00
115
- </span>
116
- </div>
117
- </div>
118
- </div>
119
- );
120
- }
1
+ "use client";
2
+
3
+ import { adminAssetUrl, adminThumbUrl } from "../../../lib/assets";
4
+ import type { AudioBlock } from "../../../lib/sanity/types";
5
+
6
+ /**
7
+ * LiveAudioPreview — Static preview for builder canvas.
8
+ *
9
+ * Same layout as the runtime renderer but no audio element / no playback —
10
+ * a frozen snapshot with a 0% progress bar, a play glyph, and a dummy
11
+ * `0:00 / 0:00` time label. Metadata (title / artist) and cover art
12
+ * render when present.
13
+ */
14
+
15
+ const widthStyleMap: Record<string, { width: string; margin?: string }> = {
16
+ full: { width: "100%" },
17
+ contained: { width: "75%", margin: "0 auto" },
18
+ small: { width: "50%", margin: "0 auto" },
19
+ };
20
+
21
+ export default function LiveAudioPreview({ block }: { block: AudioBlock }) {
22
+ const accent = block.accent_color || "#3580f9";
23
+ const coverSrc = block.cover_path ? (adminThumbUrl(block.cover_path) || adminAssetUrl(block.cover_path)) : null;
24
+
25
+ const isFill = block.width === "fill";
26
+ const widthStyle = isFill ? { width: "100%" } : (widthStyleMap[block.width ?? "contained"] || widthStyleMap.contained);
27
+
28
+ const rawRadius = block.border_radius ? String(block.border_radius).replace(/[a-z%]+$/i, "") : "";
29
+ const borderRadius = rawRadius && !isNaN(Number(rawRadius)) ? `${rawRadius}px` : "12px";
30
+
31
+ const hasMetaText = !!(block.title || block.artist);
32
+ const hasAsset = !!block.asset_path;
33
+
34
+ const containerStyle: React.CSSProperties = {
35
+ ...widthStyle,
36
+ display: "flex",
37
+ alignItems: "center",
38
+ gap: 14,
39
+ padding: "12px 16px",
40
+ background: "#fafafa",
41
+ border: "1px solid #ececec",
42
+ borderRadius,
43
+ boxShadow: block.shadow ? "0 8px 24px -12px rgba(0,0,0,0.25)" : undefined,
44
+ overflow: "hidden",
45
+ opacity: hasAsset ? 1 : 0.75,
46
+ };
47
+
48
+ return (
49
+ <div style={containerStyle}>
50
+ {coverSrc ? (
51
+ <div style={{ width: 52, height: 52, flexShrink: 0, borderRadius: 8, overflow: "hidden", background: "#eee" }}>
52
+ {/* eslint-disable-next-line @next/next/no-img-element */}
53
+ <img src={coverSrc} alt={block.alt || block.title || ""} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
54
+ </div>
55
+ ) : null}
56
+
57
+ <div
58
+ aria-hidden
59
+ style={{
60
+ width: 40,
61
+ height: 40,
62
+ flexShrink: 0,
63
+ borderRadius: "50%",
64
+ background: accent,
65
+ color: "#fff",
66
+ display: "flex",
67
+ alignItems: "center",
68
+ justifyContent: "center",
69
+ }}
70
+ >
71
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{ marginLeft: 2 }}>
72
+ <path d="M8 5v14l11-7z" />
73
+ </svg>
74
+ </div>
75
+
76
+ <div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 4 }}>
77
+ {hasMetaText ? (
78
+ <div style={{ display: "flex", alignItems: "baseline", gap: 6, minWidth: 0 }}>
79
+ {block.title && (
80
+ <span style={{ fontSize: 13, fontWeight: 600, color: "#111", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
81
+ {block.title}
82
+ </span>
83
+ )}
84
+ {block.artist && (
85
+ <span style={{ fontSize: 12, color: "#777", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
86
+ {block.artist}
87
+ </span>
88
+ )}
89
+ </div>
90
+ ) : (
91
+ !hasAsset && (
92
+ <span style={{ fontSize: 11, color: "#8a8f98" }}>Audio — pick a file</span>
93
+ )
94
+ )}
95
+ <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
96
+ <div style={{ flex: 1, height: 4, background: "#e5e5e5", borderRadius: 999, position: "relative" }}>
97
+ <div style={{ position: "absolute", inset: 0, width: "0%", background: accent, borderRadius: 999 }} />
98
+ <div
99
+ style={{
100
+ position: "absolute",
101
+ top: "50%",
102
+ left: "0%",
103
+ width: 10,
104
+ height: 10,
105
+ marginTop: -5,
106
+ marginLeft: -5,
107
+ borderRadius: "50%",
108
+ background: "#fff",
109
+ boxShadow: `0 0 0 2px ${accent}`,
110
+ }}
111
+ />
112
+ </div>
113
+ <span style={{ fontSize: 11, color: "#777", fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" }}>
114
+ 0:00 / 0:00
115
+ </span>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ );
120
+ }
@@ -89,7 +89,7 @@ export default function LiveBeforeAfterPreview({ block }: { block: BeforeAfterBl
89
89
  : { width: "100%" };
90
90
  return (
91
91
  <div style={wrapperStyle}>
92
- <div className="w-full h-full min-h-[240px] rounded flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
92
+ <div className="w-full h-full min-h-[240px] flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
93
93
  <svg width="56" height="56" viewBox="0 0 56 56" fill="none" aria-hidden="true">
94
94
  <rect x="6" y="10" width="44" height="36" rx="3" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
95
95
  <line x1="28" y1="10" x2="28" y2="46" stroke="#b0b5bd" strokeWidth="1.5" />
@@ -55,8 +55,16 @@ export default function LiveImageGridPreview({ block }: { block: ImageGridBlock
55
55
  const images = block.images || [];
56
56
  if (images.length === 0) {
57
57
  return (
58
- <div className="border border-dashed border-neutral-700 rounded bg-neutral-900/50 flex items-center justify-center py-8">
59
- <span className="text-neutral-600 text-xs">Empty image grid</span>
58
+ <div style={{ width: "100%" }}>
59
+ <div className="w-full h-full min-h-[240px] flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
60
+ <svg width="56" height="56" viewBox="0 0 56 56" fill="none" aria-hidden="true">
61
+ <rect x="8" y="8" width="18" height="18" rx="2" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
62
+ <rect x="30" y="8" width="18" height="18" rx="2" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
63
+ <rect x="8" y="30" width="18" height="18" rx="2" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
64
+ <rect x="30" y="30" width="18" height="18" rx="2" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
65
+ </svg>
66
+ <span className="text-[11px] text-neutral-500">No images yet</span>
67
+ </div>
60
68
  </div>
61
69
  );
62
70
  }
@@ -20,7 +20,7 @@ export default function LiveImagePreview({ block }: { block: ImageBlock }) {
20
20
  : { width: "100%" };
21
21
  return (
22
22
  <div style={wrapperStyle}>
23
- <div className="w-full h-full min-h-[240px] rounded flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
23
+ <div className="w-full h-full min-h-[240px] flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
24
24
  <svg width="56" height="56" viewBox="0 0 56 56" fill="none" aria-hidden="true">
25
25
  <rect x="6" y="10" width="44" height="36" rx="3" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
26
26
  <circle cx="18" cy="21" r="3" fill="#b0b5bd" />
@@ -0,0 +1,39 @@
1
+ "use client";
2
+
3
+ /**
4
+ * LiveMarqueePreview — Builder canvas preview for marqueeBlock.
5
+ *
6
+ * Unlike LiveProjectCarouselPreview (which renders mockup cards because
7
+ * the public carousel hits the projects API), the marquee content IS the
8
+ * user's items — same text, same images. So this preview just delegates
9
+ * to the public renderer with two tweaks:
10
+ *
11
+ * - speedMultiplier = 0.3 → motion is visible but gentle, won't distract
12
+ * while editing. User's configured speed still drives the public site.
13
+ * - alwaysPlay = true → disables the IntersectionObserver pause, since
14
+ * the builder canvas has its own scrolling and the observer can yield
15
+ * false "off-screen" reports.
16
+ *
17
+ * Intentionally thin — mirrors the one-liner delegation pattern used for
18
+ * simple block types whose builder preview and public render don't diverge.
19
+ */
20
+
21
+ import type { MarqueeBlock } from "../../../lib/sanity/types";
22
+ import type { DeviceViewport } from "../../../lib/builder/types";
23
+ import MarqueeBlockRenderer from "../../blocks/MarqueeBlockRenderer";
24
+
25
+ export interface LiveMarqueePreviewProps {
26
+ block: MarqueeBlock;
27
+ viewport?: DeviceViewport;
28
+ editable?: boolean;
29
+ }
30
+
31
+ export default function LiveMarqueePreview({ block }: LiveMarqueePreviewProps) {
32
+ return (
33
+ <MarqueeBlockRenderer
34
+ block={block}
35
+ speedMultiplier={0.3}
36
+ alwaysPlay
37
+ />
38
+ );
39
+ }
@@ -141,7 +141,7 @@ export default function LiveProjectCarouselPreview({
141
141
  <div ref={containerCallbackRef} className="relative w-full">
142
142
  {/* Scroll track — same mechanics as public renderer so layout is faithful */}
143
143
  <div
144
- className="flex overflow-x-auto"
144
+ className="flex overflow-x-auto py-3"
145
145
  style={{
146
146
  gap: `${gap}px`,
147
147
  scrollSnapType: snapScroll ? "x mandatory" : undefined,
@@ -26,7 +26,7 @@ export default function LiveVideoPreview({ block }: { block: VideoBlock }) {
26
26
  : { width: "100%" };
27
27
  return (
28
28
  <div style={wrapperStyle}>
29
- <div className="w-full h-full min-h-[240px] rounded flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
29
+ <div className="w-full h-full min-h-[240px] flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
30
30
  <svg width="56" height="56" viewBox="0 0 56 56" fill="none" aria-hidden="true">
31
31
  <circle cx="28" cy="28" r="22" fill="#FFFFFF" stroke="#b0b5bd" strokeWidth="1.5" />
32
32
  <path d="M24 20 L37 28 L24 36 Z" fill="#b0b5bd" />