@frequencyads/components 0.1.1 → 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
@@ -79,6 +79,7 @@ __export(index_exports, {
79
79
  PrincipleCard: () => PrincipleCard,
80
80
  SpeedDial: () => SpeedDial,
81
81
  SplitSection: () => SplitSection,
82
+ TestimonialCard: () => TestimonialCard,
82
83
  VideoBackground: () => VideoBackground
83
84
  });
84
85
  module.exports = __toCommonJS(index_exports);
@@ -1082,7 +1083,9 @@ function GradientSwatch({ name, colors, description }) {
1082
1083
  }
1083
1084
 
1084
1085
  // src/Hero/Hero.tsx
1086
+ var import_react10 = require("react");
1085
1087
  var import_core13 = require("@mantine/core");
1088
+ var import_framer_motion3 = require("framer-motion");
1086
1089
  var import_frequency_mark_white = __toESM(require("@frequencyads/brand/assets/frequency-mark-white.svg"));
1087
1090
  var import_colors = require("@frequencyads/brand/colors");
1088
1091
 
@@ -1092,18 +1095,7 @@ var VideoBackground_default = {};
1092
1095
  // src/VideoBackground/VideoBackground.tsx
1093
1096
  var import_jsx_runtime14 = require("react/jsx-runtime");
1094
1097
  function VideoBackground({ src, opacity = 0.3 }) {
1095
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1096
- "video",
1097
- {
1098
- autoPlay: true,
1099
- muted: true,
1100
- loop: true,
1101
- playsInline: true,
1102
- className: VideoBackground_default.root,
1103
- style: { opacity },
1104
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("source", { src, type: "video/mp4" })
1105
- }
1106
- );
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" }) }) });
1107
1099
  }
1108
1100
 
1109
1101
  // src/Hero/Hero.module.css
@@ -1114,6 +1106,7 @@ var import_jsx_runtime15 = require("react/jsx-runtime");
1114
1106
  function Hero({
1115
1107
  variant = "logo",
1116
1108
  heading,
1109
+ label,
1117
1110
  tagline,
1118
1111
  logoSrc = import_frequency_mark_white.default,
1119
1112
  gradient: gradientName = "frequencyAlive",
@@ -1121,71 +1114,144 @@ function Hero({
1121
1114
  primaryAction,
1122
1115
  secondaryAction,
1123
1116
  backgroundVideoSrc,
1124
- showWaveform
1117
+ showWaveform,
1118
+ showScrollIndicator = false
1125
1119
  }) {
1126
- 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);
1127
1123
  const hasActions = primaryAction || secondaryAction;
1128
1124
  const waveformVisible = showWaveform != null ? showWaveform : true;
1129
1125
  const grad = import_colors.gradients[gradientName];
1130
- 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: [
1131
1130
  backgroundVideoSrc && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(VideoBackground, { src: backgroundVideoSrc }),
1132
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_core13.Stack, { className: Hero_default.content, align: "center", gap: "lg", children: [
1133
- variant === "logo" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: Hero_default.logoWrapper, children: [
1134
- waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AnimatedWaveform, {}),
1135
- waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.vignette }),
1136
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1137
- 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,
1138
1135
  {
1139
- src: logoSrc,
1140
- alt: resolvedHeading != null ? resolvedHeading : "Frequency",
1141
- className: Hero_default.logo,
1142
- 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
1143
1141
  }
1144
1142
  ),
1145
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.glow })
1146
- ] }),
1147
- variant === "text" && resolvedHeading && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: Hero_default.textWrapper, children: [
1148
- waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AnimatedWaveform, {}),
1149
- waveformVisible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: Hero_default.vignette }),
1150
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1151
- import_core13.Title,
1143
+ variant === "logo" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1144
+ import_framer_motion3.motion.div,
1152
1145
  {
1153
- order: 1,
1154
- className: `${Hero_default.heading} ${shimmer ? Hero_default.shimmer : ""}`,
1155
- style: {
1156
- backgroundImage: `linear-gradient(135deg, ${grad.from}, ${grad.to}, ${grad.from})`,
1157
- backgroundSize: shimmer ? "200% 100%" : void 0
1158
- },
1159
- 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
+ ]
1160
1165
  }
