@frequencyads/components 0.1.0 → 0.1.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.
package/dist/index.css CHANGED
@@ -237,6 +237,11 @@
237
237
  .root {
238
238
  position: absolute;
239
239
  inset: 0;
240
+ z-index: 1;
241
+ overflow: hidden;
242
+ pointer-events: none;
243
+ }
244
+ .video {
240
245
  width: 100%;
241
246
  height: 100%;
242
247
  object-fit: cover;
@@ -246,17 +251,34 @@
246
251
  .root {
247
252
  position: relative;
248
253
  min-height: 100vh;
249
- display: flex;
250
- align-items: center;
251
- justify-content: center;
252
- background-color: #121212;
253
254
  overflow: hidden;
255
+ background-color: #121212;
254
256
  }
255
257
  .content {
256
258
  position: relative;
257
- z-index: 2;
258
- text-align: center;
259
+ z-index: 10;
260
+ min-height: 100vh;
261
+ display: flex;
262
+ flex-direction: column;
263
+ align-items: center;
264
+ justify-content: center;
259
265
  padding: var(--mantine-spacing-xl);
266
+ text-align: center;
267
+ }
268
+ .label {
269
+ position: relative;
270
+ z-index: 20;
271
+ font-family: Montserrat, sans-serif;
272
+ font-size: calc(0.625rem * var(--mantine-scale));
273
+ text-transform: uppercase;
274
+ letter-spacing: 0.3em;
275
+ color: rgba(255, 255, 255, 0.6);
276
+ margin-bottom: calc(2rem * var(--mantine-scale));
277
+ }
278
+ @media (min-width: 48em) {
279
+ .label {
280
+ font-size: var(--mantine-font-size-xs);
281
+ }
260
282
  }
261
283
  .vignette {
262
284
  position: absolute;
@@ -276,12 +298,14 @@
276
298
  }
277
299
  .logoWrapper {
278
300
  position: relative;
301
+ margin-bottom: calc(2.5rem * var(--mantine-scale));
279
302
  }
280
303
  .logo {
281
304
  position: relative;
282
- z-index: 2;
305
+ z-index: 10;
283
306
  width: calc(10rem * var(--mantine-scale));
284
307
  height: calc(10rem * var(--mantine-scale));
308
+ object-fit: contain;
285
309
  }
286
310
  @media (min-width: 48em) {
287
311
  .logo {
@@ -304,14 +328,15 @@
304
328
  background:
305
329
  radial-gradient(
306
330
  circle,
307
- #169BDE 0%,
308
- #7E57C2 50%,
331
+ #169bde 0%,
332
+ #7e57c2 50%,
309
333
  transparent 70%);
310
334
  transform: scale(1.5);
311
335
  pointer-events: none;
312
336
  }
313
337
  .textWrapper {
314
338
  position: relative;
339
+ margin-bottom: calc(2.5rem * var(--mantine-scale));
315
340
  }
316
341
  .heading {
317
342
  position: relative;
@@ -351,6 +376,8 @@
351
376
  animation: shimmer 6s ease-in-out infinite;
352
377
  }
353
378
  .tagline {
379
+ position: relative;
380
+ z-index: 20;
354
381
  color: rgba(255, 255, 255, 0.6);
355
382
  font-size: var(--mantine-font-size-lg);
356
383
  font-family:
@@ -358,6 +385,7 @@
358
385
  "Source Sans Pro",
359
386
  sans-serif;
360
387
  max-width: calc(35rem * var(--mantine-scale));
388
+ margin-bottom: calc(3rem * var(--mantine-scale));
361
389
  }
362
390
  @media (min-width: 48em) {
363
391
  .tagline {
@@ -377,6 +405,32 @@
377
405
  .secondaryButton:hover {
378
406
  background-color: rgba(255, 255, 255, 0.05);
379
407
  }
408
+ .scrollIndicator {
409
+ position: absolute;
410
+ bottom: calc(3rem * var(--mantine-scale));
411
+ left: 50%;
412
+ transform: translateX(-50%);
413
+ display: flex;
414
+ flex-direction: column;
415
+ align-items: center;
416
+ gap: calc(1rem * var(--mantine-scale));
417
+ }
418
+ .scrollLabel {
419
+ font-family: Montserrat, sans-serif;
420
+ font-size: calc(0.625rem * var(--mantine-scale));
421
+ text-transform: uppercase;
422
+ letter-spacing: 0.3em;
423
+ color: rgba(255, 255, 255, 0.2);
424
+ }
425
+ .scrollLine {
426
+ width: 1px;
427
+ height: calc(2rem * var(--mantine-scale));
428
+ background:
429
+ linear-gradient(
430
+ to bottom,
431
+ rgba(255, 255, 255, 0.4),
432
+ transparent);
433
+ }
380
434
 
381
435
  /* src/MiniAudioPlayer/MiniAudioPlayer.module.css */
382
436
  .root {
@@ -541,3 +595,15 @@
541
595
  .description {
542
596
  max-width: 36rem;
543
597
  }
598
+
599
+ /* src/TestimonialCard/TestimonialCard.module.css */
600
+ .root {
601
+ border-left: 4px solid;
602
+ background-color: var(--mantine-color-gray-0);
603
+ }
604
+ [data-mantine-color-scheme=dark] .root {
605
+ background-color: var(--mantine-color-dark-6);
606
+ }
607
+ .quoteIcon {
608
+ opacity: 0.3;
609
+ }
package/dist/index.d.mts CHANGED
@@ -204,6 +204,8 @@ interface HeroProps {
204
204
  variant?: 'logo' | 'text';
205
205
  /** Heading text shown large in 'text' variant, used as alt text in 'logo' variant */
206
206
  heading?: string;
207
+ /** Small label shown above the logo/heading (e.g. "Brand Guidelines") */
208
+ label?: string;
207
209
  /** Subtitle shown below the logo or heading */
208
210
  tagline?: string;
209
211
  /** Image source for logo variant (defaults to Frequency mark) */
@@ -220,8 +222,10 @@ interface HeroProps {
220
222
  backgroundVideoSrc?: string;
221
223
  /** Show animated waveform (default true) */
222
224
  showWaveform?: boolean;
225
+ /** Show scroll indicator at bottom (default false) */
226
+ showScrollIndicator?: boolean;
223
227
  }
224
- declare function Hero({ variant, heading, tagline, logoSrc, gradient: gradientName, shimmer, primaryAction, secondaryAction, backgroundVideoSrc, showWaveform, }: HeroProps): react_jsx_runtime.JSX.Element;
228
+ declare function Hero({ variant, heading, label, tagline, logoSrc, gradient: gradientName, shimmer, primaryAction, secondaryAction, backgroundVideoSrc, showWaveform, showScrollIndicator, }: HeroProps): react_jsx_runtime.JSX.Element;
225
229
 
226
230
  interface HintBadgeProps {
227
231
  /** Whether this is a "do" (positive) or "don't" (negative) hint */
@@ -330,6 +334,22 @@ interface SplitSectionProps {
330
334
  }
331
335
  declare function SplitSection({ badge, badgeColor, subtitle, title, titleColor, preTitle, description, actions, heading, children, stickyHeading, py, reversed, id, className, }: SplitSectionProps): react_jsx_runtime.JSX.Element;
332
336
 
337
+ interface TestimonialCardProps {
338
+ /** The testimonial text */
339
+ quote: string;
340
+ /** Person's name */
341
+ name: string;
342
+ /** Job title / role */
343
+ role?: string;
344
+ /** Company name */
345
+ company?: string;
346
+ /** Avatar image URL (falls back to initials) */
347
+ avatarSrc?: string;
348
+ /** Accent color for the left border and quote icon (default: 'blue') */
349
+ accentColor?: MantineColor;
350
+ }
351
+ declare function TestimonialCard({ quote, name, role, company, avatarSrc, accentColor, }: TestimonialCardProps): react_jsx_runtime.JSX.Element;
352
+
333
353
  interface VideoBackgroundProps {
334
354
  /** Video source URL */
335
355
  src: string;
@@ -338,4 +358,4 @@ interface VideoBackgroundProps {
338
358
  }
339
359
  declare function VideoBackground({ src, opacity }: VideoBackgroundProps): react_jsx_runtime.JSX.Element;
340
360
 
341
- export { AnimatedCounter, type AnimatedCounterProps, AnimatedWaveform, type AnimatedWaveformProps, AudioPlayer, type AudioPlayerProps, AudioWaveform, type AudioWaveformHandle, type AudioWaveformProps, CodeBlock, type CodeBlockProps, ColorPalette, type ColorPaletteProps, ColorSwatch, type ColorSwatchProps, Copyable, type CopyableProps, DosDonts, type DosDontsItem, type DosDontsProps, ExpandableCard, type ExpandableCardProps, FadeInSection, type FadeInSectionProps, FrequencyWave, type FrequencyWaveProps, type GradientName, GradientSwatch, type GradientSwatchProps, Hero, type HeroAction, type HeroProps, HintBadge, type HintBadgeProps, MiniAudioPlayer, type MiniAudioPlayerProps, PrincipleCard, type PrincipleCardProps, SpeedDial, type SpeedDialAction, type SpeedDialProps, SplitSection, type SplitSectionProps, VideoBackground, type VideoBackgroundProps };
361
+ export { AnimatedCounter, type AnimatedCounterProps, AnimatedWaveform, type AnimatedWaveformProps, AudioPlayer, type AudioPlayerProps, AudioWaveform, type AudioWaveformHandle, type AudioWaveformProps, CodeBlock, type CodeBlockProps, ColorPalette, type ColorPaletteProps, ColorSwatch, type ColorSwatchProps, Copyable, type CopyableProps, DosDonts, type DosDontsItem, type DosDontsProps, ExpandableCard, type ExpandableCardProps, FadeInSection, type FadeInSectionProps, FrequencyWave, type FrequencyWaveProps, type GradientName, GradientSwatch, type GradientSwatchProps, Hero, type HeroAction, type HeroProps, HintBadge, type HintBadgeProps, MiniAudioPlayer, type MiniAudioPlayerProps, PrincipleCard, type PrincipleCardProps, SpeedDial, type SpeedDialAction, type SpeedDialProps, SplitSection, type SplitSectionProps, TestimonialCard, type TestimonialCardProps, VideoBackground, type VideoBackgroundProps };
package/dist/index.d.ts CHANGED
@@ -204,6 +204,8 @@ interface HeroProps {
204
204
  variant?: 'logo' | 'text';
205
205
  /** Heading text shown large in 'text' variant, used as alt text in 'logo' variant */
206
206
  heading?: string;
207
+ /** Small label shown above the logo/heading (e.g. "Brand Guidelines") */
208
+ label?: string;
207
209
  /** Subtitle shown below the logo or heading */
208
210
  tagline?: string;
209
211
  /** Image source for logo variant (defaults to Frequency mark) */
@@ -220,8 +222,10 @@ interface HeroProps {
220
222
  backgroundVideoSrc?: string;
221
223
  /** Show animated waveform (default true) */
222
224
  showWaveform?: boolean;
225
+ /** Show scroll indicator at bottom (default false) */
226
+ showScrollIndicator?: boolean;
223
227
  }
224
- declare function Hero({ variant, heading, tagline, logoSrc, gradient: gradientName, shimmer, primaryAction, secondaryAction, backgroundVideoSrc, showWaveform, }: HeroProps): react_jsx_runtime.JSX.Element;
228
+ declare function Hero({ variant, heading, label, tagline, logoSrc, gradient: gradientName, shimmer, primaryAction, secondaryAction, backgroundVideoSrc, showWaveform, showScrollIndicator, }: HeroProps): react_jsx_runtime.JSX.Element;
225
229
 
226
230
  interface HintBadgeProps {
227
231
  /** Whether this is a "do" (positive) or "don't" (negative) hint */
@@ -330,6 +334,22 @@ interface SplitSectionProps {
330
334
  }
331
335
  declare function SplitSection({ badge, badgeColor, subtitle, title, titleColor, preTitle, description, actions, heading, children, stickyHeading, py, reversed, id, className, }: SplitSectionProps): react_jsx_runtime.JSX.Element;
332
336
 
337
+ interface TestimonialCardProps {
338
+ /** The testimonial text */
339
+ quote: string;
340
+ /** Person's name */
341
+ name: string;
342
+ /** Job title / role */
343
+ role?: string;
344
+ /** Company name */
345
+ company?: string;
346
+ /** Avatar image URL (falls back to initials) */
347
+ avatarSrc?: string;
348
+ /** Accent color for the left border and quote icon (default: 'blue') */
349
+ accentColor?: MantineColor;
350
+ }
351
+ declare function TestimonialCard({ quote, name, role, company, avatarSrc, accentColor, }: TestimonialCardProps): react_jsx_runtime.JSX.Element;
352
+
333
353
  interface VideoBackgroundProps {
334
354
  /** Video source URL */
335
355
  src: string;
@@ -338,4 +358,4 @@ interface VideoBackgroundProps {
338
358
  }
339
359
  declare function VideoBackground({ src, opacity }: VideoBackgroundProps): react_jsx_runtime.JSX.Element;
340
360
 
341
- export { AnimatedCounter, type AnimatedCounterProps, AnimatedWaveform, type AnimatedWaveformProps, AudioPlayer, type AudioPlayerProps, AudioWaveform, type AudioWaveformHandle, type AudioWaveformProps, CodeBlock, type CodeBlockProps, ColorPalette, type ColorPaletteProps, ColorSwatch, type ColorSwatchProps, Copyable, type CopyableProps, DosDonts, type DosDontsItem, type DosDontsProps, ExpandableCard, type ExpandableCardProps, FadeInSection, type FadeInSectionProps, FrequencyWave, type FrequencyWaveProps, type GradientName, GradientSwatch, type GradientSwatchProps, Hero, type HeroAction, type HeroProps, HintBadge, type HintBadgeProps, MiniAudioPlayer, type MiniAudioPlayerProps, PrincipleCard, type PrincipleCardProps, SpeedDial, type SpeedDialAction, type SpeedDialProps, SplitSection, type SplitSectionProps, VideoBackground, type VideoBackgroundProps };
361
+ export { AnimatedCounter, type AnimatedCounterProps, AnimatedWaveform, type AnimatedWaveformProps, AudioPlayer, type AudioPlayerProps, AudioWaveform, type AudioWaveformHandle, type AudioWaveformProps, CodeBlock, type CodeBlockProps, ColorPalette, type ColorPaletteProps, ColorSwatch, type ColorSwatchProps, Copyable, type CopyableProps, DosDonts, type DosDontsItem, type DosDontsProps, ExpandableCard, type ExpandableCardProps, FadeInSection, type FadeInSectionProps, FrequencyWave, type FrequencyWaveProps, type GradientName, GradientSwatch, type GradientSwatchProps, Hero, type HeroAction, type HeroProps, HintBadge, type HintBadgeProps, MiniAudioPlayer, type MiniAudioPlayerProps, PrincipleCard, type PrincipleCardProps, SpeedDial, type SpeedDialAction, type SpeedDialProps, SplitSection, type SplitSectionProps, TestimonialCard, type TestimonialCardProps, VideoBackground, type VideoBackgroundProps };
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -78,6 +79,7 @@ __export(index_exports, {
78
79
  PrincipleCard: () => PrincipleCard,
79
80
  SpeedDial: () => SpeedDial,
80
81
  SplitSection: () => SplitSection,
82
+ TestimonialCard: () => TestimonialCard,
81
83
  VideoBackground: () => VideoBackground
82
84
  });
83
85
  module.exports = __toCommonJS(index_exports);
@@ -1081,7 +1083,9 @@ function GradientSwatch({ name, colors, description }) {
1081
1083
  }
1082
1084
 
1083
1085
  // src/Hero/Hero.tsx
1086
+ var import_react10 = require("react");
1084
1087
  var import_core13 = require("@mantine/core");
1088
+ var import_framer_motion3 = require("framer-motion");
1085
1089
  var import_frequency_mark_white = __toESM(require("@frequencyads/brand/assets/frequency-mark-white.svg"));
1086
1090
  var import_colors = require("@frequencyads/brand/colors");
1087
1091
 
@@ -1091,18 +1095,7 @@ var VideoBackground_default = {};
1091
1095
  // src/VideoBackground/VideoBackground.tsx
1092
1096
  var import_jsx_runtime14 = require("react/jsx-runtime");
1093
1097
  function VideoBackground({ src, opacity = 0.3 }) {
1094
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1095
- "video",
1096
- {
1097
- autoPlay: true,
1098
- muted: true,
1099
- loop: true,
1100
- playsInline: true,
1101
- className: VideoBackground_default.root,
1102
- style: { opacity },
1103
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("source", { src, type: "video/mp4" })
1104
- }
1105
- );
1098
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: VideoBackground_default.root, style: { opacity }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("video", { autoPlay: true, muted: true, loop: true, playsInline: true, className: VideoBackground_default.video, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("source", { src, type: "video/mp4" }) }) });
1106
1099
  }
1107
1100
 
1108
1101
  // src/Hero/Hero.module.css
@@ -1113,6 +1106,7 @@ var import_jsx_runtime15 = require("react/jsx-runtime");
1113
1106
  function Hero({
1114
1107
  variant = "logo",
1115
1108
  heading,
1109
+ label,
1116
1110
  tagline,
1117
1111
  logoSrc = import_frequency_mark_white.default,
1118
1112
  gradient: gradientName = "frequencyAlive",
@@ -1120,71 +1114,144 @@ function Hero({
1120
1114
  primaryAction,
1121
1115
  secondaryAction,
1122
1116
  backgroundVideoSrc,
1123
- showWaveform
1117
+ showWaveform,
1118
+ showScrollIndicator = false
1124
1119
  }) {
1125
- const resolvedHeading = heading != null ? heading : variant === "text" ? "Frequency" : void 0;
1120
+ const [mounted, setMounted] = (0, import_react10.useState)(false);
1121
+ const resolvedHeading = heading != null ? heading : variant === "text" ? "Your Message" : void 0;
1122
+ const resolvedLabel = label || (variant === "logo" ? "Welcome" : void 0);
1126
1123
  const hasActions = primaryAction || secondaryAction;
1127
1124
  const waveformVisible = showWaveform != null ? showWaveform : true;
1128
1125
  const grad = import_colors.gradients[gradientName];
1129
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_core13.Box, { className: Hero_default.root, children: [
1126
+ (0, import_react10.useEffect)(() => {
1127
+ setMounted(true);
1128
+ }, []);
1129
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_core13.Box, { component: "section", className: Hero_default.root, children: [
1130
1130
  backgroundVideoSrc && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VideoBackground, { src: backgroundVideoSrc }),
1131
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_core13.Stack, { className: Hero_default.content, align: "center", gap: "lg", children: [
1132
- variant === "logo" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: Hero_default.logoWrapper, children: [
1133
- waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AnimatedWaveform, {}),
1134
- waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.vignette }),
1135
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1136
- import_core13.Image,
1131
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: Hero_default.content, children: [
1132
+ mounted && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1133
+ resolvedLabel && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1134
+ import_framer_motion3.motion.p,
1137
1135
  {
1138
- src: logoSrc,
1139
- alt: resolvedHeading != null ? resolvedHeading : "Frequency",
1140
- className: Hero_default.logo,
1141
- fit: "contain"
1136
+ initial: { opacity: 0 },
1137
+ animate: { opacity: 1 },
1138
+ transition: { duration: 0.8, delay: 0.2 },
1139
+ className: Hero_default.label,
1140
+ children: resolvedLabel
1142
1141
  }
1143
1142
  ),
1144
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.glow })
1145
- ] }),
1146
- variant === "text" && resolvedHeading && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: Hero_default.textWrapper, children: [
1147
- waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AnimatedWaveform, {}),
1148
- waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.vignette }),
1149
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1150
- import_core13.Title,
1143
+ variant === "logo" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1144
+ import_framer_motion3.motion.div,
1151
1145
  {
1152
- order: 1,
1153
- className: `${Hero_default.heading} ${shimmer ? Hero_default.shimmer : ""}`,
1154
- style: {
1155
- backgroundImage: `linear-gradient(135deg, ${grad.from}, ${grad.to}, ${grad.from})`,
1156
- backgroundSize: shimmer ? "200% 100%" : void 0
1157
- },
1158
- children: resolvedHeading
1146
+ initial: { opacity: 0, scale: 0.8 },
1147
+ animate: { opacity: 1, scale: 1 },
1148
+ transition: { duration: 1.2, delay: 0.3, ease: "easeOut" },
1149
+ className: Hero_default.logoWrapper,
1150
+ children: [
1151
+ waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AnimatedWaveform, {}),
1152
+ waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.vignette }),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1154
+ import_framer_motion3.motion.img,
1155
+ {
1156
+ src: logoSrc,
1157
+ alt: resolvedHeading != null ? resolvedHeading : "Frequency",
1158
+ className: Hero_default.logo,
1159
+ animate: { rotate: [0, 0.5, -0.5, 0] },
1160
+ transition: { duration: 8, repeat: Infinity, ease: "easeInOut" }
1161
+ }
1162
+ ),
1163
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.glow })
1164
+ ]
1159
1165
  }
1160
- )
1161
- ] }),
1162
- tagline && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_core13.Text, { className: Hero_default.tagline, children: tagline }),
1163
- hasActions && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_core13.Group, { gap: "md", justify: "center", wrap: "wrap", children: [
1164
- primaryAction && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1165
- import_core13.Button,
1166
+ ),
1167
+ variant === "text" && resolvedHeading && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1168
+ import_framer_motion3.motion.div,
1166
1169
  {
1167
- component: "a",
1168
- href: primaryAction.href,
1169
- size: "lg",
1170
- radius: 4,
1171
- className: Hero_default.primaryButton,
1172
- children: primaryAction.label
1170
+ initial: { opacity: 0, scale: 0.8 },
1171
+ animate: { opacity: 1, scale: 1 },
1172
+ transition: { duration: 1.2, delay: 0.3, ease: "easeOut" },
1173
+ className: Hero_default.textWrapper,
1174
+ children: [
1175
+ waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AnimatedWaveform, {}),
1176
+ waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.vignette }),
1177
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1178
+ import_core13.Title,
1179
+ {
1180
+ order: 1,
1181
+ className: `${Hero_default.heading} ${shimmer ? Hero_default.shimmer : ""}`,
1182
+ style: {
1183
+ backgroundImage: `linear-gradient(135deg, ${grad.from}, ${grad.to}, ${grad.from})`,
1184
+ backgroundSize: shimmer ? "200% 100%" : void 0
1185
+ },
1186
+ children: resolvedHeading
1187
+ }
1188
+ )
1189
+ ]
1173
1190
  }
