@digilogiclabs/saas-factory-ui 1.33.0 → 1.34.0

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.d.mts CHANGED
@@ -3759,8 +3759,16 @@ declare function useEscapeKey(handler: () => void, enabled?: boolean): void;
3759
3759
  * 3. `prefers-color-scheme: dark` media query.
3760
3760
  *
3761
3761
  * Live-updates via a `MutationObserver` watching the root class attribute
3762
- * and a `change` listener on the media query. Safe for SSR — returns
3763
- * `false` when `document` is undefined.
3762
+ * and a `change` listener on the media query.
3763
+ *
3764
+ * SSR-safe by construction: the FIRST render always returns `false` (or the
3765
+ * `forced` value when provided), matching what the server renders. Detection
3766
+ * only runs after mount in `useEffect`, so the initial client render is
3767
+ * byte-identical to SSR — no hydration mismatch warnings. The trade-off is
3768
+ * a brief flash of light-theme markup before the effect commits the real
3769
+ * value; a no-flash app should also set `.dark` on `<html>` via a synchronous
3770
+ * script tag in the document head before React hydrates (next-themes does
3771
+ * this), in which case this hook then picks up that class on mount.
3764
3772
  */
3765
3773
  declare function useDarkMode(forced?: boolean): boolean;
3766
3774
 