1161
- )
1162
- ] }),
1163
- tagline && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_core13.Text, { className: Hero_default.tagline, children: tagline }),
1164
- hasActions && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_core13.Group, { gap: "md", justify: "center", wrap: "wrap", children: [
1165
- primaryAction && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1166
- import_core13.Button,
1166
+ ),
1167
+ variant === "text" && resolvedHeading && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1168
+ import_framer_motion3.motion.div,
1167
1169
  {
1168
- component: "a",
1169
- href: primaryAction.href,
1170
- size: "lg",
1171
- radius: 4,
1172
- className: Hero_default.primaryButton,
1173
- 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
+ ]
1174
1190
  }
1175
1191
  ),
1176
- secondaryAction && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1177
- import_core13.Button,
1192
+ tagline && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1193
+ import_framer_motion3.motion.div,
1178
1194
  {
1179
- component: "a",
1180
- href: secondaryAction.href,
1181
- size: "lg",
1182
- radius: 4,
1183
- variant: "outline",
1184
- className: Hero_default.secondaryButton,
1185
- 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
+ ] })
1186
1232
  }
1187
1233
  )
1188
- ] })
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
+ )
1189
1255
  ] })
1190
1256
  ] });
1191
1257
  }
@@ -1219,7 +1285,7 @@ function HintBadge({ variant, children }) {
1219
1285
  }
1220
1286
 
1221
1287
  // src/MiniAudioPlayer/MiniAudioPlayer.tsx
1222
- var import_react10 = require("react");
1288
+ var import_react11 = require("react");
1223
1289
  var import_core15 = require("@mantine/core");
1224
1290
  var import_icons_react3 = require("@tabler/icons-react");
1225
1291
 
@@ -1233,7 +1299,7 @@ var SIZE_CONFIG = {
1233
1299
  small: { height: 32, buttonSize: 28, iconSize: 16, waveHeight: 20, barWidth: 1, barGap: 0 },
1234
1300
  compact: { height: 40, buttonSize: 32, iconSize: 18, waveHeight: 24, barWidth: 2, barGap: 1 }
1235
1301
  };