1174
1191
  ),
1175
- secondaryAction && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1176
- import_core13.Button,
1192
+ tagline && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1193
+ import_framer_motion3.motion.div,
1177
1194
  {
1178
- component: "a",
1179
- href: secondaryAction.href,
1180
- size: "lg",
1181
- radius: 4,
1182
- variant: "outline",
1183
- className: Hero_default.secondaryButton,
1184
- children: secondaryAction.label
1195
+ initial: { opacity: 0, y: 20 },
1196
+ animate: { opacity: 1, y: 0 },
1197
+ transition: { duration: 0.8, delay: 0.7 },
1198
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_core13.Text, { className: Hero_default.tagline, children: tagline })
1199
+ }
1200
+ ),
1201
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1202
+ import_framer_motion3.motion.div,
1203
+ {
1204
+ initial: { opacity: 0, y: 20 },
1205
+ animate: { opacity: 1, y: 0 },
1206
+ transition: { duration: 0.8, delay: 0.9 },
1207
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_core13.Group, { gap: "md", justify: "center", wrap: "wrap", children: [
1208
+ primaryAction && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1209
+ import_core13.Button,
1210
+ {
1211
+ component: "a",
1212
+ href: primaryAction.href,
1213
+ size: "lg",
1214
+ radius: 4,
1215
+ className: Hero_default.primaryButton,
1216
+ children: primaryAction.label
1217
+ }
1218
+ ),
1219
+ secondaryAction && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1220
+ import_core13.Button,
1221
+ {
1222
+ component: "a",
1223
+ href: secondaryAction.href,
1224
+ size: "lg",
1225
+ radius: 4,
1226
+ variant: "outline",
1227
+ className: Hero_default.secondaryButton,
1228
+ children: secondaryAction.label
1229
+ }
1230
+ )
1231
+ ] })
1185
1232
  }