@@ -4979,9 +4987,9 @@ interface RandomPlayerPickerProps {
4979
4987
  * Visual intensity preset. Default "normal".
4980
4988
  * - "subtle" / "normal": current behavior — shorter spins, standard
4981
4989
  * ease-out curve.
4982
- * - "dramatic": physics-like spin — 1.5× duration, ease-out-quart
4990
+ * - "dramatic": physics-like spin — 2.5× duration, inertia-style
4983
4991
  * curve so motion continues through the full duration with a slow
4984
- * winding-down tail, +4 extra revolutions, plus more
4992
+ * winding-down tail, +12 extra revolutions, plus more
4985
4993
  * particles/glow/shake on reveal.
4986
4994
  */
4987
4995
  intensity?: RandomPlayerIntensity;
@@ -5066,6 +5074,40 @@ interface HeroBannerCta {
5066
5074
  interface HeroBannerSlide {
5067
5075
  /** Background image URL. */
5068
5076
  image?: string;
5077
+ /**
5078
+ * Render this slide's image as a real `<img>` (with `fetchpriority="high"`,
5079
+ * `loading="eager"`, `decoding="async"`) instead of a CSS `background-image`.
5080
+ *
5081
+ * Set this on the first slide to make it a reliable LCP target. CSS
5082
+ * backgrounds are valid LCP candidates but the browser discovers them
5083
+ * later (after CSS parse), and `<link rel="preload">` connects to real
5084
+ * `<img>` elements far more reliably than to `background-image` URLs.
5085
+ *
5086
+ * Only the slide with `priority: true` swaps to `<img>`; all other slides
5087
+ * stay on CSS `background-image` (cheaper to cycle). The `imageFilter`,
5088
+ * `bgColor`, overlays, and accent layers all still apply.
5089
+ */
5090
+ priority?: boolean;
5091
+ /**
5092
+ * Optional alt text for the priority `<img>`. Defaults to empty (decorative)
5093
+ * — appropriate when the slide already carries semantic text via headline /
5094
+ * kicker.
5095
+ */
5096
+ imageAlt?: string;
5097
+ /**
5098
+ * Optional intrinsic width of the priority image (in pixels). When set with
5099
+ * `imageHeight`, emitted as the `<img>` `width` HTML attribute. Lets the
5100
+ * browser compute aspect ratio for the preload connection without waiting
5101
+ * for image headers. Has no layout effect when the parent already sizes
5102
+ * the image via absolute positioning (the default). Ignored unless
5103
+ * `priority` is true.
5104
+ */
5105
+ imageWidth?: number;
5106
+ /**
5107
+ * Optional intrinsic height of the priority image (in pixels). Companion
5108
+ * to `imageWidth`. See `imageWidth` for usage notes.
5109
+ */
5110
+ imageHeight?: number;
5069
5111
  /** CSS gradient string (e.g. `"linear-gradient(135deg,#0f172a,#0f4c75)"`). */
5070
5112
  gradient?: string;
5071
5113
  /** Fallback solid background color. */
package/dist/index.d.ts CHANGED
@@ -3759,8 +3759,16 @@ declare function useEscapeKey(handler: () => void, enabled?: boolean): void;
3759
3759
  * 3. `prefers-color-scheme: dark` media query.
3760
3760
  *
3761
3761
  * Live-updates via a `MutationObserver` watching the root class attribute
3762
- * and a `change` listener on the media query. Safe for SSR — returns
3763
- * `false` when `document` is undefined.
3762
+ * and a `change` listener on the media query.
3763
+ *
3764
+ * SSR-safe by construction: the FIRST render always returns `false` (or the
3765
+ * `forced` value when provided), matching what the server renders. Detection
3766
+ * only runs after mount in `useEffect`, so the initial client render is
3767
+ * byte-identical to SSR — no hydration mismatch warnings. The trade-off is
3768
+ * a brief flash of light-theme markup before the effect commits the real
3769
+ * value; a no-flash app should also set `.dark` on `<html>` via a synchronous
3770
+ * script tag in the document head before React hydrates (next-themes does
3771
+ * this), in which case this hook then picks up that class on mount.
3764
3772
  */
3765
3773
  declare function useDarkMode(forced?: boolean): boolean;
3766
3774
 
@@ -4979,9 +4987,9 @@ interface RandomPlayerPickerProps {
4979
4987
  * Visual intensity preset. Default "normal".
4980
4988
  * - "subtle" / "normal": current behavior — shorter spins, standard
4981
4989
  * ease-out curve.
4982
- * - "dramatic": physics-like spin — 1.5× duration, ease-out-quart
4990
+ * - "dramatic": physics-like spin — 2.5× duration, inertia-style
4983
4991
  * curve so motion continues through the full duration with a slow
4984
- * winding-down tail, +4 extra revolutions, plus more
4992
+ * winding-down tail, +12 extra revolutions, plus more
4985
4993
  * particles/glow/shake on reveal.
4986
4994
  */
4987
4995
  intensity?: RandomPlayerIntensity;
@@ -5066,6 +5074,40 @@ interface HeroBannerCta {
5066
5074
  interface HeroBannerSlide {
5067
5075
  /** Background image URL. */
5068
5076
  image?: string;
5077
+ /**
5078
+ * Render this slide's image as a real `<img>` (with `fetchpriority="high"`,
5079
+ * `loading="eager"`, `decoding="async"`) instead of a CSS `background-image`.
5080
+ *
5081
+ * Set this on the first slide to make it a reliable LCP target. CSS
5082
+ * backgrounds are valid LCP candidates but the browser discovers them
5083
+ * later (after CSS parse), and `<link rel="preload">` connects to real
5084
+ * `<img>` elements far more reliably than to `background-image` URLs.
5085
+ *
5086
+ * Only the slide with `priority: true` swaps to `<img>`; all other slides
5087
+ * stay on CSS `background-image` (cheaper to cycle). The `imageFilter`,
5088
+ * `bgColor`, overlays, and accent layers all still apply.
5089
+ */
5090
+ priority?: boolean;
5091
+ /**
5092
+ * Optional alt text for the priority `<img>`. Defaults to empty (decorative)
5093
+ * — appropriate when the slide already carries semantic text via headline /
5094
+ * kicker.
5095
+ */
5096
+ imageAlt?: string;
5097
+ /**
5098
+ * Optional intrinsic width of the priority image (in pixels). When set with
5099
+ * `imageHeight`, emitted as the `<img>` `width` HTML attribute. Lets the
5100
+ * browser compute aspect ratio for the preload connection without waiting
5101
+ * for image headers. Has no layout effect when the parent already sizes
5102
+ * the image via absolute positioning (the default). Ignored unless
5103
+ * `priority` is true.
5104
+ */
5105
+ imageWidth?: number;
5106
+ /**
5107
+ * Optional intrinsic height of the priority image (in pixels). Companion
5108
+ * to `imageWidth`. See `imageWidth` for usage notes.
5109
+ */
5110
+ imageHeight?: number;
5069
5111
  /** CSS gradient string (e.g. `"linear-gradient(135deg,#0f172a,#0f4c75)"`). */
5070
5112
  gradient?: string;
5071
5113
  /** Fallback solid background color. */
package/dist/index.js CHANGED
@@ -10,8 +10,8 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
10
10
  var __esm = (fn, res) => function __init() {
11
11
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
12
  };
13
- var __commonJS = (cb, mod) => function __require() {
14
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
+ var __commonJS = (cb, mod2) => function __require() {
14
+ return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
15
15
  };
16
16
  var __export = (target, all) => {
17
17
  for (var name in all)
@@ -25,15 +25,15 @@ var __copyProps = (to, from, except, desc) => {
25
25
  }
26
26
  return to;
27
27
  };
28
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
28
+ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __copyProps(
29
29
  // If the importer is in node compatibility mode or this is not an ESM
30
30
  // file that has been converted to a CommonJS file using a Babel-
31
31
  // compatible transform (i.e. "__esModule" has not been set), then set
32
32
  // "default" to the CommonJS "module.exports" for node compatibility.
33
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
34
- mod
33
+ isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
34
+ mod2
35
35
  ));
36
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
+ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
37
37
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
38
38
 
39
39
  // src/config/constants.ts
@@ -25443,11 +25443,6 @@ var import_react55 = require("react");
25443
25443
  function useDarkMode(forced) {
25444
25444
  const [dark, setDark] = (0, import_react55.useState)(() => {
25445
25445
  if (typeof forced === "boolean") return forced;
25446
- if (typeof document === "undefined") return false;
25447
- if (document.documentElement.classList.contains("dark")) return true;
25448
- if (typeof window !== "undefined" && window.matchMedia) {
25449
- return window.matchMedia("(prefers-color-scheme: dark)").matches;
25450
- }
25451
25446
  return false;
25452
25447
  });
25453
25448
  (0, import_react55.useEffect)(() => {
@@ -31768,8 +31763,7 @@ function BracketGenerator({
31768
31763
  champion: null
31769
31764
  };
31770
31765
  setBracketState(newState);
31771
- onStateChange?.(newState);
31772
- }, [effectiveParticipants, seedingStrategy, onStateChange]);
31766
+ }, [effectiveParticipants, seedingStrategy]);
31773
31767
  const simulateNext = (0, import_react69.useCallback)(() => {
31774
31768
  setBracketState((prev) => {
31775
31769
  const next = prev.matches.find((m) => m.a && m.b && !m.winner);
@@ -31782,13 +31776,9 @@ function BracketGenerator({
31782
31776
  matches = propagateWinner(matches, completed);
31783
31777
  const finalMatch = matches.find((m) => m.round === prev.rounds - 1);
31784
31778
  const champion = finalMatch?.winner || null;
31785
- onMatchComplete?.(completed, completed.winner);
31786
- if (champion && !prev.champion) onChampion?.(champion);
31787
- const newState = { ...prev, matches, champion };
31788
- onStateChange?.(newState);
31789
- return newState;
31779
+ return { ...prev, matches, champion };
31790
31780
  });
31791
- }, [onMatchComplete, onChampion, onStateChange]);
31781
+ }, []);
31792
31782
  const autoPlay = (0, import_react69.useCallback)(() => {
31793
31783
  if (simulating) {
31794
31784
  if (autoRef.current) clearTimeout(autoRef.current);
@@ -31811,17 +31801,13 @@ function BracketGenerator({
31811
31801
  matches = propagateWinner(matches, completed);
31812
31802
  const finalMatch = matches.find((m) => m.round === prev.rounds - 1);
31813
31803
  const champion = finalMatch?.winner || null;
31814
- onMatchComplete?.(completed, completed.winner);
31815
- if (champion && !prev.champion) onChampion?.(champion);
31816
- const newState = { ...prev, matches, champion };
31817
- onStateChange?.(newState);
31818
31804
  if (!champion) autoRef.current = setTimeout(tick, simulateDelayMs);
31819
31805
  else setSimulating(false);
31820
- return newState;
31806
+ return { ...prev, matches, champion };
31821
31807
  });
31822
31808
  };
31823
31809
  tick();
31824
- }, [simulating, simulateDelayMs, onMatchComplete, onChampion, onStateChange]);
31810
+ }, [simulating, simulateDelayMs]);
31825
31811
  const advanceWinner = (0, import_react69.useCallback)(
31826
31812
  (matchId, winnerSlot) => {
31827
31813
  setBracketState((prev) => {
@@ -31840,15 +31826,31 @@ function BracketGenerator({
31840
31826
  matches = propagateWinner(matches, completed);
31841
31827
  const finalMatch = matches.find((x) => x.round === prev.rounds - 1);
31842
31828
  const champion = finalMatch?.winner || null;
31843
- onMatchComplete?.(completed, winner);
31844
- if (champion && !prev.champion) onChampion?.(champion);
31845
- const newState = { ...prev, matches, champion };
31846
- onStateChange?.(newState);
31847
- return newState;
31829
+ return { ...prev, matches, champion };
31848
31830
  });
31849
31831
  },
31850
- [onMatchComplete, onChampion, onStateChange]
31832
+ []
31851
31833
  );
31834
+ const prevBracketStateRef = (0, import_react69.useRef)(bracketState);
31835
+ (0, import_react69.useEffect)(() => {
31836
+ const prev = prevBracketStateRef.current;
31837
+ const next = bracketState;
31838
+ if (prev === next) return;
31839
+ if (onMatchComplete) {
31840
+ const prevById = new Map(prev.matches.map((m) => [m.id, m]));
31841
+ for (const m of next.matches) {
31842
+ const before = prevById.get(m.id);
31843
+ if (m.winner && !before?.winner) {
31844
+ onMatchComplete(m, m.winner);
31845
+ }
31846
+ }
31847
+ }
31848
+ if (onChampion && next.champion && !prev.champion) {
31849
+ onChampion(next.champion);
31850
+ }
31851
+ onStateChange?.(next);
31852
+ prevBracketStateRef.current = next;
31853
+ }, [bracketState, onMatchComplete, onChampion, onStateChange]);
31852
31854
  (0, import_react69.useEffect)(() => {
31853
31855
  seedBracket();
31854
31856
  return () => {
@@ -31940,7 +31942,7 @@ function BracketGenerator({
31940
31942
  height: MATCH_H / 2,
31941
31943
  cursor: hasBoth && !m.winner ? "pointer" : "default",
31942
31944
  opacity: isLoss ? 0.45 : 1,
31943
- textDecoration: isLoss ? "line-through" : "none",
31945
+ textDecorationLine: isLoss ? "line-through" : "none",
31944
31946
  textDecorationColor: colors.faint,
31945
31947
  transition: "background 0.2s, color 0.2s",
31946
31948
  fontStyle: !p ? "italic" : "normal"
@@ -33324,8 +33326,8 @@ var WEDGE_PALETTE = [
33324
33326
  "#14b8a6",
33325
33327
  "#f97316"
33326
33328
  ];
33327
- var DEFAULT_EASING = "cubic-bezier(0.12, 0.78, 0.22, 1)";
33328
- var DRAMATIC_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
33329
+ var DEFAULT_EASING = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
33330
+ var DRAMATIC_EASING = "cubic-bezier(0.39, 0.575, 0.565, 1)";
33329
33331
  var INTENSITY_CONFIG = {
33330
33332
  subtle: {
33331
33333
  particles: 14,
@@ -33350,9 +33352,9 @@ var INTENSITY_CONFIG = {
33350
33352
  shakePeak: 10,
33351
33353
  glowMult: 1.45,
33352
33354
  pulseStroke: 8,
33353
- durationMult: 1.5,
33355
+ durationMult: 2.5,
33354
33356
  easing: DRAMATIC_EASING,
33355
- revsBoost: 4
33357
+ revsBoost: 12
33356
33358
  }
33357
33359
  };
33358
33360
  function polar(cx, cy, r, a) {
@@ -33378,6 +33380,20 @@ function weightedIndex(pool) {
33378
33380
  }
33379
33381
  return pool.length - 1;
33380
33382
  }
33383
+ function mod(value, divisor) {
33384
+ return (value % divisor + divisor) % divisor;
33385
+ }
33386
+ function wheelIndexAtPointer(rotation, count2) {
33387
+ if (count2 < 1) return -1;
33388
+ const wedgeDeg = 360 / count2;
33389
+ const localDegAtPointer = mod(-rotation, 360);
33390
+ return Math.min(count2 - 1, Math.floor(localDegAtPointer / wedgeDeg));
33391
+ }
33392
+ function reelIndexAtCenter(reelY, centerOffsetPx, rowHeight, count2) {
33393
+ if (count2 < 1) return -1;
33394
+ const centerRow = Math.round((reelY + centerOffsetPx) / rowHeight);
33395
+ return mod(centerRow, count2);
33396
+ }
33381
33397
  function ReelStage({
33382
33398
  pool,
33383
33399
  winner,
@@ -33805,7 +33821,9 @@ function RandomPlayerPicker({
33805
33821
  const winnerIdx = weightedIndex(pool);
33806
33822
  const minRev = minRevolutions + intensityCfg.revsBoost;
33807
33823
  const maxRev = maxRevolutions + intensityCfg.revsBoost;
33808
- const revolutions = minRev + Math.random() * (maxRev - minRev);
33824
+ const minFullRevs = Math.ceil(minRev);
33825
+ const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
33826
+ const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
33809
33827
  const wedgeMidRad = winnerIdx * anglePerWedge + anglePerWedge / 2;
33810
33828
  const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
33811
33829
  const wheelWobble = (Math.random() - 0.5) * (anglePerWedge * 180 / Math.PI) * 0.6;
@@ -33819,10 +33837,12 @@ function RandomPlayerPicker({
33819
33837
  const currentCenterRow = Math.round(
33820
33838
  (reelY + centerOffsetPx) / reelRowHeight
33821
33839
  );
33822
- const currentCenterMod = (currentCenterRow % n + n) % n;
33840
+ const currentCenterMod = mod(currentCenterRow, n);
33823
33841
  const targetRowOffset = (winnerIdx - currentCenterMod + n) % n;
33824
33842
  const reelRevRows = Math.round(revolutions * n);
33825
33843
  const finalReelY = reelY + (reelRevRows + targetRowOffset) * reelRowHeight;
33844
+ const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, n) : wheelIndexAtPointer(finalRotation, n);
33845
+ const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < pool.length ? settledWinnerIdx : winnerIdx;
33826
33846
  const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
33827
33847
  setSpinning(true);
33828
33848
  setWinner(null);
@@ -33867,7 +33887,7 @@ function RandomPlayerPicker({
33867
33887
  schedule();
33868
33888
  }
33869
33889
  winnerTimer.current = setTimeout(() => {
33870
- const picked = pool[winnerIdx];
33890
+ const picked = pool[finalWinnerIdx];
33871
33891
  if (!picked) {
33872
33892
  setSpinning(false);
33873
33893
  return;
@@ -33914,7 +33934,7 @@ function RandomPlayerPicker({
33914
33934
  }
33915
33935
  if (removeWinners) {
33916
33936
  popTimer.current = setTimeout(() => {
33917
- setPool((p) => p.filter((_, i) => i !== winnerIdx));
33937
+ setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
33918
33938
  setWinner(null);
33919
33939
  setRotation(0);
33920
33940
  setReelY(0);
@@ -35059,23 +35079,46 @@ function DynamicHeroBanner({
35059
35079
  const filter = IMAGE_FILTERS[slide.imageFilter ?? imageFilter] ?? IMAGE_FILTERS.cinematic;
35060
35080
  const slideOverlayOpacity = slide.overlayOpacity ?? overlayOpacity;
35061
35081
  const slideAlign = slide.textAlign ?? textAlign;
35082
+ const usePriorityImg = slide.priority === true && !!slide.image;
35083
+ const bgDivStyle = usePriorityImg ? {
35084
+ position: "absolute",
35085
+ inset: 0,
35086
+ backgroundColor: slide.bgColor ?? "#0c0e14",
35087
+ transform: isCurrent ? "scale(1)" : "scale(1.06)",
35088
+ transition: reducedMotion ? "none" : `transform ${autoPlayInterval}ms linear`
35089
+ } : {
35090
+ position: "absolute",
35091
+ inset: 0,
35092
+ backgroundImage: slide.image ? `url(${slide.image})` : slide.gradient ?? "none",
35093
+ backgroundColor: slide.bgColor ?? "#0c0e14",
35094
+ backgroundSize: "cover",
35095
+ backgroundPosition: "center",
35096
+ filter,
35097
+ transform: isCurrent ? "scale(1)" : "scale(1.06)",
35098
+ transition: reducedMotion ? "none" : `transform ${autoPlayInterval}ms linear`
35099
+ };
35062
35100
  return /* @__PURE__ */ (0, import_jsx_runtime118.jsxs)("div", { style: bgStyle(i), "aria-hidden": !isCurrent, children: [
35063
- /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(
35064
- "div",
35101
+ /* @__PURE__ */ (0, import_jsx_runtime118.jsx)("div", { style: bgDivStyle, children: usePriorityImg && /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(
35102
+ "img",
35065
35103
  {
35104
+ src: slide.image,
35105
+ alt: slide.imageAlt ?? "",
35106
+ width: slide.imageWidth && slide.imageHeight ? slide.imageWidth : void 0,
35107
+ height: slide.imageWidth && slide.imageHeight ? slide.imageHeight : void 0,
35108
+ fetchPriority: "high",
35109
+ loading: "eager",
35110
+ decoding: "async",
35066
35111
  style: {
35067
35112
  position: "absolute",
35068
35113
  inset: 0,
35069
- backgroundImage: slide.image ? `url(${slide.image})` : slide.gradient ?? "none",
35070
- backgroundColor: slide.bgColor ?? "#0c0e14",
35071
- backgroundSize: "cover",
35072
- backgroundPosition: "center",
35073
- filter,
35074
- transform: isCurrent ? "scale(1)" : "scale(1.06)",
35075
- transition: reducedMotion ? "none" : `transform ${autoPlayInterval}ms linear`
35114
+ width: "100%",
35115
+ height: "100%",
35116
+ objectFit: "cover",
35117
+ objectPosition: "center",
35118
+ filter
35076
35119
  }
35077
35120
  }
35078
- ),
35121
+ ) }),
35079
35122
  /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(
35080
35123
  "div",
35081
35124
  {
@@ -35475,7 +35518,7 @@ function CtaButton({
35475
35518
  background: isPrimary ? accent : isGhost ? "transparent" : "rgba(255,255,255,0.06)",
35476
35519
  color: isPrimary ? "#fff" : "rgba(255,255,255,0.8)",
35477
35520
  cursor: "pointer",
35478
- textDecoration: isGhost ? "underline" : "none",
35521
+ textDecorationLine: isGhost ? "underline" : "none",
35479
35522
  textUnderlineOffset: isGhost ? 4 : void 0,
35480
35523
  letterSpacing: "-0.01em",
35481
35524
  boxShadow: isPrimary ? `0 4px 20px ${glow}33` : "none",