1236
- var MiniAudioPlayer = (0, import_react10.forwardRef)(
1302
+ var MiniAudioPlayer = (0, import_react11.forwardRef)(
1237
1303
  ({
1238
1304
  audioUrl,
1239
1305
  size = "mini",
@@ -1244,11 +1310,11 @@ var MiniAudioPlayer = (0, import_react10.forwardRef)(
1244
1310
  className,
1245
1311
  style
1246
1312
  }, ref) => {
1247
- const audioRef = (0, import_react10.useRef)(null);
1248
- const [isPlaying, setIsPlaying] = (0, import_react10.useState)(false);
1249
- 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);
1250
1316
  const config = SIZE_CONFIG[size];
1251
- (0, import_react10.useEffect)(() => {
1317
+ (0, import_react11.useEffect)(() => {
1252
1318
  const audio = audioRef.current;
1253
1319
  if (!audio) return;
1254
1320
  const handleTimeUpdate = () => setCurrentTime(audio.currentTime);
@@ -1269,7 +1335,7 @@ var MiniAudioPlayer = (0, import_react10.forwardRef)(
1269
1335
  audio.removeEventListener("play", handlePlay);
1270
1336
  };
1271
1337
  }, [audioUrl]);
1272
- const handlePlayPause = (0, import_react10.useCallback)(() => {
1338
+ const handlePlayPause = (0, import_react11.useCallback)(() => {
1273
1339
  const audio = audioRef.current;
1274
1340
  if (!audio) return;
1275
1341
  if (isPlaying) {
@@ -1281,7 +1347,7 @@ var MiniAudioPlayer = (0, import_react10.forwardRef)(
1281
1347
  onPlay == null ? void 0 : onPlay();
1282
1348
  }
1283
1349
  }, [isPlaying, onPlay, onPause]);
1284
- const handleSeek = (0, import_react10.useCallback)((time) => {
1350
+ const handleSeek = (0, import_react11.useCallback)((time) => {
1285
1351
  const audio = audioRef.current;
1286
1352
  if (audio && !isNaN(time) && isFinite(time)) {
1287
1353
  audio.currentTime = time;
@@ -1366,7 +1432,7 @@ function PrincipleCard({
1366
1432
  }
1367
1433
 
1368
1434
  // src/SpeedDial/SpeedDial.tsx
1369
- var import_react11 = require("react");
1435
+ var import_react12 = require("react");
1370
1436
  var import_core17 = require("@mantine/core");
1371
1437
  var import_icons_react4 = require("@tabler/icons-react");
1372
1438
 
@@ -1389,14 +1455,14 @@ function SpeedDial({
1389
1455
  className
1390
1456
  }) {
1391
1457
  var _a;
1392
- const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react11.useState)(defaultOpen);
1458
+ const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react12.useState)(defaultOpen);
1393
1459
  const isOpen = controlledOpen != null ? controlledOpen : uncontrolledOpen;
1394
- const toggle = (0, import_react11.useCallback)(() => {
1460
+ const toggle = (0, import_react12.useCallback)(() => {
1395
1461
  const next = !isOpen;
1396
1462
  setUncontrolledOpen(next);
1397
1463
  onOpenChange == null ? void 0 : onOpenChange(next);
1398
1464
  }, [isOpen, onOpenChange]);
1399
- const close = (0, import_react11.useCallback)(() => {
1465
+ const close = (0, import_react12.useCallback)(() => {
1400
1466
  setUncontrolledOpen(false);
1401
1467
  onOpenChange == null ? void 0 : onOpenChange(false);
1402
1468
  }, [onOpenChange]);
@@ -1538,6 +1604,40 @@ function SplitSection({
1538
1604
  }
1539
1605
  );
1540
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
+ }
1541
1641
  // Annotate the CommonJS export names for ESM import in node:
1542
1642
  0 && (module.exports = {
1543
1643
  AnimatedCounter,
@@ -1559,5 +1659,6 @@ function SplitSection({
1559
1659
  PrincipleCard,
1560
1660
  SpeedDial,
1561
1661
  SplitSection,
1662
+ TestimonialCard,
1562
1663
  VideoBackground
1563
1664
  });
package/dist/index.mjs CHANGED
@@ -1036,15 +1036,9 @@ function GradientSwatch({ name, colors, description }) {
1036
1036
  }
1037
1037
 
1038
1038
  // src/Hero/Hero.tsx
1039
- import {
1040
- Box as Box6,
1041
- Button as Button2,
1042
- Group as Group5,
1043
- Image,
1044
- Stack as Stack2,
1045
- Text as Text9,
1046
- Title as Title2
1047
- } 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";
1048
1042
  import defaultLogoSrc from "@frequencyads/brand/assets/frequency-mark-white.svg";
1049
1043
  import { gradients } from "@frequencyads/brand/colors";
1050
1044
 
@@ -1054,28 +1048,18 @@ var VideoBackground_default = {};
1054
1048
  // src/VideoBackground/VideoBackground.tsx
1055
1049
  import { jsx as jsx13 } from "react/jsx-runtime";
1056
1050
  function VideoBackground({ src, opacity = 0.3 }) {
1057
- return /* @__PURE__ */ jsx13(
1058
- "video",
1059
- {
1060
- autoPlay: true,
1061
- muted: true,
1062
- loop: true,
1063
- playsInline: true,
1064
- className: VideoBackground_default.root,
1065
- style: { opacity },
1066
- children: /* @__PURE__ */ jsx13("source", { src, type: "video/mp4" })
1067
- }
1068
- );
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" }) }) });
1069
1052
  }
1070
1053
 
1071
1054
  // src/Hero/Hero.module.css
1072
1055
  var Hero_default = {};
1073
1056
 
1074
1057
  // src/Hero/Hero.tsx
1075
- 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";
1076
1059
  function Hero({
1077
1060
  variant = "logo",
1078
1061
  heading,
1062
+ label,
1079
1063
  tagline,
1080
1064
  logoSrc = defaultLogoSrc,
1081
1065
  gradient: gradientName = "frequencyAlive",
@@ -1083,71 +1067,144 @@ function Hero({
1083
1067
  primaryAction,
1084
1068
  secondaryAction,
1085
1069
  backgroundVideoSrc,
1086
- showWaveform
1070
+ showWaveform,
1071
+ showScrollIndicator = false
1087
1072
  }) {
1088
- 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);
1089
1076
  const hasActions = primaryAction || secondaryAction;
1090
1077
  const waveformVisible = showWaveform != null ? showWaveform : true;
1091
1078
  const grad = gradients[gradientName];
1092
- 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: [
1093
1083
  backgroundVideoSrc && /* @__PURE__ */ jsx14(VideoBackground, { src: backgroundVideoSrc }),
1094
- /* @__PURE__ */ jsxs12(Stack2, { className: Hero_default.content, align: "center", gap: "lg", children: [
1095
- variant === "logo" && /* @__PURE__ */ jsxs12("div", { className: Hero_default.logoWrapper, children: [
1096
- waveformVisible && /* @__PURE__ */ jsx14(AnimatedWaveform, {}),
1097
- waveformVisible && /* @__PURE__ */ jsx14("div", { className: Hero_default.vignette }),
1098
- /* @__PURE__ */ jsx14(
1099
- Image,
1084
+ /* @__PURE__ */ jsxs12("div", { className: Hero_default.content, children: [
1085
+ mounted && /* @__PURE__ */ jsxs12(Fragment2, { children: [
1086
+ resolvedLabel && /* @__PURE__ */ jsx14(
1087
+ motion3.p,
1100
1088
  {
1101
- src: logoSrc,
1102
- alt: resolvedHeading != null ? resolvedHeading : "Frequency",
1103
- className: Hero_default.logo,
1104
- 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
1105
1094
  }
1106
1095
  ),
1107
- /* @__PURE__ */ jsx14("div", { className: Hero_default.glow })
1108
- ] }),
1109
- variant === "text" && resolvedHeading && /* @__PURE__ */ jsxs12("div", { className: Hero_default.textWrapper, children: [
1110
- waveformVisible && /* @__PURE__ */ jsx14(AnimatedWaveform, {}),
1111
- waveformVisible && /* @__PURE__ */ jsx14("div", { className: Hero_default.vignette }),
1112
- /* @__PURE__ */ jsx14(
1113
- Title2,
1096
+ variant === "logo" && /* @__PURE__ */ jsxs12(
1097
+ motion3.div,
1114
1098
  {
1115
- order: 1,
1116
- className: `${Hero_default.heading} ${shimmer ? Hero_default.shimmer : ""}`,
1117
- style: {
1118
- backgroundImage: `linear-gradient(135deg, ${grad.from}, ${grad.to}, ${grad.from})`,
1119
- backgroundSize: shimmer ? "200% 100%" : void 0
1120
- },
1121
- 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
+ ]
1122
1118
  }
1123
- )
1124
- ] }),
1125
- tagline && /* @__PURE__ */ jsx14(Text9, { className: Hero_default.tagline, children: tagline }),
1126
- hasActions && /* @__PURE__ */ jsxs12(Group5, { gap: "md", justify: "center", wrap: "wrap", children: [
1127
- primaryAction && /* @__PURE__ */ jsx14(
1128
- Button2,
1119
+ ),
1120
+ variant === "text" && resolvedHeading && /* @__PURE__ */ jsxs12(
1121
+ motion3.div,
1129
1122
  {
1130
- component: "a",
1131
- href: primaryAction.href,
1132
- size: "lg",
1133
- radius: 4,
1134
- className: Hero_default.primaryButton,
1135
- 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
+ ]
1136
1143
  }
1137
1144
  ),
1138
- secondaryAction && /* @__PURE__ */ jsx14(
1139
- Button2,
1145
+ tagline && /* @__PURE__ */ jsx14(
1146
+ motion3.div,
1140
1147
  {
1141
- component: "a",
1142
- href: secondaryAction.href,
1143
- size: "lg",
1144
- radius: 4,
1145
- variant: "outline",
1146
- className: Hero_default.secondaryButton,
1147
- 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
+ ] })
1148
1185
  }
1149
1186
  )
1150
- ] })
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
+ )
1151
1208
  ] })
1152
1209
  ] });
1153
1210
  }
@@ -1181,7 +1238,7 @@ function HintBadge({ variant, children }) {
1181
1238
  }
1182
1239
 
1183
1240
  // src/MiniAudioPlayer/MiniAudioPlayer.tsx
1184
- 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";
1185
1242
  import { ActionIcon as ActionIcon2, Group as Group6, Box as Box7 } from "@mantine/core";
1186
1243
  import { IconPlayerPlayFilled as IconPlayerPlayFilled2, IconPlayerPauseFilled as IconPlayerPauseFilled2 } from "@tabler/icons-react";
1187
1244
 
@@ -1207,10 +1264,10 @@ var MiniAudioPlayer = forwardRef2(
1207
1264
  style
1208
1265
  }, ref) => {
1209
1266
  const audioRef = useRef7(null);
1210
- const [isPlaying, setIsPlaying] = useState10(false);
1211
- const [currentTime, setCurrentTime] = useState10(0);
1267
+ const [isPlaying, setIsPlaying] = useState11(false);
1268
+ const [currentTime, setCurrentTime] = useState11(0);
1212
1269
  const config = SIZE_CONFIG[size];
1213
- useEffect8(() => {
1270
+ useEffect9(() => {
1214
1271
  const audio = audioRef.current;
1215
1272
  if (!audio) return;
1216
1273
  const handleTimeUpdate = () => setCurrentTime(audio.currentTime);
@@ -1328,7 +1385,7 @@ function PrincipleCard({
1328
1385
  }
1329
1386
 
1330
1387
  // src/SpeedDial/SpeedDial.tsx
1331
- import { useState as useState11, useCallback as useCallback7 } from "react";
1388
+ import { useState as useState12, useCallback as useCallback7 } from "react";
1332
1389
  import { ActionIcon as ActionIcon3, Transition, Tooltip, Box as Box8 } from "@mantine/core";
1333
1390
  import { IconPlus } from "@tabler/icons-react";
1334
1391
 
@@ -1351,7 +1408,7 @@ function SpeedDial({
1351
1408
  className
1352
1409
  }) {
1353
1410
  var _a;
1354
- const [uncontrolledOpen, setUncontrolledOpen] = useState11(defaultOpen);
1411
+ const [uncontrolledOpen, setUncontrolledOpen] = useState12(defaultOpen);
1355
1412
  const isOpen = controlledOpen != null ? controlledOpen : uncontrolledOpen;
1356
1413
  const toggle = useCallback7(() => {
1357
1414
  const next = !isOpen;
@@ -1426,7 +1483,7 @@ import {
1426
1483
  Badge as Badge2,
1427
1484
  Text as Text11,
1428
1485
  Title as Title4,
1429
- Stack as Stack3
1486
+ Stack as Stack2
1430
1487
  } from "@mantine/core";
1431
1488
 
1432
1489
  // src/SplitSection/SplitSection.module.css
@@ -1451,7 +1508,7 @@ function SplitSection({
1451
1508
  id,
1452
1509
  className
1453
1510
  }) {
1454
- const headingContent = heading != null ? heading : /* @__PURE__ */ jsxs17(Stack3, { gap: "sm", children: [
1511
+ const headingContent = heading != null ? heading : /* @__PURE__ */ jsxs17(Stack2, { gap: "sm", children: [
1455
1512
  badge && /* @__PURE__ */ jsxs17(Box9, { children: [
1456
1513
  /* @__PURE__ */ jsx18(
1457
1514
  Badge2,
@@ -1506,6 +1563,40 @@ function SplitSection({
1506
1563
  }
1507
1564
  );
1508
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
+ }
1509
1600
  export {
1510
1601
  AnimatedCounter,
1511
1602
  AnimatedWaveform,
@@ -1526,5 +1617,6 @@ export {
1526
1617
  PrincipleCard,
1527
1618
  SpeedDial,
1528
1619
  SplitSection,
1620
+ TestimonialCard,
1529
1621
  VideoBackground
1530
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.1",
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",