1186
1233
  )
1187
- ] })
1234
+ ] }),
1235
+ mounted && showScrollIndicator && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1236
+ import_framer_motion3.motion.div,
1237
+ {
1238
+ className: Hero_default.scrollIndicator,
1239
+ initial: { opacity: 0 },
1240
+ animate: { opacity: 1 },
1241
+ transition: { duration: 1, delay: 1.4 },
1242
+ children: [
1243
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: Hero_default.scrollLabel, children: "Scroll" }),
1244
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1245
+ import_framer_motion3.motion.div,
1246
+ {
1247
+ className: Hero_default.scrollLine,
1248
+ animate: { scaleY: [1, 1.5, 1] },
1249
+ transition: { duration: 1.5, repeat: Infinity }
1250
+ }
1251
+ )
1252
+ ]
1253
+ }
1254
+ )
1188
1255
  ] })
1189
1256
  ] });
1190
1257
  }
@@ -1218,7 +1285,7 @@ function HintBadge({ variant, children }) {
1218
1285
  }
1219
1286
 
1220
1287
  // src/MiniAudioPlayer/MiniAudioPlayer.tsx
1221
- var import_react10 = require("react");
1288
+ var import_react11 = require("react");
1222
1289
  var import_core15 = require("@mantine/core");
1223
1290
  var import_icons_react3 = require("@tabler/icons-react");
1224
1291
 
@@ -1232,7 +1299,7 @@ var SIZE_CONFIG = {
1232
1299
  small: { height: 32, buttonSize: 28, iconSize: 16, waveHeight: 20, barWidth: 1, barGap: 0 },
1233
1300
  compact: { height: 40, buttonSize: 32, iconSize: 18, waveHeight: 24, barWidth: 2, barGap: 1 }
1234
1301
  };
1235
- var MiniAudioPlayer = (0, import_react10.forwardRef)(
1302
+ var MiniAudioPlayer = (0, import_react11.forwardRef)(
1236
1303
  ({
1237
1304
  audioUrl,
1238
1305
  size = "mini",
@@ -1243,11 +1310,11 @@ var MiniAudioPlayer = (0, import_react10.forwardRef)(
1243
1310
  className,
1244
1311
  style
1245
1312
  }, ref) => {
1246
- const audioRef = (0, import_react10.useRef)(null);
1247
- const [isPlaying, setIsPlaying] = (0, import_react10.useState)(false);
1248
- const [currentTime, setCurrentTime] = (0, import_react10.useState)(0);
1313
+ const audioRef = (0, import_react11.useRef)(null);
1314
+ const [isPlaying, setIsPlaying] = (0, import_react11.useState)(false);
1315
+ const [currentTime, setCurrentTime] = (0, import_react11.useState)(0);
1249
1316
  const config = SIZE_CONFIG[size];
1250
- (0, import_react10.useEffect)(() => {
1317
+ (0, import_react11.useEffect)(() => {
1251
1318
  const audio = audioRef.current;
1252
1319
  if (!audio) return;
1253
1320
  const handleTimeUpdate = () => setCurrentTime(audio.currentTime);
@@ -1268,7 +1335,7 @@ var MiniAudioPlayer = (0, import_react10.forwardRef)(
1268
1335
  audio.removeEventListener("play", handlePlay);
1269
1336
  };
1270
1337
  }, [audioUrl]);
1271
- const handlePlayPause = (0, import_react10.useCallback)(() => {
1338
+ const handlePlayPause = (0, import_react11.useCallback)(() => {
1272
1339
  const audio = audioRef.current;
1273
1340
  if (!audio) return;
1274
1341
  if (isPlaying) {
@@ -1280,7 +1347,7 @@ var MiniAudioPlayer = (0, import_react10.forwardRef)(
1280
1347
  onPlay == null ? void 0 : onPlay();
1281
1348
  }
1282
1349
  }, [isPlaying, onPlay, onPause]);
1283
- const handleSeek = (0, import_react10.useCallback)((time) => {
1350
+ const handleSeek = (0, import_react11.useCallback)((time) => {
1284
1351
  const audio = audioRef.current;
1285
1352
  if (audio && !isNaN(time) && isFinite(time)) {
1286
1353
  audio.currentTime = time;
@@ -1365,7 +1432,7 @@ function PrincipleCard({
1365
1432
  }
1366
1433
 
1367
1434
  // src/SpeedDial/SpeedDial.tsx
1368
- var import_react11 = require("react");
1435
+ var import_react12 = require("react");
1369
1436
  var import_core17 = require("@mantine/core");
1370
1437
  var import_icons_react4 = require("@tabler/icons-react");
1371
1438
 
@@ -1388,14 +1455,14 @@ function SpeedDial({
1388
1455
  className
1389
1456
  }) {
1390
1457
  var _a;
1391
- const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react11.useState)(defaultOpen);
1458
+ const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react12.useState)(defaultOpen);
1392
1459
  const isOpen = controlledOpen != null ? controlledOpen : uncontrolledOpen;
1393
- const toggle = (0, import_react11.useCallback)(() => {
1460
+ const toggle = (0, import_react12.useCallback)(() => {
1394
1461
  const next = !isOpen;
1395
1462
  setUncontrolledOpen(next);
1396
1463
  onOpenChange == null ? void 0 : onOpenChange(next);
1397
1464
  }, [isOpen, onOpenChange]);
1398
- const close = (0, import_react11.useCallback)(() => {
1465
+ const close = (0, import_react12.useCallback)(() => {
1399
1466
  setUncontrolledOpen(false);
1400
1467
  onOpenChange == null ? void 0 : onOpenChange(false);
1401
1468
  }, [onOpenChange]);
@@ -1537,6 +1604,40 @@ function SplitSection({
1537
1604
  }
1538
1605
  );
1539
1606
  }
1607
+
1608
+ // src/TestimonialCard/TestimonialCard.tsx
1609
+ var import_core19 = require("@mantine/core");
1610
+ var import_icons_react5 = require("@tabler/icons-react");
1611
+
1612
+ // src/TestimonialCard/TestimonialCard.module.css
1613
+ var TestimonialCard_default = {};
1614
+
1615
+ // src/TestimonialCard/TestimonialCard.tsx
1616
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1617
+ function getInitials(name) {
1618
+ return name.split(" ").map((part) => part[0]).join("").toUpperCase().slice(0, 2);
1619
+ }
1620
+ function TestimonialCard({
1621
+ quote,
1622
+ name,
1623
+ role,
1624
+ company,
1625
+ avatarSrc,
1626
+ accentColor = "blue"
1627
+ }) {
1628
+ const borderColor = `var(--mantine-color-${accentColor}-filled)`;
1629
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_core19.Paper, { className: TestimonialCard_default.root, p: "lg", radius: "md", style: { borderLeftColor: borderColor }, children: [
1630
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_icons_react5.IconQuote, { size: 32, className: TestimonialCard_default.quoteIcon, color: borderColor }),
1631
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_core19.Text, { fs: "italic", mt: "xs", mb: "md", children: quote }),
1632
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_core19.Group, { gap: "sm", children: [
1633
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_core19.Avatar, { src: avatarSrc, alt: name, color: accentColor, radius: "xl", size: "md", children: getInitials(name) }),
1634
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_core19.Stack, { gap: 0, children: [
1635
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_core19.Text, { fw: 600, size: "sm", children: name }),
1636
+ (role || company) && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_core19.Text, { size: "xs", c: "dimmed", children: [role, company].filter(Boolean).join(", ") })
1637
+ ] })
1638
+ ] })
1639
+ ] });
1640
+ }
1540
1641
  // Annotate the CommonJS export names for ESM import in node:
1541
1642
  0 && (module.exports = {
1542
1643
  AnimatedCounter,
@@ -1558,5 +1659,6 @@ function SplitSection({
1558
1659
  PrincipleCard,
1559
1660
  SpeedDial,
1560
1661
  SplitSection,
1662
+ TestimonialCard,
1561
1663
  VideoBackground
1562
1664
  });
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  var __defProp = Object.defineProperty;
2
3
  var __defProps = Object.defineProperties;
3
4
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
@@ -1035,15 +1036,9 @@ function GradientSwatch({ name, colors, description }) {
1035
1036
  }
1036
1037
 
1037
1038
  // src/Hero/Hero.tsx
1038
- import {
1039
- Box as Box6,
1040
- Button as Button2,
1041
- Group as Group5,
1042
- Image,
1043
- Stack as Stack2,
1044
- Text as Text9,
1045
- Title as Title2
1046
- } from "@mantine/core";
1039
+ import { useEffect as useEffect8, useState as useState10 } from "react";
1040
+ import { Box as Box6, Button as Button2, Group as Group5, Text as Text9, Title as Title2 } from "@mantine/core";
1041
+ import { motion as motion3 } from "framer-motion";
1047
1042
  import defaultLogoSrc from "@frequencyads/brand/assets/frequency-mark-white.svg";
1048
1043
  import { gradients } from "@frequencyads/brand/colors";
1049
1044
 
@@ -1053,28 +1048,18 @@ var VideoBackground_default = {};
1053
1048
  // src/VideoBackground/VideoBackground.tsx
1054
1049
  import { jsx as jsx13 } from "react/jsx-runtime";
1055
1050
  function VideoBackground({ src, opacity = 0.3 }) {
1056
- return /* @__PURE__ */ jsx13(
1057
- "video",
1058
- {
1059
- autoPlay: true,
1060
- muted: true,
1061
- loop: true,
1062
- playsInline: true,
1063
- className: VideoBackground_default.root,
1064
- style: { opacity },
1065
- children: /* @__PURE__ */ jsx13("source", { src, type: "video/mp4" })
1066
- }
1067
- );
1051
+ return /* @__PURE__ */ jsx13("div", { className: VideoBackground_default.root, style: { opacity }, children: /* @__PURE__ */ jsx13("video", { autoPlay: true, muted: true, loop: true, playsInline: true, className: VideoBackground_default.video, children: /* @__PURE__ */ jsx13("source", { src, type: "video/mp4" }) }) });
1068
1052
  }
1069
1053
 
1070
1054
  // src/Hero/Hero.module.css
1071
1055
  var Hero_default = {};
1072
1056
 
1073
1057
  // src/Hero/Hero.tsx
1074
- import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1058
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1075
1059
  function Hero({
1076
1060
  variant = "logo",
1077
1061
  heading,
1062
+ label,
1078
1063
  tagline,
1079
1064
  logoSrc = defaultLogoSrc,
1080
1065
  gradient: gradientName = "frequencyAlive",
@@ -1082,71 +1067,144 @@ function Hero({
1082
1067
  primaryAction,
1083
1068
  secondaryAction,
1084
1069
  backgroundVideoSrc,
1085
- showWaveform
1070
+ showWaveform,
1071
+ showScrollIndicator = false
1086
1072
  }) {
1087
- const resolvedHeading = heading != null ? heading : variant === "text" ? "Frequency" : void 0;
1073
+ const [mounted, setMounted] = useState10(false);
1074
+ const resolvedHeading = heading != null ? heading : variant === "text" ? "Your Message" : void 0;
1075
+ const resolvedLabel = label || (variant === "logo" ? "Welcome" : void 0);
1088
1076
  const hasActions = primaryAction || secondaryAction;
1089
1077
  const waveformVisible = showWaveform != null ? showWaveform : true;
1090
1078
  const grad = gradients[gradientName];
1091
- return /* @__PURE__ */ jsxs12(Box6, { className: Hero_default.root, children: [
1079
+ useEffect8(() => {
1080
+ setMounted(true);
1081
+ }, []);
1082
+ return /* @__PURE__ */ jsxs12(Box6, { component: "section", className: Hero_default.root, children: [
1092
1083
  backgroundVideoSrc && /* @__PURE__ */ jsx14(VideoBackground, { src: backgroundVideoSrc }),
1093
- /* @__PURE__ */ jsxs12(Stack2, { className: Hero_default.content, align: "center", gap: "lg", children: [
1094
- variant === "logo" && /* @__PURE__ */ jsxs12("div", { className: Hero_default.logoWrapper, children: [
1095
- waveformVisible && /* @__PURE__ */ jsx14(AnimatedWaveform, {}),
1096
- waveformVisible && /* @__PURE__ */ jsx14("div", { className: Hero_default.vignette }),
1097
- /* @__PURE__ */ jsx14(
1098
- Image,
1084
+ /* @__PURE__ */ jsxs12("div", { className: Hero_default.content, children: [
1085
+ mounted && /* @__PURE__ */ jsxs12(Fragment2, { children: [
1086
+ resolvedLabel && /* @__PURE__ */ jsx14(
1087
+ motion3.p,
1099
1088
  {
1100
- src: logoSrc,
1101
- alt: resolvedHeading != null ? resolvedHeading : "Frequency",
1102
- className: Hero_default.logo,
1103
- fit: "contain"
1089
+ initial: { opacity: 0 },
1090
+ animate: { opacity: 1 },
1091
+ transition: { duration: 0.8, delay: 0.2 },
1092
+ className: Hero_default.label,
1093
+ children: resolvedLabel
1104
1094
  }
1105
1095
  ),
1106
- /* @__PURE__ */ jsx14("div", { className: Hero_default.glow })
1107
- ] }),
1108
- variant === "text" && resolvedHeading && /* @__PURE__ */ jsxs12("div", { className: Hero_default.textWrapper, children: [
1109
- waveformVisible && /* @__PURE__ */ jsx14(AnimatedWaveform, {}),
1110
- waveformVisible && /* @__PURE__ */ jsx14("div", { className: Hero_default.vignette }),
1111
- /* @__PURE__ */ jsx14(
1112
- Title2,
1096
+ variant === "logo" && /* @__PURE__ */ jsxs12(
1097
+ motion3.div,
1113
1098
  {
1114
- order: 1,
1115
- className: `${Hero_default.heading} ${shimmer ? Hero_default.shimmer : ""}`,
1116
- style: {
1117
- backgroundImage: `linear-gradient(135deg, ${grad.from}, ${grad.to}, ${grad.from})`,
1118
- backgroundSize: shimmer ? "200% 100%" : void 0
1119
- },
1120
- children: resolvedHeading
1099
+ initial: { opacity: 0, scale: 0.8 },
1100
+ animate: { opacity: 1, scale: 1 },
1101
+ transition: { duration: 1.2, delay: 0.3, ease: "easeOut" },
1102
+ className: Hero_default.logoWrapper,
1103
+ children: [
1104
+ waveformVisible && /* @__PURE__ */ jsx14(AnimatedWaveform, {}),
1105
+ waveformVisible && /* @__PURE__ */ jsx14("div", { className: Hero_default.vignette }),
1106
+ /* @__PURE__ */ jsx14(
1107
+ motion3.img,
1108
+ {
1109
+ src: logoSrc,
1110
+ alt: resolvedHeading != null ? resolvedHeading : "Frequency",
1111
+ className: Hero_default.logo,
1112
+ animate: { rotate: [0, 0.5, -0.5, 0] },
1113
+ transition: { duration: 8, repeat: Infinity, ease: "easeInOut" }
1114
+ }
1115
+ ),
1116
+ /* @__PURE__ */ jsx14("div", { className: Hero_default.glow })
1117
+ ]
1121
1118
  }
1122
- )
1123
- ] }),
1124
- tagline && /* @__PURE__ */ jsx14(Text9, { className: Hero_default.tagline, children: tagline }),
1125
- hasActions && /* @__PURE__ */ jsxs12(Group5, { gap: "md", justify: "center", wrap: "wrap", children: [
1126
- primaryAction && /* @__PURE__ */ jsx14(
1127
- Button2,
1119
+ ),
1120
+ variant === "text" && resolvedHeading && /* @__PURE__ */ jsxs12(
1121
+ motion3.div,
1128
1122
  {
1129
- component: "a",
1130
- href: primaryAction.href,
1131
- size: "lg",
1132
- radius: 4,
1133
- className: Hero_default.primaryButton,
1134
- children: primaryAction.label
1123
+ initial: { opacity: 0, scale: 0.8 },
1124
+ animate: { opacity: 1, scale: 1 },
1125
+ transition: { duration: 1.2, delay: 0.3, ease: "easeOut" },
1126
+ className: Hero_default.textWrapper,
1127
+ children: [
1128
+ waveformVisible && /* @__PURE__ */ jsx14(AnimatedWaveform, {}),
1129
+ waveformVisible && /* @__PURE__ */ jsx14("div", { className: Hero_default.vignette }),
1130
+ /* @__PURE__ */ jsx14(
1131
+ Title2,
1132
+ {
1133
+ order: 1,
1134
+ className: `${Hero_default.heading} ${shimmer ? Hero_default.shimmer : ""}`,
1135
+ style: {
1136
+ backgroundImage: `linear-gradient(135deg, ${grad.from}, ${grad.to}, ${grad.from})`,
1137
+ backgroundSize: shimmer ? "200% 100%" : void 0
1138
+ },
1139
+ children: resolvedHeading
1140
+ }
1141
+ )
1142
+ ]
1135
1143
  }
1136
1144
  ),
1137
- secondaryAction && /* @__PURE__ */ jsx14(
1138
- Button2,
1145
+ tagline && /* @__PURE__ */ jsx14(
1146
+ motion3.div,
1139
1147
  {
1140
- component: "a",
1141
- href: secondaryAction.href,
1142
- size: "lg",
1143
- radius: 4,
1144
- variant: "outline",
1145
- className: Hero_default.secondaryButton,
1146
- children: secondaryAction.label
1148
+ initial: { opacity: 0, y: 20 },
1149
+ animate: { opacity: 1, y: 0 },
1150
+ transition: { duration: 0.8, delay: 0.7 },
1151
+ children: /* @__PURE__ */ jsx14(Text9, { className: Hero_default.tagline, children: tagline })
1152
+ }
1153
+ ),
1154
+ hasActions && /* @__PURE__ */ jsx14(
1155
+ motion3.div,
1156
+ {
1157
+ initial: { opacity: 0, y: 20 },
1158
+ animate: { opacity: 1, y: 0 },
1159
+ transition: { duration: 0.8, delay: 0.9 },
1160
+ children: /* @__PURE__ */ jsxs12(Group5, { gap: "md", justify: "center", wrap: "wrap", children: [
1161
+ primaryAction && /* @__PURE__ */ jsx14(
1162
+ Button2,
1163
+ {
1164
+ component: "a",
1165
+ href: primaryAction.href,
1166
+ size: "lg",
1167
+ radius: 4,
1168
+ className: Hero_default.primaryButton,
1169
+ children: primaryAction.label
1170
+ }
1171
+ ),
1172
+ secondaryAction && /* @__PURE__ */ jsx14(
1173
+ Button2,
1174
+ {
1175
+ component: "a",
1176
+ href: secondaryAction.href,
1177
+ size: "lg",
1178
+ radius: 4,
1179
+ variant: "outline",
1180
+ className: Hero_default.secondaryButton,
1181
+ children: secondaryAction.label
1182
+ }
1183
+ )
1184
+ ] })
1147
1185
  }
1148
1186
  )
1149
- ] })
1187
+ ] }),
1188
+ mounted && showScrollIndicator && /* @__PURE__ */ jsxs12(
1189
+ motion3.div,
1190
+ {
1191
+ className: Hero_default.scrollIndicator,
1192
+ initial: { opacity: 0 },
1193
+ animate: { opacity: 1 },
1194
+ transition: { duration: 1, delay: 1.4 },
1195
+ children: [
1196
+ /* @__PURE__ */ jsx14("span", { className: Hero_default.scrollLabel, children: "Scroll" }),
1197
+ /* @__PURE__ */ jsx14(
1198
+ motion3.div,
1199
+ {
1200
+ className: Hero_default.scrollLine,
1201
+ animate: { scaleY: [1, 1.5, 1] },
1202
+ transition: { duration: 1.5, repeat: Infinity }
1203
+ }
1204
+ )
1205
+ ]
1206
+ }
1207
+ )
1150
1208
  ] })
1151
1209
  ] });
1152
1210
  }
@@ -1180,7 +1238,7 @@ function HintBadge({ variant, children }) {
1180
1238
  }
1181
1239
 
1182
1240
  // src/MiniAudioPlayer/MiniAudioPlayer.tsx
1183
- import { useRef as useRef7, useState as useState10, useEffect as useEffect8, forwardRef as forwardRef2, useCallback as useCallback6 } from "react";
1241
+ import { useRef as useRef7, useState as useState11, useEffect as useEffect9, forwardRef as forwardRef2, useCallback as useCallback6 } from "react";
1184
1242
  import { ActionIcon as ActionIcon2, Group as Group6, Box as Box7 } from "@mantine/core";
1185
1243
  import { IconPlayerPlayFilled as IconPlayerPlayFilled2, IconPlayerPauseFilled as IconPlayerPauseFilled2 } from "@tabler/icons-react";
1186
1244
 
@@ -1206,10 +1264,10 @@ var MiniAudioPlayer = forwardRef2(
1206
1264
  style
1207
1265
  }, ref) => {
1208
1266
  const audioRef = useRef7(null);
1209
- const [isPlaying, setIsPlaying] = useState10(false);
1210
- const [currentTime, setCurrentTime] = useState10(0);
1267
+ const [isPlaying, setIsPlaying] = useState11(false);
1268
+ const [currentTime, setCurrentTime] = useState11(0);
1211
1269
  const config = SIZE_CONFIG[size];
1212
- useEffect8(() => {
1270
+ useEffect9(() => {
1213
1271
  const audio = audioRef.current;
1214
1272
  if (!audio) return;
1215
1273
  const handleTimeUpdate = () => setCurrentTime(audio.currentTime);
@@ -1327,7 +1385,7 @@ function PrincipleCard({
1327
1385
  }
1328
1386
 
1329
1387
  // src/SpeedDial/SpeedDial.tsx
1330
- import { useState as useState11, useCallback as useCallback7 } from "react";
1388
+ import { useState as useState12, useCallback as useCallback7 } from "react";
1331
1389
  import { ActionIcon as ActionIcon3, Transition, Tooltip, Box as Box8 } from "@mantine/core";
1332
1390
  import { IconPlus } from "@tabler/icons-react";
1333
1391
 
@@ -1350,7 +1408,7 @@ function SpeedDial({
1350
1408
  className
1351
1409
  }) {
1352
1410
  var _a;
1353
- const [uncontrolledOpen, setUncontrolledOpen] = useState11(defaultOpen);
1411
+ const [uncontrolledOpen, setUncontrolledOpen] = useState12(defaultOpen);
1354
1412
  const isOpen = controlledOpen != null ? controlledOpen : uncontrolledOpen;
1355
1413
  const toggle = useCallback7(() => {
1356
1414
  const next = !isOpen;
@@ -1425,7 +1483,7 @@ import {
1425
1483
  Badge as Badge2,
1426
1484
  Text as Text11,
1427
1485
  Title as Title4,
1428
- Stack as Stack3
1486
+ Stack as Stack2
1429
1487
  } from "@mantine/core";
1430
1488
 
1431
1489
  // src/SplitSection/SplitSection.module.css
@@ -1450,7 +1508,7 @@ function SplitSection({
1450
1508
  id,
1451
1509
  className
1452
1510
  }) {
1453
- const headingContent = heading != null ? heading : /* @__PURE__ */ jsxs17(Stack3, { gap: "sm", children: [
1511
+ const headingContent = heading != null ? heading : /* @__PURE__ */ jsxs17(Stack2, { gap: "sm", children: [
1454
1512
  badge && /* @__PURE__ */ jsxs17(Box9, { children: [
1455
1513
  /* @__PURE__ */ jsx18(
1456
1514
  Badge2,
@@ -1505,6 +1563,40 @@ function SplitSection({
1505
1563
  }
1506
1564
  );
1507
1565
  }
1566
+
1567
+ // src/TestimonialCard/TestimonialCard.tsx
1568
+ import { Paper as Paper6, Text as Text12, Group as Group8, Avatar, Stack as Stack3 } from "@mantine/core";
1569
+ import { IconQuote } from "@tabler/icons-react";
1570
+
1571
+ // src/TestimonialCard/TestimonialCard.module.css
1572
+ var TestimonialCard_default = {};
1573
+
1574
+ // src/TestimonialCard/TestimonialCard.tsx
1575
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
1576
+ function getInitials(name) {
1577
+ return name.split(" ").map((part) => part[0]).join("").toUpperCase().slice(0, 2);
1578
+ }
1579
+ function TestimonialCard({
1580
+ quote,
1581
+ name,
1582
+ role,
1583
+ company,
1584
+ avatarSrc,
1585
+ accentColor = "blue"
1586
+ }) {
1587
+ const borderColor = `var(--mantine-color-${accentColor}-filled)`;
1588
+ return /* @__PURE__ */ jsxs18(Paper6, { className: TestimonialCard_default.root, p: "lg", radius: "md", style: { borderLeftColor: borderColor }, children: [
1589
+ /* @__PURE__ */ jsx19(IconQuote, { size: 32, className: TestimonialCard_default.quoteIcon, color: borderColor }),
1590
+ /* @__PURE__ */ jsx19(Text12, { fs: "italic", mt: "xs", mb: "md", children: quote }),
1591
+ /* @__PURE__ */ jsxs18(Group8, { gap: "sm", children: [
1592
+ /* @__PURE__ */ jsx19(Avatar, { src: avatarSrc, alt: name, color: accentColor, radius: "xl", size: "md", children: getInitials(name) }),
1593
+ /* @__PURE__ */ jsxs18(Stack3, { gap: 0, children: [
1594
+ /* @__PURE__ */ jsx19(Text12, { fw: 600, size: "sm", children: name }),
1595
+ (role || company) && /* @__PURE__ */ jsx19(Text12, { size: "xs", c: "dimmed", children: [role, company].filter(Boolean).join(", ") })
1596
+ ] })
1597
+ ] })
1598
+ ] });
1599
+ }
1508
1600
  export {
1509
1601
  AnimatedCounter,
1510
1602
  AnimatedWaveform,
@@ -1525,5 +1617,6 @@ export {
1525
1617
  PrincipleCard,
1526
1618
  SpeedDial,
1527
1619
  SplitSection,
1620
+ TestimonialCard,
1528
1621
  VideoBackground
1529
1622
  };
package/llms.txt CHANGED
@@ -90,6 +90,15 @@ import { PrincipleCard } from '@frequencyads/components';
90
90
  ```
91
91
  Props: accentColor, title, description, doHint, dontHint
92
92
 
93
+ ### TestimonialCard
94
+ Quote card with attribution, avatar, and accent color. Displays customer testimonials or endorsements.
95
+ ```tsx
96
+ import { TestimonialCard } from '@frequencyads/components';
97
+ <TestimonialCard quote="Frequency transformed our workflow." name="Sarah Chen" role="Head of Audio" company="Podcast Network Inc." accentColor="blue" />
98
+ <TestimonialCard quote="Simply the best." name="Alex Kim" avatarSrc="/avatars/alex.jpg" accentColor="violet" />
99
+ ```
100
+ Props: quote, name, role, company, avatarSrc, accentColor (MantineColor, default 'blue')
101
+
93
102
  ### DosDonts
94
103
  Side-by-side Do/Don't comparison cards with green/red coloring and check/X icons.
95
104
  ```tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frequencyads/components",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Shared Mantine UI components for Frequency apps",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -14,8 +14,8 @@
14
14
  }
15
15
  },
16
16
  "scripts": {
17
- "build": "tsup src/index.ts --format cjs,esm --dts",
18
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
17
+ "build": "tsup",
18
+ "dev": "tsup --watch",
19
19
  "lint": "eslint .",
20
20
  "type-check": "tsc --noEmit"
21
21
  },