@shohojdhara/atomix 0.6.3 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/atomix.css +119 -40
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +1 -1
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/atomix.umd.js +1 -1
  6. package/dist/atomix.umd.js.map +1 -1
  7. package/dist/atomix.umd.min.js +1 -1
  8. package/dist/charts.d.ts +30 -1
  9. package/dist/charts.js +566 -597
  10. package/dist/charts.js.map +1 -1
  11. package/dist/core.d.ts +30 -1
  12. package/dist/core.js +600 -624
  13. package/dist/core.js.map +1 -1
  14. package/dist/forms.d.ts +30 -1
  15. package/dist/forms.js +1122 -1163
  16. package/dist/forms.js.map +1 -1
  17. package/dist/heavy.d.ts +31 -89
  18. package/dist/heavy.js +1015 -1045
  19. package/dist/heavy.js.map +1 -1
  20. package/dist/index.d.ts +378 -104
  21. package/dist/index.esm.js +10959 -10837
  22. package/dist/index.esm.js.map +1 -1
  23. package/dist/index.js +10935 -10812
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.min.js +1 -1
  26. package/dist/index.min.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/components/Accordion/Accordion.tsx +2 -5
  29. package/src/components/AtomixGlass/AtomixGlass.test.tsx +14 -16
  30. package/src/components/AtomixGlass/AtomixGlass.tsx +137 -355
  31. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +32 -249
  32. package/src/components/AtomixGlass/GlassFilter.tsx +62 -68
  33. package/src/components/AtomixGlass/README.md +2 -1
  34. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +19 -18
  35. package/src/components/AtomixGlass/glass-border-styles.test.ts +58 -0
  36. package/src/components/AtomixGlass/glass-border-styles.ts +136 -0
  37. package/src/components/AtomixGlass/glass-utils.ts +411 -6
  38. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +158 -537
  39. package/src/components/AtomixGlass/stories/Border.stories.tsx +149 -0
  40. package/src/components/AtomixGlass/stories/Examples.stories.tsx +229 -89
  41. package/src/components/AtomixGlass/stories/Playground.stories.tsx +29 -340
  42. package/src/components/AtomixGlass/stories/argTypes.ts +30 -13
  43. package/src/components/AtomixGlass/stories/premium-presets.ts +206 -0
  44. package/src/components/AtomixGlass/stories/shared-components.tsx +52 -8
  45. package/src/components/Badge/Badge.tsx +4 -4
  46. package/src/components/Button/Button.tsx +2 -6
  47. package/src/components/Callout/Callout.test.tsx +4 -3
  48. package/src/components/Callout/Callout.tsx +2 -5
  49. package/src/components/Dropdown/Dropdown.tsx +3 -7
  50. package/src/components/Form/Checkbox.tsx +2 -8
  51. package/src/components/Form/Input.tsx +2 -9
  52. package/src/components/Form/Radio.tsx +2 -9
  53. package/src/components/Form/Select.tsx +2 -7
  54. package/src/components/Form/Textarea.tsx +2 -9
  55. package/src/components/Messages/Messages.tsx +2 -8
  56. package/src/components/Modal/Modal.tsx +4 -5
  57. package/src/components/Navigation/Nav/Nav.tsx +2 -6
  58. package/src/components/Navigation/Navbar/Navbar.tsx +2 -9
  59. package/src/components/Navigation/SideMenu/SideMenu.tsx +2 -6
  60. package/src/components/Pagination/Pagination.tsx +2 -10
  61. package/src/components/Popover/Popover.tsx +2 -9
  62. package/src/components/Progress/Progress.tsx +2 -7
  63. package/src/components/Rating/Rating.tsx +2 -10
  64. package/src/components/Spinner/Spinner.tsx +2 -7
  65. package/src/components/Steps/Steps.tsx +2 -10
  66. package/src/components/Tabs/Tabs.tsx +2 -9
  67. package/src/components/Toggle/Toggle.tsx +2 -10
  68. package/src/components/Tooltip/Tooltip.tsx +2 -5
  69. package/src/lib/composables/useAtomixGlass.ts +41 -10
  70. package/src/lib/composables/useAtomixGlassStyles.ts +59 -75
  71. package/src/lib/composables/usePerformanceMonitor.ts +5 -0
  72. package/src/lib/constants/components.ts +358 -46
  73. package/src/lib/types/components.ts +33 -1
  74. package/src/styles/01-settings/_settings.atomix-glass.scss +69 -31
  75. package/src/styles/02-tools/_tools.glass.scss +45 -3
  76. package/src/styles/06-components/_components.atomix-glass.scss +114 -77
  77. package/src/components/AtomixGlass/deprecated/AtomixGlass.deprecated.tsx +0 -390
package/dist/charts.js CHANGED
@@ -462,6 +462,44 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
462
462
  POSITIONS: [ "top", "bottom", "left", "right" ],
463
463
  ACTION_VARIANTS: [ "primary", "secondary", "success", "info", "warning", "error" ]
464
464
  }
465
+ }, GLASS_BORDER_GRADIENT = {
466
+ BASE_ANGLE: 135,
467
+ ANGLE_MULTIPLIER: .5,
468
+ VELOCITY_ANGLE_MULTIPLIER: .5,
469
+ CHROMATIC_OFFSET: 1.5,
470
+ STOP_1: {
471
+ MIN: 10,
472
+ BASE: 33,
473
+ get MULTIPLIER() {
474
+ return .009 * this.BASE;
475
+ }
476
+ },
477
+ STOP_2: {
478
+ MAX: 90,
479
+ BASE: 66,
480
+ get MULTIPLIER() {
481
+ return .006 * this.BASE;
482
+ }
483
+ },
484
+ OPACITY: {
485
+ /** Matches $glass-border-1-opacity (0.08) */
486
+ BASE_1: .08,
487
+ get BASE_2() {
488
+ return 3.33 * this.BASE_1;
489
+ },
490
+ get BASE_3() {
491
+ return 2.66 * this.BASE_1;
492
+ },
493
+ get BASE_4() {
494
+ return 5 * this.BASE_1;
495
+ },
496
+ get MULTIPLIER_LOW() {
497
+ return .066 * this.BASE_1;
498
+ },
499
+ get MULTIPLIER_HIGH() {
500
+ return .1 * this.BASE_1;
501
+ }
502
+ }
465
503
  }, ATOMIX_GLASS = {
466
504
  BASE_CLASS: "c-atomix-glass",
467
505
  CONTAINER_CLASS: "c-atomix-glass__container",
@@ -473,6 +511,22 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
473
511
  BORDER_BACKDROP_CLASS: "c-atomix-glass__border-backdrop",
474
512
  BORDER_1_CLASS: "c-atomix-glass__border-1",
475
513
  BORDER_2_CLASS: "c-atomix-glass__border-2",
514
+ /** Centralized liquid glass rim configuration */
515
+ BORDER: {
516
+ WIDTH_CSS_VAR: "--atomix-glass-border-width",
517
+ DEFAULT_WIDTH: "0.5px",
518
+ GRADIENT_CSS_VARS: {
519
+ GRADIENT_1: "--atomix-glass-border-gradient-1",
520
+ GRADIENT_2: "--atomix-glass-border-gradient-2"
521
+ },
522
+ GRADIENT: GLASS_BORDER_GRADIENT,
523
+ OVER_LIGHT: {
524
+ opacity: .7
525
+ },
526
+ DARK: {
527
+ opacity: .35
528
+ }
529
+ },
476
530
  HOVER_1_CLASS: "c-atomix-glass__hover-1",
477
531
  HOVER_2_CLASS: "c-atomix-glass__hover-2",
478
532
  HOVER_3_CLASS: "c-atomix-glass__hover-3",
@@ -501,25 +555,22 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
501
555
  SHADER: "c-atomix-glass--shader"
502
556
  },
503
557
  DEFAULTS: {
504
- DISPLACEMENT_SCALE: 70,
558
+ /** Subtle refraction — Apple UI chrome avoids heavy liquid distortion */
559
+ DISPLACEMENT_SCALE: 28,
505
560
  get BLUR_AMOUNT() {
506
- return .15 * this.DISPLACEMENT_SCALE;
507
- // Dynamically computed based on displacement
508
- },
509
- get SATURATION() {
510
- return 100 + .5 * this.DISPLACEMENT_SCALE;
511
- // Saturate relative to intensity
512
- },
561
+ // Apple Music sidebar / player bar: ~20–40px frost (see $glass-backdrop-filter)
562
+ return Math.max(20, .72 * this.DISPLACEMENT_SCALE);
563
+ },
564
+ /** Fixed 180% matches Apple's saturate(180%) backdrop recipe */
565
+ SATURATION: 180,
513
566
  get ABERRATION_INTENSITY() {
514
- return .03 * this.DISPLACEMENT_SCALE;
515
- // Scale aberration with displacement
516
- },
517
- ELASTICITY: .15,
567
+ return .02 * this.DISPLACEMENT_SCALE;
568
+ },
569
+ ELASTICITY: .05,
518
570
  get CORNER_RADIUS() {
519
571
  return 16;
520
572
  // Use 16 to match SCSS design system (was 20)
521
573
  },
522
- PADDING: "0",
523
574
  MODE: "standard",
524
575
  OVER_LIGHT: !1,
525
576
  ENABLE_OVER_LIGHT_LAYERS: !0,
@@ -535,19 +586,24 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
535
586
  },
536
587
  CONSTANTS: {
537
588
  ACTIVATION_ZONE: 200,
538
- LERP_FACTOR: .08,
589
+ LERP_FACTOR: .05,
590
+ // Lower = more viscous, liquid-smooth tracking (Apple feel)
539
591
  SMOOTHSTEP_POWER: 2.5,
540
592
  MIN_BLUR: .1,
541
593
  MOUSE_INFLUENCE_DIVISOR: 100,
542
594
  EDGE_FADE_PIXELS: 2,
543
- // Elasticity physics constants
544
- ELASTICITY_TRANSLATION_FACTOR: .1,
595
+ // Elasticity physics constants — Apple-tuned: soft springs, fast settling, minimal stretch
596
+ ELASTICITY_TRANSLATION_FACTOR: .06,
597
+ // Subtler elastic shift (was 0.1)
545
598
  ELASTICITY_DISTANCE_THRESHOLD: 200,
546
599
  ELASTICITY_COMPRESSION_FACTOR: .3,
547
- ELASTICITY_STIFFNESS: .1,
548
- ELASTICITY_DAMPING: .76,
600
+ ELASTICITY_STIFFNESS: .06,
601
+ // Softer springs = gentler motion (was 0.1)
602
+ ELASTICITY_DAMPING: .88,
603
+ // Fast settling, no wobble (was 0.76)
549
604
  ELASTICITY_VELOCITY_FACTOR: .65,
550
- ELASTICITY_STRETCH_RATIO: .45,
605
+ ELASTICITY_STRETCH_RATIO: .25,
606
+ // Less visible surface tension stretch (was 0.45)
551
607
  ELASTICITY_MAGNIFICATION_BASE: 1.02,
552
608
  // Note: This default must match the SCSS variable --atomix-radius-md
553
609
  // @see src/styles/01-settings/_settings.global.scss
@@ -561,55 +617,16 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
561
617
  },
562
618
  // Gradient calculation constants
563
619
  GRADIENT: {
564
- BASE_ANGLE: 135,
565
- // Base angle for border gradients (degrees)
566
- ANGLE_MULTIPLIER: 1.2,
567
- // Multiplier for mouse influence on angle
568
- VELOCITY_ANGLE_MULTIPLIER: 2.5,
569
- // How much velocity affects gradient rotation
570
- CHROMATIC_OFFSET: 1.5,
571
- // Degree offset for chromatic rim layers
572
- BORDER_STOP_1: {
573
- MIN: 10,
574
- // Minimum percentage for border stop 1
575
- BASE: 33,
576
- // Base percentage for border stop 1
577
- get MULTIPLIER() {
578
- return .009 * this.BASE;
579
- }
580
- },
581
- BORDER_STOP_2: {
582
- MAX: 90,
583
- // Maximum percentage for border stop 2
584
- BASE: 66,
585
- // Base percentage for border stop 2
586
- get MULTIPLIER() {
587
- return .006 * this.BASE;
588
- }
589
- },
590
- BORDER_OPACITY: {
591
- BASE_1: .12,
592
- // Base opacity for border gradient 1
593
- get BASE_2() {
594
- return 3.33 * this.BASE_1;
595
- },
596
- // Base opacity for border gradient 2
597
- get BASE_3() {
598
- return 2.66 * this.BASE_1;
599
- },
600
- // Base opacity for border gradient 3
601
- get BASE_4() {
602
- return 5 * this.BASE_1;
603
- },
604
- // Base opacity for border gradient 4
605
- get MULTIPLIER_LOW() {
606
- return .066 * this.BASE_1;
607
- },
608
- // Low multiplier for mouse influence on opacity
609
- get MULTIPLIER_HIGH() {
610
- return .1 * this.BASE_1;
611
- }
612
- },
620
+ BASE_ANGLE: GLASS_BORDER_GRADIENT.BASE_ANGLE,
621
+ ANGLE_MULTIPLIER: GLASS_BORDER_GRADIENT.ANGLE_MULTIPLIER,
622
+ VELOCITY_ANGLE_MULTIPLIER: GLASS_BORDER_GRADIENT.VELOCITY_ANGLE_MULTIPLIER,
623
+ CHROMATIC_OFFSET: GLASS_BORDER_GRADIENT.CHROMATIC_OFFSET,
624
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.STOP_1 */
625
+ BORDER_STOP_1: GLASS_BORDER_GRADIENT.STOP_1,
626
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.STOP_2 */
627
+ BORDER_STOP_2: GLASS_BORDER_GRADIENT.STOP_2,
628
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.OPACITY */
629
+ BORDER_OPACITY: GLASS_BORDER_GRADIENT.OPACITY,
613
630
  CENTER_POSITION: 50,
614
631
  // Center position percentage (50%)
615
632
  HOVER_POSITION: {
@@ -642,8 +659,8 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
642
659
  return 2 * this.BLACK_STOP;
643
660
  },
644
661
  // End percentage for black hover 1
645
- WHITE_START: .5,
646
- // Start opacity for white hover 1
662
+ WHITE_START: .35,
663
+ // Gentler hover flash Apple hover is barely visible
647
664
  get WHITE_STOP() {
648
665
  return this.BLACK_END - 10;
649
666
  }
@@ -661,8 +678,8 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
661
678
  return 2 * this.BLACK_STOP;
662
679
  },
663
680
  // End percentage for black hover 2
664
- WHITE_START: 1,
665
- // Start opacity for white hover 2
681
+ WHITE_START: .7,
682
+ // Gentler hover flash
666
683
  get WHITE_STOP() {
667
684
  return this.BLACK_END;
668
685
  }
@@ -680,8 +697,8 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
680
697
  return 2 * this.BLACK_STOP;
681
698
  },
682
699
  // End percentage for black hover 3
683
- WHITE_START: 1,
684
- // Start opacity for white hover 3
700
+ WHITE_START: .7,
701
+ // Gentler hover flash
685
702
  get WHITE_STOP() {
686
703
  return this.BLACK_END;
687
704
  }
@@ -691,13 +708,13 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
691
708
  BASE_GRADIENT: {
692
709
  ANGLE: 135,
693
710
  // Gradient angle in degrees
694
- BLACK_START_BASE: .15,
711
+ BLACK_START_BASE: .1,
695
712
  // Base start opacity for black
696
713
  get BLACK_START_MULTIPLIER() {
697
714
  return .02 * this.BLACK_START_BASE;
698
715
  },
699
716
  // Multiplier for mouse X influence on start
700
- BLACK_MID_BASE: .1,
717
+ BLACK_MID_BASE: .07,
701
718
  // Base mid opacity for black
702
719
  get BLACK_MID_MULTIPLIER() {
703
720
  return .02 * this.BLACK_MID_BASE;
@@ -718,7 +735,7 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
718
735
  }
719
736
  },
720
737
  OVERLAY_GRADIENT: {
721
- BLACK_START_BASE: .12,
738
+ BLACK_START_BASE: .08,
722
739
  // Base start opacity for black overlay
723
740
  get BLACK_START_MULTIPLIER() {
724
741
  return .025 * this.BLACK_START_BASE;
@@ -742,14 +759,14 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
742
759
  return .416 * this.BLACK_START_BASE;
743
760
  }
744
761
  },
745
- // Overlay highlight constants
762
+ // Overlay highlight constants — Apple places specular at the top-center
746
763
  OVERLAY_HIGHLIGHT: {
747
- POSITION_X: 20,
748
- // X position percentage
749
- POSITION_Y: 20,
750
- // Y position percentage
751
- WHITE_OPACITY: .4,
752
- // White opacity in gradient
764
+ POSITION_X: 50,
765
+ // Centered horizontal — Apple's top-center specular
766
+ POSITION_Y: 5,
767
+ // Very top — catches light like a curved glass surface
768
+ WHITE_OPACITY: .28,
769
+ // Softer specular visible but not glaring
753
770
  get STOP() {
754
771
  return 150 * this.WHITE_OPACITY;
755
772
  },
@@ -770,6 +787,10 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
770
787
  SATURATION: {
771
788
  HIGH_CONTRAST: 200
772
789
  },
790
+ // Container shadows — hairline inner catch + soft floating lift (Apple player bar)
791
+ CONTAINER_SHADOW: {
792
+ LIGHT: "inset 0 0.5px 0 rgba(255, 255, 255, 0.32), inset 0 1px 2px rgba(255, 255, 255, 0.06), 0 8px 32px rgba(0, 0, 0, 0.28), 0 2px 8px rgba(0, 0, 0, 0.16)"
793
+ },
773
794
  // Phase 1: Animation System Constants
774
795
  ANIMATION: {
775
796
  // Breathing effect timing (in milliseconds)
@@ -1664,9 +1685,38 @@ const ChartToolbar = memo( forwardRef((({chartType: chartType = "line", groups:
1664
1685
  });
1665
1686
  })));
1666
1687
 
1688
+ /**
1689
+ * Component Utilities
1690
+ *
1691
+ * Helper functions for component development with the new customization system
1692
+ */
1693
+ /**
1694
+ * Merge multiple class names
1695
+ */
1696
+ function mergeClassNames(...classes) {
1697
+ return classes.filter(Boolean).join(" ");
1698
+ }
1699
+
1700
+ /**
1701
+ * Utility to merge multiple React refs into one
1702
+ */ function setRef(ref, value) {
1703
+ "function" == typeof ref ? ref(value) : ref && (
1704
+ // This is safe because we're checking that ref exists first
1705
+ ref.current = value);
1706
+ }
1707
+
1708
+ /**
1709
+ * Combines two React refs into a single ref function
1710
+ * This is used when you need to use and forward a ref at the same time
1711
+ */ function useForkRef(refA, refB) {
1712
+ return React.useMemo((() => null == refA && null == refB ? null : refValue => {
1713
+ setRef(refA, refValue), setRef(refB, refValue);
1714
+ }), [ refA, refB ]);
1715
+ }
1716
+
1667
1717
  ChartToolbar.displayName = "ChartToolbar";
1668
1718
 
1669
- const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect => rect ? {
1719
+ const {CONSTANTS: CONSTANTS$3} = ATOMIX_GLASS, calculateElementCenter = rect => rect ? {
1670
1720
  x: rect.left + rect.width / 2,
1671
1721
  y: rect.top + rect.height / 2
1672
1722
  } : {
@@ -1674,37 +1724,37 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
1674
1724
  y: 0
1675
1725
  }, calculateMouseInfluence = mouseOffset => {
1676
1726
  if (!mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y) return 0;
1677
- // Bounded calculation keeps the glass effect subtle and stable
1678
- const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$2.MOUSE_INFLUENCE_DIVISOR;
1727
+ // Clamp influence to keep mouse response subtle and stable.
1728
+ const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$3.MOUSE_INFLUENCE_DIVISOR;
1679
1729
  return Math.min(.8, influence);
1680
1730
  // Tighter cap to prevent blur/filter blow-out
1681
- }, clampBlur = value => "number" != typeof value || isNaN(value) ? CONSTANTS$2.MIN_BLUR : Math.max(CONSTANTS$2.MIN_BLUR, Math.min(50, value)), validateGlassSize = size => size && "number" == typeof size.width && "number" == typeof size.height && size.width > 0 && size.height > 0 && size.width <= CONSTANTS$2.MAX_SIZE && size.height <= CONSTANTS$2.MAX_SIZE, parseBorderRadiusValue = value => {
1731
+ }, clampBlur = value => "number" != typeof value || isNaN(value) ? CONSTANTS$3.MIN_BLUR : Math.max(CONSTANTS$3.MIN_BLUR, Math.min(50, value)), validateGlassSize = size => size && "number" == typeof size.width && "number" == typeof size.height && size.width > 0 && size.height > 0 && size.width <= CONSTANTS$3.MAX_SIZE && size.height <= CONSTANTS$3.MAX_SIZE, parseBorderRadiusValue = value => {
1682
1732
  if ("number" == typeof value) return Math.max(0, value);
1683
- if ("string" != typeof value || !value.trim()) return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
1733
+ if ("string" != typeof value || !value.trim()) return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
1684
1734
  const trimmedValue = value.trim();
1685
1735
  // Handle px values
1686
1736
  if (trimmedValue.endsWith("px")) {
1687
1737
  const parsed = parseFloat(trimmedValue);
1688
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
1738
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
1689
1739
  }
1690
1740
  // Handle rem values (assume 16px = 1rem)
1691
1741
  if (trimmedValue.endsWith("rem")) {
1692
1742
  const parsed = parseFloat(trimmedValue);
1693
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
1743
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
1694
1744
  }
1695
1745
  // Handle em values (assume 16px = 1em for simplicity)
1696
1746
  if (trimmedValue.endsWith("em")) {
1697
1747
  const parsed = parseFloat(trimmedValue);
1698
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
1748
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
1699
1749
  }
1700
1750
  // Handle percentage (convert to approximate px value, assuming 200px container)
1701
1751
  if (trimmedValue.endsWith("%")) {
1702
1752
  const parsed = parseFloat(trimmedValue);
1703
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
1753
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
1704
1754
  }
1705
1755
  // Handle unitless numbers
1706
1756
  const numValue = parseFloat(trimmedValue);
1707
- return isNaN(numValue) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
1757
+ return isNaN(numValue) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
1708
1758
  }, extractBorderRadiusFromElement = element => {
1709
1759
  if (!element || !element.props) return null;
1710
1760
  // Check inline styles first (highest priority)
@@ -1720,11 +1770,11 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
1720
1770
  // If element has children, recursively check them
1721
1771
  if (element.props.children) {
1722
1772
  const childRadius = extractBorderRadiusFromChildren(element.props.children);
1723
- if (childRadius > 0 && childRadius !== CONSTANTS$2.DEFAULT_CORNER_RADIUS) return childRadius;
1773
+ if (childRadius > 0 && childRadius !== CONSTANTS$3.DEFAULT_CORNER_RADIUS) return childRadius;
1724
1774
  }
1725
1775
  return null;
1726
1776
  }, extractBorderRadiusFromChildren = children => {
1727
- if (!children) return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
1777
+ if (!children) return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
1728
1778
  try {
1729
1779
  const childArray = React.Children.toArray(children);
1730
1780
  for (let i = 0; i < childArray.length; i++) {
@@ -1737,7 +1787,7 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
1737
1787
  } catch (error) {
1738
1788
  // Silently handle errors
1739
1789
  }
1740
- return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
1790
+ return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
1741
1791
  }, smoothstep = t => {
1742
1792
  const clamped = Math.max(0, Math.min(1, t));
1743
1793
  return clamped * clamped * (3 - 2 * clamped);
@@ -1747,7 +1797,47 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
1747
1797
  value: current + newVelocity,
1748
1798
  velocity: newVelocity
1749
1799
  };
1750
- }, getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
1800
+ };
1801
+
1802
+ /**
1803
+ * Calculate element center from bounding rect
1804
+ */
1805
+ /**
1806
+ * Normalizes a layout inset for use in CSS custom properties.
1807
+ *
1808
+ * @param value - Raw inset from `style` (number, px string, or `auto`).
1809
+ * @param fallback - Value used when `value` is undefined.
1810
+ */
1811
+ function formatGlassInsetValue(value, fallback = "auto") {
1812
+ return void 0 === value ? "number" == typeof fallback ? `${fallback}px` : String(fallback) : "auto" === value ? "auto" : "number" == typeof value ? `${value}px` : value;
1813
+ }
1814
+
1815
+ /**
1816
+ * Determines whether the glass should use fixed/sticky layout semantics.
1817
+ *
1818
+ * @param explicit - Value of the `isFixedOrSticky` prop.
1819
+ * @param position - `position` from the consumer `style` object.
1820
+ */
1821
+ /** Coerces a value to a finite number, returning `fallback` when invalid. */
1822
+ function toSafeNumber(value, fallback = 0) {
1823
+ return "number" != typeof value || isNaN(value) ? fallback : value;
1824
+ }
1825
+
1826
+ /**
1827
+ * Calculates the target frame rate for shader time-animation loops.
1828
+ *
1829
+ * Balances visual quality against distortion complexity and `animationSpeed`.
1830
+ */
1831
+ /**
1832
+ * Computes per-channel displacement scale for the SVG chromatic-aberration filter.
1833
+ */
1834
+ function getChromaticDisplacementScale(mode, displacementScale, aberrationIntensity, channelFactor) {
1835
+ return displacementScale * (("shader" === mode ? 1 : -1) - aberrationIntensity * channelFactor);
1836
+ }
1837
+
1838
+ /**
1839
+ * Get displacement map URL based on mode
1840
+ */ const getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
1751
1841
  switch (mode) {
1752
1842
  case "standard":
1753
1843
  return displacementMap;
@@ -1764,13 +1854,28 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
1764
1854
  default:
1765
1855
  return console.warn("AtomixGlass: Invalid displacement mode"), displacementMap;
1766
1856
  }
1767
- }, sharedShaderCache = new Map, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
1768
- style: {
1769
- position: "absolute",
1770
- width: "100%",
1771
- height: "100%",
1772
- inset: 0
1773
- },
1857
+ }, sharedShaderCache = new Map, CHROMATIC_CHANNELS = [ {
1858
+ result: "RED_DISPLACED",
1859
+ channelResult: "RED_CHANNEL",
1860
+ aberrationFactor: 0,
1861
+ colorMatrix: "1 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 1 0"
1862
+ }, {
1863
+ result: "GREEN_DISPLACED",
1864
+ channelResult: "GREEN_CHANNEL",
1865
+ aberrationFactor: .02,
1866
+ colorMatrix: "0 0 0 0 0\n0 1 0 0 0\n0 0 0 0 0\n0 0 0 1 0"
1867
+ }, {
1868
+ result: "BLUE_DISPLACED",
1869
+ channelResult: "BLUE_CHANNEL",
1870
+ aberrationFactor: .03,
1871
+ colorMatrix: "0 0 0 0 0\n0 0 0 0 0\n0 0 1 0 0\n0 0 0 1 0"
1872
+ } ], FILTER_SVG_STYLE = {
1873
+ position: "absolute",
1874
+ width: "100%",
1875
+ height: "100%",
1876
+ inset: 0
1877
+ }, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsx("svg", {
1878
+ style: FILTER_SVG_STYLE,
1774
1879
  "aria-hidden": "true",
1775
1880
  children: jsxs("defs", {
1776
1881
  children: [ jsxs("radialGradient", {
@@ -1824,43 +1929,21 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
1824
1929
  dx: "0",
1825
1930
  dy: "0",
1826
1931
  result: "CENTER_ORIGINAL"
1827
- }), jsx("feDisplacementMap", {
1828
- in: "SourceGraphic",
1829
- in2: "DISPLACEMENT_MAP",
1830
- scale: displacementScale * ("shader" === mode ? 1 : -1),
1831
- xChannelSelector: "R",
1832
- yChannelSelector: "B",
1833
- result: "RED_DISPLACED"
1834
- }), jsx("feColorMatrix", {
1835
- in: "RED_DISPLACED",
1836
- type: "matrix",
1837
- values: "1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
1838
- result: "RED_CHANNEL"
1839
- }), jsx("feDisplacementMap", {
1840
- in: "SourceGraphic",
1841
- in2: "DISPLACEMENT_MAP",
1842
- scale: displacementScale * (("shader" === mode ? 1 : -1) - .02 * aberrationIntensity),
1843
- xChannelSelector: "R",
1844
- yChannelSelector: "B",
1845
- result: "GREEN_DISPLACED"
1846
- }), jsx("feColorMatrix", {
1847
- in: "GREEN_DISPLACED",
1848
- type: "matrix",
1849
- values: "0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
1850
- result: "GREEN_CHANNEL"
1851
- }), jsx("feDisplacementMap", {
1852
- in: "SourceGraphic",
1853
- in2: "DISPLACEMENT_MAP",
1854
- scale: displacementScale * (("shader" === mode ? 1 : -1) - .03 * aberrationIntensity),
1855
- xChannelSelector: "R",
1856
- yChannelSelector: "B",
1857
- result: "BLUE_DISPLACED"
1858
- }), jsx("feColorMatrix", {
1859
- in: "BLUE_DISPLACED",
1860
- type: "matrix",
1861
- values: "0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0",
1862
- result: "BLUE_CHANNEL"
1863
- }), jsx("feBlend", {
1932
+ }), CHROMATIC_CHANNELS.map((channel => jsxs(React.Fragment, {
1933
+ children: [ jsx("feDisplacementMap", {
1934
+ in: "SourceGraphic",
1935
+ in2: "DISPLACEMENT_MAP",
1936
+ scale: getChromaticDisplacementScale(mode, displacementScale, aberrationIntensity, channel.aberrationFactor),
1937
+ xChannelSelector: "R",
1938
+ yChannelSelector: "B",
1939
+ result: channel.result
1940
+ }), jsx("feColorMatrix", {
1941
+ in: channel.result,
1942
+ type: "matrix",
1943
+ values: channel.colorMatrix,
1944
+ result: channel.channelResult
1945
+ }) ]
1946
+ }, channel.channelResult))), jsx("feBlend", {
1864
1947
  in: "GREEN_CHANNEL",
1865
1948
  in2: "BLUE_CHANNEL",
1866
1949
  mode: "screen",
@@ -1901,21 +1984,22 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
1901
1984
  });
1902
1985
 
1903
1986
  /**
1904
- * Calculate distance between two points
1987
+ * Module-level LRU cache for shader displacement maps.
1988
+ *
1989
+ * Shared across all `AtomixGlassContainer` instances so identical size and
1990
+ * variant combinations are generated once.
1905
1991
  */ GlassFilterComponent.displayName = "GlassFilter";
1906
1992
 
1907
- // Memoize component to prevent unnecessary re-renders
1993
+ /** Shallow prop comparison to avoid redundant SVG filter regeneration. */
1908
1994
  const GlassFilter = memo(GlassFilterComponent, ((prevProps, nextProps) => prevProps.id === nextProps.id && prevProps.displacementScale === nextProps.displacementScale && prevProps.aberrationIntensity === nextProps.aberrationIntensity && prevProps.mode === nextProps.mode && prevProps.shaderMapUrl === nextProps.shaderMapUrl && prevProps.blurAmount === nextProps.blurAmount)), AtomixGlassContainer = forwardRef((({children: children, className: className = "", style: style, displacementScale: displacementScale = 25, blurAmount: blurAmount = .0625, saturation: saturation = 180, aberrationIntensity: aberrationIntensity = 2, mouseOffset: mouseOffset = {
1909
1995
  x: 0,
1910
1996
  y: 0
1911
- }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, padding: padding = "0 0", glassSize: glassSize = {
1997
+ }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, glassSize: glassSize = {
1912
1998
  width: 0,
1913
1999
  height: 0
1914
- }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, isFixedOrSticky: isFixedOrSticky = !1,
1915
- // Phase 1: Animation System props
1916
- shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", contentRef: contentRef}, ref) => {
2000
+ }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, isFixedOrSticky: isFixedOrSticky = !1, shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", contentRef: contentRef}, ref) => {
1917
2001
  // React 18 useId — stable, unique, and SSR-safe (no module-level counter)
1918
- const rawId = useId(), filterId = useMemo((() => `atomix-glass-filter-${rawId.replace(/:/g, "")}`), [ rawId ]), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
2002
+ const rawId = useId(), filterId = useMemo((() => `atomix-glass-filter-${rawId.replace(/:/g, "")}`), [ rawId ]), containerRef = useForkRef(ref, null), [shaderMapUrl, setShaderMapUrl] = useState(""), shaderGeneratorRef = useRef(null), shaderUtilsRef = useRef(null), shaderDebounceTimeoutRef = useRef(null), shaderUpdateTimeoutRef = useRef(null), animationFrameRef = useRef(null);
1919
2003
  // Lazy load shader utilities only when shader mode is needed
1920
2004
  useEffect((() => {
1921
2005
  "shader" === mode ?
@@ -1993,15 +2077,23 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
1993
2077
  shaderGeneratorRef.current = null;
1994
2078
  }
1995
2079
  };
1996
- }), [ mode, glassSize, shaderVariant ]),
1997
- // Phase 1: Time-Based Animation Loop - Continuous shader regeneration
1998
- useEffect((() => {
2080
+ }), [ mode, glassSize, shaderVariant ]), useEffect((() => {
1999
2081
  // Only run animations in shader mode with time animation enabled
2000
2082
  if ("shader" !== mode || !withTimeAnimation || effectiveReducedMotion || effectiveWithoutEffects)
2001
2083
  // Cancel any existing animation frame
2002
2084
  return void (null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
2003
2085
  animationFrameRef.current = null));
2004
- const baseFps = "ultra" === distortionQuality ? 60 : "high" === distortionQuality ? 30 : "medium" === distortionQuality ? 24 : 20, effectiveSpeed = Math.max(.5, Math.min(2, animationSpeed || 1)), complexity = withMultiLayerDistortion ? Math.max(1, (distortionOctaves || 3) / 3 + .25 * Math.max(0, (distortionLacunarity || 2) - 2) + Math.max(0, (distortionGain || .5) - .5)) : 1, frameInterval = 1e3 / Math.max(12, Math.min(60, Math.round(baseFps * effectiveSpeed / complexity)));
2086
+ const targetFps = function(options) {
2087
+ const {distortionQuality: distortionQuality, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5} = options, baseFps = "ultra" === distortionQuality ? 60 : "high" === distortionQuality ? 30 : "medium" === distortionQuality ? 24 : 20, effectiveSpeed = Math.max(.5, Math.min(2, animationSpeed)), complexity = withMultiLayerDistortion ? Math.max(1, distortionOctaves / 3 + .25 * Math.max(0, distortionLacunarity - 2) + Math.max(0, distortionGain - .5)) : 1;
2088
+ return Math.max(12, Math.min(60, Math.round(baseFps * effectiveSpeed / complexity)));
2089
+ }({
2090
+ distortionQuality: distortionQuality,
2091
+ animationSpeed: animationSpeed,
2092
+ withMultiLayerDistortion: withMultiLayerDistortion,
2093
+ distortionOctaves: distortionOctaves,
2094
+ distortionLacunarity: distortionLacunarity,
2095
+ distortionGain: distortionGain
2096
+ }), frameInterval = 1e3 / targetFps;
2005
2097
  let lastUpdate = 0, isCancelled = !1;
2006
2098
  const animate = currentTime => {
2007
2099
  if (!isCancelled) {
@@ -2025,88 +2117,21 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
2025
2117
  };
2026
2118
  }), [ mode, withTimeAnimation, animationSpeed, displacementScale, withMultiLayerDistortion, distortionOctaves, distortionLacunarity, distortionGain, distortionQuality, effectiveReducedMotion, effectiveWithoutEffects, glassSize ]);
2027
2119
  // Removed forced reflow to avoid layout thrash and potential feedback sizing loops
2028
- const [rectCache, setRectCache] = useState(null);
2029
- useEffect((() => {
2030
- if (!ref || "function" == typeof ref) return;
2031
- const element = ref.current;
2032
- if (element) try {
2033
- setRectCache(element.getBoundingClientRect());
2034
- } catch (error) {
2035
- console.warn("AtomixGlassContainer: Error getting element bounds", error), setRectCache(null);
2036
- }
2037
- }), [ ref, glassSize ]);
2038
- const liquidBlur = useMemo((() => {
2039
- const defaultBlur = {
2040
- baseBlur: blurAmount,
2041
- edgeBlur: 1.25 * blurAmount,
2042
- centerBlur: 1.1 * blurAmount,
2043
- flowBlur: 1.2 * blurAmount
2044
- };
2045
- // Enhanced validation for liquid blur
2046
- if (!withLiquidBlur || !rectCache || !mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y || isNaN(mouseOffset.x) || isNaN(mouseOffset.y)) return defaultBlur;
2047
- try {
2048
- const mouseInfluence = calculateMouseInfluence(mouseOffset), maxBlur = 2 * blurAmount, baseBlur = Math.min(maxBlur, blurAmount + mouseInfluence * blurAmount * .15), edgeIntensity = .15 * mouseInfluence, edgeBlur = Math.min(maxBlur, baseBlur * (.8 + .4 * edgeIntensity)), centerIntensity = .1 * mouseInfluence, centerBlur = Math.min(maxBlur, baseBlur * (.3 + .3 * centerIntensity)), flowBlur = Math.min(maxBlur, 1.2 * baseBlur);
2049
- // NOTE: hover/active multipliers intentionally omitted here —
2050
- // they belong on opacity layers, not the blur filter itself.
2051
- return {
2052
- baseBlur: clampBlur(baseBlur),
2053
- edgeBlur: clampBlur(edgeBlur),
2054
- centerBlur: clampBlur(centerBlur),
2055
- flowBlur: clampBlur(flowBlur)
2056
- };
2057
- } catch (error) {
2058
- return console.warn("AtomixGlassContainer: Error calculating liquid blur", error),
2059
- defaultBlur;
2060
- }
2061
- }), [ withLiquidBlur, blurAmount, mouseOffset, rectCache ]), backdropStyle = useMemo((() => {
2120
+ const containerVars = useMemo((() => {
2062
2121
  try {
2063
- const dynamicSaturation = saturation + 20 * (liquidBlur.baseBlur || 0), validatedBaseBlur = "number" != typeof liquidBlur.baseBlur || isNaN(liquidBlur.baseBlur) ? 0 : liquidBlur.baseBlur, validatedEdgeBlur = "number" != typeof liquidBlur.edgeBlur || isNaN(liquidBlur.edgeBlur) ? 0 : liquidBlur.edgeBlur, validatedCenterBlur = "number" != typeof liquidBlur.centerBlur || isNaN(liquidBlur.centerBlur) ? 0 : liquidBlur.centerBlur, validatedFlowBlur = "number" != typeof liquidBlur.flowBlur || isNaN(liquidBlur.flowBlur) ? 0 : liquidBlur.flowBlur, area = rectCache ? rectCache.width * rectCache.height : 0;
2064
- // Validate blur values before using them
2065
- return !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? {
2066
- backdropFilter: `blur(${clampBlur(Math.max(validatedBaseBlur, .8 * validatedEdgeBlur, 1.1 * validatedCenterBlur, .9 * validatedFlowBlur))}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1.05}) brightness(${overLightConfig?.brightness || 1.05})`
2067
- } : {
2068
- backdropFilter: `blur(${clampBlur(.4 * validatedBaseBlur + .25 * validatedEdgeBlur + .15 * validatedCenterBlur + .2 * validatedFlowBlur)}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig?.contrast || 1}) brightness(${overLightConfig?.brightness || 1})`
2069
- };
2070
- // Single-pass fallback: stronger radius to match perceived blur of multi-pass
2071
- } catch (error) {
2072
- return console.warn("AtomixGlassContainer: Error calculating backdrop style", error),
2073
- {
2074
- backdropFilter: `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`
2075
- };
2076
- }
2077
- }), [ liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveWithoutEffects, withLiquidBlur, overLightConfig ]), containerVars = useMemo((() => {
2078
- try {
2079
- // Safe extraction of mouse offset values
2080
- const mx = mouseOffset && "number" == typeof mouseOffset.x && !isNaN(mouseOffset.x) ? mouseOffset.x : 0, my = mouseOffset && "number" == typeof mouseOffset.y && !isNaN(mouseOffset.y) ? mouseOffset.y : 0;
2081
2122
  return {
2082
- "--atomix-glass-container-radius": `${"number" != typeof borderRadius || isNaN(borderRadius) ? 0 : borderRadius}px`,
2083
- "--atomix-glass-container-backdrop": backdropStyle?.backdropFilter || "none",
2084
- "--atomix-glass-container-shadow": overLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.4 + .002 * mx) * (overLightConfig?.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.2 + .001 * Math.abs(my)) * (overLightConfig?.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.08 + .001 * Math.abs(mx + my)) * (overLightConfig?.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.12 + .002 * Math.abs(my)) * (overLightConfig?.shadowIntensity || 1)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset",
2085
- "--atomix-glass-container-shadow-opacity": effectiveWithoutEffects ? 0 : 1,
2086
- // Background and shadow values use design token-aligned RGB values
2087
- "--atomix-glass-container-bg": overLight ? `linear-gradient(${180 + .5 * mx}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)` : "none",
2088
- "--atomix-glass-container-text-shadow": overLight ? "0px 1px 2px rgba(255, 255, 255, 0.15)" : "0px 2px 12px rgba(0, 0, 0, 0.4)",
2089
- "--atomix-glass-container-box-shadow": overLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)"
2123
+ "--atomix-glass-container-radius": `${"number" != typeof borderRadius || isNaN(borderRadius) ? 0 : borderRadius}px`
2090
2124
  };
2091
2125
  } catch (error) {
2092
2126
  return console.warn("AtomixGlassContainer: Error generating container variables", error),
2093
2127
  {
2094
- "--atomix-glass-container-padding": "0 0",
2095
- "--atomix-glass-container-radius": "0px",
2096
- "--atomix-glass-container-backdrop": "none",
2097
- "--atomix-glass-container-shadow": "none",
2098
- "--atomix-glass-container-shadow-opacity": 1,
2099
- "--atomix-glass-container-bg": "none",
2100
- "--atomix-glass-container-text-shadow": "none"
2128
+ "--atomix-glass-container-radius": "0px"
2101
2129
  };
2102
2130
  }
2103
- }), [ borderRadius, backdropStyle, mouseOffset, overLight, effectiveWithoutEffects, overLightConfig ]);
2131
+ }), [ borderRadius ]);
2104
2132
  return jsx("div", {
2105
- ref: el => {
2106
- // Handle forwarded ref
2107
- "function" == typeof ref ? ref(el) : ref && (ref.current = el);
2108
- },
2109
- className: `${ATOMIX_GLASS.CONTAINER_CLASS} ${className} ${isActive ? ATOMIX_GLASS.CLASSES.ACTIVE : ""} ${overLight ? ATOMIX_GLASS.CLASSES.OVER_LIGHT : ""}`,
2133
+ ref: containerRef,
2134
+ className: mergeClassNames(ATOMIX_GLASS.CONTAINER_CLASS, className, isActive && ATOMIX_GLASS.CLASSES.ACTIVE, overLight && ATOMIX_GLASS.CLASSES.OVER_LIGHT),
2110
2135
  style: {
2111
2136
  ...style,
2112
2137
  ...containerVars
@@ -2124,8 +2149,8 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
2124
2149
  blurAmount: blurAmount,
2125
2150
  mode: mode,
2126
2151
  id: filterId,
2127
- displacementScale: "number" != typeof displacementScale || isNaN(displacementScale) ? 0 : displacementScale,
2128
- aberrationIntensity: "number" != typeof aberrationIntensity || isNaN(aberrationIntensity) ? 0 : aberrationIntensity,
2152
+ displacementScale: toSafeNumber(displacementScale),
2153
+ aberrationIntensity: toSafeNumber(aberrationIntensity),
2129
2154
  shaderMapUrl: shaderMapUrl
2130
2155
  }), jsx("div", {
2131
2156
  className: ATOMIX_GLASS.FILTER_OVERLAY_CLASS,
@@ -2147,8 +2172,12 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
2147
2172
  });
2148
2173
  }));
2149
2174
 
2150
- // ─── Blur multiplier constants (module-level, never change at runtime) ────────
2151
- AtomixGlassContainer.displayName = "AtomixGlassContainer";
2175
+ /**
2176
+ * Internal glass surface that owns backdrop-filter, SVG distortion, and content.
2177
+ *
2178
+ * Layout and stacking styles are applied via the `style` prop from the parent.
2179
+ * The root wrapper supplies CSS custom properties only.
2180
+ */ AtomixGlassContainer.displayName = "AtomixGlassContainer";
2152
2181
 
2153
2182
  // Singleton instance
2154
2183
  const globalMouseTracker = new
@@ -2263,28 +2292,32 @@ class {
2263
2292
  */ getSubscriberCount() {
2264
2293
  return this.listeners.size;
2265
2294
  }
2266
- }, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
2295
+ }, {BORDER: BORDER, CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, BORDER_GRADIENT = BORDER.GRADIENT, WHITE = CONSTANTS$2.PALETTE.WHITE, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
2267
2296
  if (!wrapperElement && !containerElement) return;
2268
2297
  if (!validateGlassSize(params.glassSize)) return;
2269
- const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, elasticTranslation: elasticTranslation, elasticVelocity: elasticVelocity, mouseVelocity: mouseVelocity, directionalScale: directionalScale, scaleBase: scaleBase, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, isFixedOrSticky: isFixedOrSticky = !1} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
2298
+ const {mouseOffset: mouseOffset, globalMousePosition: globalMousePosition, glassSize: glassSize, isHovered: isHovered, isActive: isActive, isOverLight: isOverLight, baseOverLightConfig: baseOverLightConfig, effectiveBorderRadius: effectiveBorderRadius, effectiveWithoutEffects: effectiveWithoutEffects, effectiveReducedMotion: effectiveReducedMotion, elasticity: elasticity, elasticTranslation: elasticTranslation, elasticVelocity: elasticVelocity, mouseVelocity: mouseVelocity, directionalScale: directionalScale, scaleBase: scaleBase, onClick: onClick, withLiquidBlur: withLiquidBlur, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, isFixedOrSticky: isFixedOrSticky = !1, borderAnimated: borderAnimated = !0, borderOpacityMultiplier: borderOpacityMultiplier = 1} = params, mouseInfluence = calculateMouseInfluence(mouseOffset), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, overLightConfig = {
2270
2299
  opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
2271
2300
  contrast: Math.min(1.6, baseOverLightConfig.contrast + .1 * mouseInfluence),
2272
2301
  brightness: Math.min(1.1, baseOverLightConfig.brightness + .05 * mouseInfluence),
2273
2302
  shadowIntensity: Math.min(1.2, Math.max(.5, baseOverLightConfig.shadowIntensity + .2 * mouseInfluence)),
2274
2303
  borderOpacity: Math.min(1, Math.max(.3, baseOverLightConfig.borderOpacity + .1 * mouseInfluence)),
2275
2304
  saturationBoost: baseOverLightConfig.saturationBoost
2276
- }, scaleX = directionalScale.x * scaleBase, scaleY = directionalScale.y * scaleBase, transformStyle = effectiveWithoutEffects ? `scale(${scaleBase})` : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) scaleX(${scaleX}) scaleY(${scaleY})`, stretchMagnitude = ((pos1, pos2) => {
2277
- if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
2278
- const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
2279
- return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
2280
- })({
2281
- x: 0,
2282
- y: 0
2283
- }, elasticTranslation), tensionFactor = smoothstep(stretchMagnitude / 80), lightingContrast = Math.min(1.8, overLightConfig.contrast + .2 * tensionFactor), lightingBrightness = Math.min(1.2, overLightConfig.brightness + .1 * tensionFactor);
2305
+ }, scaleX = directionalScale.x * scaleBase, scaleY = directionalScale.y * scaleBase, transformStyle = effectiveWithoutEffects ? `scale(${scaleBase})` : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) scaleX(${scaleX}) scaleY(${scaleY})`, tensionFactor =
2306
+ /**
2307
+ * Computes tension factor from elastic translation magnitude (0–1).
2308
+ */
2309
+ function(elasticTranslation) {
2310
+ const magnitude = Math.hypot(elasticTranslation.x, elasticTranslation.y);
2311
+ return smoothstep(magnitude / 80);
2312
+ }
2313
+ /**
2314
+ * Updates the styles of the AtomixGlass wrapper and container elements imperatively
2315
+ * to avoid React re-renders on mouse movement.
2316
+ */ (elasticTranslation), lightingContrast = Math.min(1.8, overLightConfig.contrast + .2 * tensionFactor), lightingBrightness = Math.min(1.2, overLightConfig.brightness + .1 * tensionFactor);
2284
2317
  // Calculate mouse influence
2285
2318
  // Update Wrapper Styles (glassVars)
2286
2319
  if (wrapperElement) {
2287
- const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, velocityRotation = (mouseVelocity.x + elasticVelocity.x) * (GRADIENT.VELOCITY_ANGLE_MULTIPLIER || 2.5), borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER + velocityRotation, chromaticOffset = GRADIENT.CHROMATIC_OFFSET || 1.5, angleR = borderGradientAngle - chromaticOffset, angleB = borderGradientAngle + chromaticOffset, borderStop1 = Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER), borderStop2 = Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER), tensionGlow = 1 + .5 * tensionFactor, borderOpacities = [ (GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW) * tensionGlow, (GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH) * tensionGlow ], configBorderOpacity = overLightConfig.borderOpacity, whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, hoverPositions = {
2320
+ const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT, whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, hoverPositions = {
2288
2321
  hover1: {
2289
2322
  x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
2290
2323
  y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
@@ -2301,28 +2334,55 @@ class {
2301
2334
  x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
2302
2335
  y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER
2303
2336
  }, opacityValues = {
2304
- hover1: isHovered || isActive ? .5 : 0,
2305
- hover2: isActive ? .5 : 0,
2306
- hover3: isHovered ? .4 : isActive ? .8 : 0,
2307
- base: isOverLight ? overLightConfig.opacity : 0,
2308
- over: isOverLight ? 1.1 * overLightConfig.opacity : 0
2337
+ // hover-1: ambient highlight glow present on hover and during press
2338
+ hover1: isHovered || isActive ? 1 : 0,
2339
+ // hover-2: press depth shadow only fires on active (mousedown)
2340
+ hover2: isActive ? 1 : 0,
2341
+ // hover-3: global soft-light surface shift half-strength on hover, full on press
2342
+ hover3: isActive ? 1 : isHovered ? .55 : 0,
2343
+ // Dark chrome: faint smoky tint; over-light keeps stronger fill
2344
+ base: isOverLight ? overLightConfig.opacity : .14,
2345
+ over: isOverLight ? 1.1 * overLightConfig.opacity : .1
2309
2346
  }, style = wrapperElement.style;
2310
2347
  style.setProperty("--atomix-glass-transform", transformStyle || "none");
2311
2348
  // Parallax for content (liquid refraction feel)
2312
2349
  const parallaxFactor = .38 + .12 * tensionFactor;
2313
2350
  style.setProperty("--atomix-glass-child-parallax", `translate(${elasticTranslation.x * -parallaxFactor}px, ${elasticTranslation.y * -parallaxFactor}px)`),
2314
- style.setProperty("--atomix-glass-contrast", lightingContrast.toString()), style.setProperty("--atomix-glass-brightness", lightingBrightness.toString()),
2351
+ style.setProperty("--atomix-glass-contrast", lightingContrast.toString()), style.setProperty("--atomix-glass-brightness", lightingBrightness.toString());
2315
2352
  // ── Chromatic Rim Lighting ──────────────────────────────────────
2316
- // Layer 1: Core White/Blue highlight
2317
- style.setProperty("--atomix-glass-border-gradient-1", `linear-gradient(${angleB}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`),
2318
- // Layer 2: Subtle Red/Warm highlight (offset angle)
2319
- style.setProperty("--atomix-glass-border-gradient-2", `linear-gradient(${angleR}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`),
2320
- // Hover gradients
2321
- style.setProperty("--atomix-glass-hover-1-gradient", isOverLight ? `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_STOP}%)`),
2322
- style.setProperty("--atomix-glass-hover-2-gradient", isOverLight ? `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`),
2323
- style.setProperty("--atomix-glass-hover-3-gradient", isOverLight ? `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`),
2324
- style.setProperty("--atomix-glass-base-gradient", isOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`),
2325
- style.setProperty("--atomix-glass-overlay-gradient", isOverLight ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`),
2353
+ const borderVars = ATOMIX_GLASS.BORDER.GRADIENT_CSS_VARS;
2354
+ if (borderAnimated && !effectiveWithoutEffects) {
2355
+ const borderCssVars =
2356
+ /**
2357
+ * Builds animated chromatic rim CSS variables for border layers 1 and 2.
2358
+ * When empty, SCSS static conic/linear fallbacks apply.
2359
+ */
2360
+ function(params) {
2361
+ const {mouseOffset: mouseOffset, mouseVelocity: mouseVelocity, elasticVelocity: elasticVelocity, borderOpacity: borderOpacity, opacityMultiplier: opacityMultiplier = 1, tensionFactor: tensionFactor = 0} = params, mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), velocityRotation = (mouseVelocity.x + elasticVelocity.x) * BORDER_GRADIENT.VELOCITY_ANGLE_MULTIPLIER, borderGradientAngle = BORDER_GRADIENT.BASE_ANGLE + mx * BORDER_GRADIENT.ANGLE_MULTIPLIER + velocityRotation, chromaticOffset = BORDER_GRADIENT.CHROMATIC_OFFSET, angleR = borderGradientAngle - chromaticOffset, angleB = borderGradientAngle + chromaticOffset, borderStop1 = Math.max(BORDER_GRADIENT.STOP_1.MIN, BORDER_GRADIENT.STOP_1.BASE + my * BORDER_GRADIENT.STOP_1.MULTIPLIER), borderStop2 = Math.min(BORDER_GRADIENT.STOP_2.MAX, BORDER_GRADIENT.STOP_2.BASE + my * BORDER_GRADIENT.STOP_2.MULTIPLIER), tensionGlow = 1 + .5 * tensionFactor, opacities = BORDER_GRADIENT.OPACITY, borderOpacities = [ (opacities.BASE_1 + absMx * opacities.MULTIPLIER_LOW) * tensionGlow, (opacities.BASE_2 + absMx * opacities.MULTIPLIER_HIGH) * tensionGlow, (opacities.BASE_3 + absMx * opacities.MULTIPLIER_LOW) * tensionGlow, (opacities.BASE_4 + absMx * opacities.MULTIPLIER_HIGH) * tensionGlow ], configBorderOpacity = borderOpacity * opacityMultiplier, gradient1 = `linear-gradient(${angleB}deg, rgba(${WHITE}, 0) 0%, rgba(${WHITE}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${WHITE}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${WHITE}, 0) 100%)`, gradient2 = `linear-gradient(${angleR}deg, rgba(${WHITE}, 0) 0%, rgba(${WHITE}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${WHITE}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${WHITE}, 0) 100%)`;
2362
+ return {
2363
+ [BORDER.GRADIENT_CSS_VARS.GRADIENT_1]: gradient1,
2364
+ [BORDER.GRADIENT_CSS_VARS.GRADIENT_2]: gradient2
2365
+ };
2366
+ }({
2367
+ mouseOffset: mouseOffset,
2368
+ mouseVelocity: mouseVelocity,
2369
+ elasticVelocity: elasticVelocity,
2370
+ borderOpacity: overLightConfig.borderOpacity,
2371
+ opacityMultiplier: borderOpacityMultiplier,
2372
+ tensionFactor: tensionFactor
2373
+ });
2374
+ style.setProperty(borderVars.GRADIENT_1, borderCssVars[borderVars.GRADIENT_1] ?? ""),
2375
+ style.setProperty(borderVars.GRADIENT_2, borderCssVars[borderVars.GRADIENT_2] ?? "");
2376
+ } else style.removeProperty(borderVars.GRADIENT_1), style.removeProperty(borderVars.GRADIENT_2);
2377
+ // Hover gradients — cursor-relative radial positions for realistic light tracking.
2378
+ // hover-1: white overlay highlight following cursor (works on both dark + light)
2379
+ style.setProperty("--atomix-glass-hover-1-gradient", `radial-gradient(65% 55% at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${whiteColor}, 0.24) 0%, rgba(${whiteColor}, 0.06) 45%, rgba(${whiteColor}, 0) 72%)`),
2380
+ // hover-2: press depth — darkens at cursor with multiply blend, isOverLight uses stronger black
2381
+ style.setProperty("--atomix-glass-hover-2-gradient", isOverLight ? `radial-gradient(60% 50% at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, 0.22) 0%, rgba(${blackColor}, 0.06) 50%, rgba(${blackColor}, 0) 72%)` : `radial-gradient(60% 50% at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, 0.18) 0%, rgba(${blackColor}, 0.04) 50%, rgba(${blackColor}, 0) 72%)`),
2382
+ // hover-3: full-surface soft-light tint; linear gradient angled with cursor X
2383
+ style.setProperty("--atomix-glass-hover-3-gradient", `linear-gradient(${150 + .3 * mx}deg, rgba(${whiteColor}, ${isOverLight ? .08 : .12}) 0%, rgba(${whiteColor}, 0.04) 55%, rgba(${whiteColor}, 0) 100%)`),
2384
+ style.setProperty("--atomix-glass-base-gradient", isOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `linear-gradient(180deg, rgba(${blackColor}, 0.42) 0%, rgba(${blackColor}, 0.22) 55%, rgba(${blackColor}, 0.12) 100%)`),
2385
+ style.setProperty("--atomix-glass-overlay-gradient", isOverLight ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `radial-gradient(120% 80% at 50% 0%, rgba(${whiteColor}, 0.14) 0%, rgba(${whiteColor}, 0) 55%)`),
2326
2386
  // Opacities
2327
2387
  style.setProperty("--atomix-glass-hover-1-opacity", opacityValues.hover1.toString()),
2328
2388
  style.setProperty("--atomix-glass-hover-2-opacity", opacityValues.hover2.toString()),
@@ -2359,21 +2419,17 @@ class {
2359
2419
  backdropFilterString = !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? `blur(${clampBlur(Math.max(liquidBlur.baseBlur, .8 * liquidBlur.edgeBlur, 1.1 * liquidBlur.centerBlur, .9 * liquidBlur.flowBlur))}px) saturate(${Math.min(dynamicSaturation, 250)}%) contrast(${lightingContrast}) brightness(${lightingBrightness})` : `blur(${clampBlur(.4 * liquidBlur.baseBlur + .25 * liquidBlur.edgeBlur + .15 * liquidBlur.centerBlur + .2 * liquidBlur.flowBlur)}px) saturate(${Math.min(dynamicSaturation, 250)}%) contrast(${lightingContrast}) brightness(${lightingBrightness})`;
2360
2420
  // Container variables
2361
2421
  const style = containerElement.style;
2362
- style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
2422
+ style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
2363
2423
  style.setProperty("--atomix-glass-container-backdrop", backdropFilterString),
2364
2424
  // Shadows
2365
- style.setProperty("--atomix-glass-container-shadow", isOverLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.4 + .002 * mx) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.2 + .001 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.08 + .001 * Math.abs(mx + my)) * (overLightConfig.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.12 + .002 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})` ].join(", ") : "0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset"),
2425
+ style.setProperty("--atomix-glass-container-shadow", isOverLight ? [ `inset 0 1px 0 rgba(255, 255, 255, ${(.35 + .002 * mx) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 -1px 0 rgba(0, 0, 0, ${(.15 + .001 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})`, `inset 0 0 20px rgba(0, 0, 0, ${(.06 + .001 * Math.abs(mx + my)) * (overLightConfig.shadowIntensity || 1)})`, `0 2px 12px rgba(0, 0, 0, ${(.08 + .002 * Math.abs(my)) * (overLightConfig.shadowIntensity || 1)})` ].join(", ") : ATOMIX_GLASS.CONSTANTS.CONTAINER_SHADOW.LIGHT),
2366
2426
  style.setProperty("--atomix-glass-container-shadow-opacity", effectiveWithoutEffects ? "0" : "1"),
2367
2427
  style.setProperty("--atomix-glass-container-bg", isOverLight ? `linear-gradient(${180 + .5 * mx}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)` : "none"),
2368
2428
  style.setProperty("--atomix-glass-container-text-shadow", isOverLight ? "0px 1px 2px rgba(255, 255, 255, 0.15)" : "0px 2px 12px rgba(0, 0, 0, 0.4)"),
2369
- style.setProperty("--atomix-glass-container-box-shadow", isOverLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)");
2429
+ style.setProperty("--atomix-glass-container-box-shadow", isOverLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0 8px 32px rgba(0, 0, 0, 0.32), 0 2px 8px rgba(0, 0, 0, 0.18)");
2370
2430
  }
2371
2431
  };
2372
2432
 
2373
- /**
2374
- * Updates the styles of the AtomixGlass wrapper and container elements imperatively
2375
- * to avoid React re-renders on mouse movement.
2376
- */
2377
2433
  /**
2378
2434
  * Animation System for AtomixGlass Component
2379
2435
  *
@@ -2518,12 +2574,38 @@ const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new Weak
2518
2574
  * Composable hook for AtomixGlass component logic
2519
2575
  * Manages all state, calculations, and event handlers
2520
2576
  */
2521
- function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, padding: padding, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1, priority: priority = 1, withTimeAnimation:
2577
+ function useAtomixGlass({glassRef: glassRef, contentRef: contentRef, wrapperRef: wrapperRef, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, border: border, withBorder: withBorder = !0, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, onClick: onClick, debugBorderRadius: debugBorderRadius = !1, debugOverLight: debugOverLight = !1, children: children, blurAmount: blurAmount, saturation: saturation, withLiquidBlur: withLiquidBlur, isFixedOrSticky: isFixedOrSticky = !1, priority: priority = 1, withTimeAnimation:
2522
2578
  // Default priority
2523
2579
  // Phase 1: Animation System Props
2524
2580
  withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED, withMultiLayerDistortion: withMultiLayerDistortion = ATOMIX_GLASS.DEFAULTS.WITH_MULTI_LAYER_DISTORTION, distortionOctaves: distortionOctaves = ATOMIX_GLASS.DEFAULTS.DISTORTION_OCTAVES, distortionLacunarity: distortionLacunarity = ATOMIX_GLASS.DEFAULTS.DISTORTION_LACUNARITY, distortionGain: distortionGain = ATOMIX_GLASS.DEFAULTS.DISTORTION_GAIN, distortionQuality: distortionQuality = ATOMIX_GLASS.DEFAULTS.DISTORTION_QUALITY}) {
2525
2581
  // State
2526
- const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), cachedRectRef = useRef(null), internalGlobalMousePositionRef = useRef({
2582
+ const [isHovered, setIsHovered] = useState(!1), [isActive, setIsActive] = useState(!1), resolvedBorder = useMemo((() =>
2583
+ /**
2584
+ * Resolves `border` and legacy `withBorder` into a single configuration object.
2585
+ */
2586
+ function(border, withBorder) {
2587
+ const legacyDefault = withBorder ?? !0;
2588
+ return void 0 === border ? {
2589
+ enabled: legacyDefault,
2590
+ width: BORDER.DEFAULT_WIDTH,
2591
+ opacityMultiplier: 1,
2592
+ animated: !0
2593
+ } : "boolean" == typeof border ? {
2594
+ enabled: border,
2595
+ width: BORDER.DEFAULT_WIDTH,
2596
+ opacityMultiplier: 1,
2597
+ animated: !0
2598
+ } : {
2599
+ enabled: border.enabled ?? legacyDefault,
2600
+ width: (value = border.width, void 0 === value ? BORDER.DEFAULT_WIDTH : "number" == typeof value ? `${value}px` : value),
2601
+ opacityMultiplier: border.opacity ?? 1,
2602
+ animated: !1 !== border.animated
2603
+ };
2604
+ /**
2605
+ * Formats border width for CSS custom properties.
2606
+ */
2607
+ var value;
2608
+ }(border, withBorder)), [ border, withBorder ]), cachedRectRef = useRef(null), internalGlobalMousePositionRef = useRef({
2527
2609
  x: 0,
2528
2610
  y: 0
2529
2611
  }), internalMouseOffsetRef = useRef({
@@ -2818,21 +2900,22 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2818
2900
  isOverLight: isOverLight,
2819
2901
  threshold: .7,
2820
2902
  opacity: isOverLight ? Math.min(.6, Math.max(.2, .5 * hoverIntensity * activeIntensity)) : 0,
2821
- contrast: 1.4,
2822
- brightness: .9,
2823
- saturationBoost: 1.3,
2824
- // Fixed value dynamic saturation amplifies perceived displacement
2825
- shadowIntensity: .9,
2826
- borderOpacity: .7
2903
+ // Dark UI (Apple Music): neutral contrast + slight brightness lift
2904
+ contrast: isOverLight ? 1.4 : 1.02,
2905
+ brightness: isOverLight ? .9 : 1.02,
2906
+ saturationBoost: isOverLight ? 1.3 : 1,
2907
+ shadowIntensity: isOverLight ? .9 : 1,
2908
+ borderOpacity: isOverLight ? ATOMIX_GLASS.BORDER.OVER_LIGHT.opacity : ATOMIX_GLASS.BORDER.DARK.opacity
2827
2909
  };
2828
2910
  if ("object" == typeof overLight && null !== overLight) {
2829
- const objConfig = overLight, validatedThreshold = validateConfigValue(objConfig.threshold, .1, 1, baseConfig.threshold), validatedOpacity = validateConfigValue(objConfig.opacity, .1, 1, baseConfig.opacity), validatedContrast = validateConfigValue(objConfig.contrast, .5, 2.5, baseConfig.contrast), validatedBrightness = validateConfigValue(objConfig.brightness, .5, 2, baseConfig.brightness), validatedSaturationBoost = validateConfigValue(objConfig.saturationBoost, .5, 3, baseConfig.saturationBoost), finalConfig = {
2911
+ const objConfig = overLight, validatedThreshold = validateConfigValue(objConfig.threshold, .1, 1, baseConfig.threshold), validatedOpacity = validateConfigValue(objConfig.opacity, .1, 1, baseConfig.opacity), validatedContrast = validateConfigValue(objConfig.contrast, .5, 2.5, baseConfig.contrast), validatedBrightness = validateConfigValue(objConfig.brightness, .5, 2, baseConfig.brightness), validatedSaturationBoost = validateConfigValue(objConfig.saturationBoost, .5, 3, baseConfig.saturationBoost), validatedBorderOpacity = validateConfigValue(objConfig.borderOpacity, .1, 1, baseConfig.borderOpacity), finalConfig = {
2830
2912
  ...baseConfig,
2831
2913
  threshold: validatedThreshold,
2832
2914
  opacity: validatedOpacity * hoverIntensity * activeIntensity,
2833
2915
  contrast: validatedContrast,
2834
2916
  brightness: validatedBrightness,
2835
- saturationBoost: validatedSaturationBoost
2917
+ saturationBoost: validatedSaturationBoost,
2918
+ borderOpacity: validatedBorderOpacity
2836
2919
  };
2837
2920
  return "undefined" == typeof process || process.env, finalConfig;
2838
2921
  }
@@ -2930,12 +3013,13 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2930
3013
  withLiquidBlur: withLiquidBlur,
2931
3014
  blurAmount: blurAmount,
2932
3015
  saturation: saturation,
2933
- padding: padding,
2934
- isFixedOrSticky: isFixedOrSticky
3016
+ isFixedOrSticky: isFixedOrSticky,
3017
+ borderAnimated: resolvedBorder.animated,
3018
+ borderOpacityMultiplier: resolvedBorder.opacityMultiplier
2935
3019
  }), Math.abs(mouseVelocityRef.current.x) < .001 && Math.abs(mouseVelocityRef.current.y) < .001 && Math.abs(elasticVelocityRef.current.x) < .001 && Math.abs(elasticVelocityRef.current.y) < .001 && Math.abs(scaleVelocityRef.current.x) < .001 && Math.abs(scaleVelocityRef.current.y) < .001 && Math.abs(internalMouseOffsetRef.current.x - targetMouseOffsetRef.current.x) < .001 && Math.abs(internalMouseOffsetRef.current.y - targetMouseOffsetRef.current.y) < .001 ? stopLerpLoop() : lerpRafRef.current = requestAnimationFrame(tick);
2936
3020
  };
2937
3021
  lerpRafRef.current = requestAnimationFrame(tick);
2938
- }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky, stopLerpLoop ]), handleGlobalMousePosition = useCallback((globalPos => {
3022
+ }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, isFixedOrSticky, resolvedBorder.animated, resolvedBorder.opacityMultiplier, stopLerpLoop ]), handleGlobalMousePosition = useCallback((globalPos => {
2939
3023
  if (externalGlobalMousePosition && externalMouseOffset) return;
2940
3024
  if (effectiveWithoutEffects) return;
2941
3025
  const container = mouseContainer?.current || glassRef.current;
@@ -2999,9 +3083,10 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2999
3083
  withLiquidBlur: withLiquidBlur,
3000
3084
  blurAmount: blurAmount,
3001
3085
  saturation: saturation,
3002
- padding: padding
3086
+ borderAnimated: resolvedBorder.animated,
3087
+ borderOpacityMultiplier: resolvedBorder.opacityMultiplier
3003
3088
  });
3004
- }), [ isHovered, isActive, glassSize, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, wrapperRef, glassRef, externalMouseOffset, externalGlobalMousePosition, withLiquidBlur, blurAmount, saturation, padding, onClick ]);
3089
+ }), [ isHovered, isActive, glassSize, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, wrapperRef, glassRef, externalMouseOffset, externalGlobalMousePosition, withLiquidBlur, blurAmount, saturation, resolvedBorder.animated, resolvedBorder.opacityMultiplier, onClick ]);
3005
3090
  // Event handlers
3006
3091
  const handleMouseEnter = useCallback((() => setIsHovered(!0)), []), handleMouseLeave = useCallback((() => setIsHovered(!1)), []), handleMouseDown = useCallback((() => setIsActive(!0)), []), handleMouseUp = useCallback((() => setIsActive(!1)), []), handleKeyDown = useCallback((e => {
3007
3092
  !onClick || "Enter" !== e.key && " " !== e.key || (e.preventDefault(), onClick());
@@ -3021,6 +3106,7 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
3021
3106
  mouseOffset: mouseOffset,
3022
3107
  // This is now static (refs or props) unless prop changes
3023
3108
  overLightConfig: overLightConfig,
3109
+ resolvedBorder: resolvedBorder,
3024
3110
  transformStyle: transformStyle,
3025
3111
  getShaderTime: getShaderTime,
3026
3112
  applyTimeBasedDistortion: applyTimeBasedDistortion,
@@ -3171,148 +3257,6 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
3171
3257
  /**
3172
3258
  * Get GPU memory info if available (Chrome DevTools only)
3173
3259
  */
3174
- /** Map an FPS value to a semantic color token string. */
3175
- const getQualityColor = quality => {
3176
- switch (quality) {
3177
- case "high":
3178
- return "var(--atomix-color-success, #4ade80)";
3179
-
3180
- case "medium":
3181
- return "var(--atomix-color-warning, #fbbf24)";
3182
-
3183
- case "low":
3184
- return "var(--atomix-color-danger, #ef4444)";
3185
-
3186
- default:
3187
- return "#9ca3af";
3188
- }
3189
- }, getFpsLabel = fps => fps >= 58 ? "Optimal" : fps >= 45 ? "Warning" : "Critical";
3190
-
3191
- /** Map a quality level string to a semantic color token string. */
3192
- // Inject keyframes once
3193
- if ("undefined" != typeof document) {
3194
- const styleId = "perf-dashboard-keyframes";
3195
- if (!document.getElementById(styleId)) {
3196
- const styleEl = document.createElement("style");
3197
- styleEl.id = styleId, styleEl.textContent = "\n@keyframes perf-dashboard-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n",
3198
- document.head.appendChild(styleEl);
3199
- }
3200
- }
3201
-
3202
- /**
3203
- * PerformanceDashboard - Real-time performance monitoring overlay.
3204
- *
3205
- * Displays FPS, frame time, quality level, GPU memory, and auto-scaling status.
3206
- * Rendered only when `debugPerformance={true}` on the parent `AtomixGlass`.
3207
- */ const PerformanceDashboard = memo((({metrics: metrics, isVisible: isVisible = !0, onClose: onClose}) => {
3208
- if (!isVisible) return null;
3209
- const fpsColor = (fps = metrics.fps) >= 58 ? "var(--atomix-color-success, #4ade80)" : fps >= 45 ? "var(--atomix-color-warning, #fbbf24)" : "var(--atomix-color-danger, #ef4444)";
3210
- var fps;
3211
- const isCritical = metrics.fps < 45;
3212
- return jsxs("div", {
3213
- className: "c-perf-dashboard u-position-fixed u-top-4 u-end-4 u-p-3 u-px-4 u-text-xs u-font-mono u-text-white u-rounded-md u-border u-border-white-alpha-10 u-shadow-lg",
3214
- style: {
3215
- zIndex: 9999,
3216
- minWidth: "12.5rem",
3217
- // 200px
3218
- backgroundColor: "rgba(17, 24, 39, 0.95)",
3219
- backdropFilter: "blur(8px)",
3220
- transition: "opacity 0.3s ease"
3221
- },
3222
- children: [ jsxs("div", {
3223
- className: "u-flex u-items-center u-justify-between u-mb-2 u-pb-2 u-border-b u-border-white-alpha-10",
3224
- children: [ jsx("span", {
3225
- className: "u-text-sm u-font-bold u-text-white",
3226
- children: "Performance Monitor"
3227
- }), onClose && jsx("button", {
3228
- className: "u-bg-transparent u-border-none u-p-0 u-line-height-1 u-text-base u-text-gray-400 u-cursor-pointer hover:u-text-white",
3229
- onClick: onClose,
3230
- "aria-label": "Close performance dashboard",
3231
- style: {
3232
- transition: "color 0.2s ease"
3233
- },
3234
- children: "×"
3235
- }) ]
3236
- }), jsxs("div", {
3237
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
3238
- children: [ jsx("span", {
3239
- className: "u-text-gray-400 u-me-3",
3240
- children: "FPS"
3241
- }), jsx("span", {
3242
- className: "u-font-bold",
3243
- style: {
3244
- color: fpsColor
3245
- },
3246
- children: Math.round(metrics.fps)
3247
- }) ]
3248
- }), jsxs("div", {
3249
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
3250
- children: [ jsx("span", {
3251
- className: "u-text-gray-400 u-me-3",
3252
- children: "Frame Time"
3253
- }), jsxs("span", {
3254
- className: "u-font-bold",
3255
- children: [ metrics.frameTime.toFixed(2), "ms" ]
3256
- }) ]
3257
- }), jsxs("div", {
3258
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
3259
- children: [ jsx("span", {
3260
- className: "u-text-gray-400 u-me-3",
3261
- children: "Quality"
3262
- }), jsx("span", {
3263
- className: "u-font-bold u-text-uppercase",
3264
- style: {
3265
- fontSize: "0.6875rem",
3266
- // 11px
3267
- color: getQualityColor(metrics.qualityLevel)
3268
- },
3269
- children: metrics.qualityLevel
3270
- }) ]
3271
- }), metrics.gpuMemory && jsxs("div", {
3272
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
3273
- children: [ jsx("span", {
3274
- className: "u-text-gray-400 u-me-3",
3275
- children: "GPU Memory"
3276
- }), jsxs("span", {
3277
- className: "u-font-bold",
3278
- children: [ "~", Math.round(metrics.gpuMemory / 1024), "MB" ]
3279
- }) ]
3280
- }), metrics.isAutoScaling && jsx("div", {
3281
- className: "u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10 u-text-center",
3282
- style: {
3283
- fontSize: "0.625rem",
3284
- // 10px
3285
- color: "#6b7280"
3286
- },
3287
- children: "Auto-scaling active"
3288
- }), jsxs("div", {
3289
- className: "u-flex u-items-center u-gap-2 u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10",
3290
- children: [ jsx("div", {
3291
- className: "u-rounded-full",
3292
- style: {
3293
- width: "0.5rem",
3294
- height: "0.5rem",
3295
- flexShrink: 0,
3296
- backgroundColor: fpsColor,
3297
- ...isCritical && {
3298
- animation: "perf-dashboard-pulse 1s infinite"
3299
- }
3300
- }
3301
- }), jsx("span", {
3302
- className: "u-text-xs",
3303
- style: {
3304
- fontSize: "0.625rem",
3305
- // 10px
3306
- color: fpsColor
3307
- },
3308
- children: getFpsLabel(metrics.fps)
3309
- }) ]
3310
- }) ]
3311
- });
3312
- }));
3313
-
3314
- PerformanceDashboard.displayName = "PerformanceDashboard";
3315
-
3316
3260
  /**
3317
3261
  * Mobile optimization presets
3318
3262
  *
@@ -3432,18 +3376,13 @@ const PERFORMANCE_PRESET = {
3432
3376
  saturation: 70
3433
3377
  }
3434
3378
  }
3435
- }, AtomixGlassInner = forwardRef((function({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", padding: padding = ATOMIX_GLASS.DEFAULTS.PADDING, overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, withBorder: withBorder = !0, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugOverLight: debugOverLight = !1, height: height, width: width, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", devicePreset: devicePreset = "balanced", disableResponsiveBreakpoints: disableResponsiveBreakpoints = !1, isFixedOrSticky: propsIsFixedOrSticky, ...rest}, ref) {
3436
- const glassRef = useRef(null), contentRef = useRef(null), internalWrapperRef = useRef(null), mergedRef = useMemo((() =>
3437
- // Helper to merge refs
3438
- function(...refs) {
3439
- return node => {
3440
- refs.forEach((ref => {
3441
- "function" == typeof ref ? ref(node) : null != ref && (ref.current = node);
3442
- }));
3443
- };
3444
- }
3445
- // Internal implementation with forwardRef
3446
- (ref, internalWrapperRef)), [ ref ]), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = propsIsFixedOrSticky || "fixed" === restStyle.position || "sticky" === restStyle.position, {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, getShaderTime: getShaderTime, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown} = useAtomixGlass({
3379
+ }, AtomixGlassInner = forwardRef((function({children: children, displacementScale: displacementScale = ATOMIX_GLASS.DEFAULTS.DISPLACEMENT_SCALE, blurAmount: blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT, saturation: saturation = ATOMIX_GLASS.DEFAULTS.SATURATION, aberrationIntensity: aberrationIntensity = ATOMIX_GLASS.DEFAULTS.ABERRATION_INTENSITY, elasticity: elasticity = ATOMIX_GLASS.DEFAULTS.ELASTICITY, borderRadius: borderRadius, globalMousePosition: externalGlobalMousePosition, mouseOffset: externalMouseOffset, mouseContainer: mouseContainer = null, className: className = "", overLight: overLight = ATOMIX_GLASS.DEFAULTS.OVER_LIGHT, style: style = {}, mode: mode = ATOMIX_GLASS.DEFAULTS.MODE, onClick: onClick, shaderVariant: shaderVariant = "liquidGlass", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, reducedMotion: reducedMotion = !1, highContrast: highContrast = !1, withoutEffects: withoutEffects = !1, withLiquidBlur: withLiquidBlur = !1, border: border, withBorder: withBorder = !0, debugBorderRadius: debugBorderRadius = !1, withOverLightLayers: withOverLightLayers = ATOMIX_GLASS.DEFAULTS.ENABLE_OVER_LIGHT_LAYERS, debugPerformance: debugPerformance = !1, debugOverLight: debugOverLight = !1, height: height, width: width, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", devicePreset: devicePreset = "balanced", disableResponsiveBreakpoints: disableResponsiveBreakpoints = !1, isFixedOrSticky: propsIsFixedOrSticky, ...rest}, ref) {
3380
+ const glassRef = useRef(null), contentRef = useRef(null), internalWrapperRef = useRef(null), mergedRef = useForkRef(ref, internalWrapperRef), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = (explicit = propsIsFixedOrSticky,
3381
+ position = restStyle.position, Boolean(explicit || "fixed" === position || "sticky" === position));
3382
+ var explicit, position;
3383
+ /**
3384
+ * Extracts layout-related properties from a React `CSSProperties` object.
3385
+ */ const {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, getShaderTime: getShaderTime, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown, resolvedBorder: resolvedBorder} = useAtomixGlass({
3447
3386
  glassRef: glassRef,
3448
3387
  contentRef: contentRef,
3449
3388
  wrapperRef: internalWrapperRef,
@@ -3463,10 +3402,11 @@ const PERFORMANCE_PRESET = {
3463
3402
  blurAmount: blurAmount,
3464
3403
  saturation: saturation,
3465
3404
  withLiquidBlur: withLiquidBlur,
3466
- padding: padding,
3405
+ border: border,
3406
+ withBorder: withBorder,
3407
+ debugBorderRadius: debugBorderRadius,
3467
3408
  style: style,
3468
3409
  isFixedOrSticky: isFixedOrSticky,
3469
- // Phase 1: Animation System props
3470
3410
  withTimeAnimation: withTimeAnimation,
3471
3411
  animationSpeed: animationSpeed,
3472
3412
  withMultiLayerDistortion: withMultiLayerDistortion,
@@ -3475,8 +3415,7 @@ const PERFORMANCE_PRESET = {
3475
3415
  distortionGain: distortionGain,
3476
3416
  distortionQuality: distortionQuality
3477
3417
  });
3478
- // Responsive breakpoint system - automatically adjusts parameters based on viewport
3479
- !
3418
+ (
3480
3419
  /**
3481
3420
  * Responsive Glass Parameters Hook
3482
3421
  *
@@ -3631,7 +3570,7 @@ const PERFORMANCE_PRESET = {
3631
3570
  }), [ enabled ]), useCallback((() => {
3632
3571
  calculateParams();
3633
3572
  }), [ calculateParams ]);
3634
- }({
3573
+ })({
3635
3574
  baseParams: {
3636
3575
  ...useMemo((() =>
3637
3576
  /**
@@ -3665,9 +3604,7 @@ const PERFORMANCE_PRESET = {
3665
3604
  breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
3666
3605
  enabled: !disableResponsiveBreakpoints && "undefined" != typeof window,
3667
3606
  debug: !1
3668
- });
3669
- // Performance monitoring - tracks FPS, frame time, memory usage
3670
- const {metrics: performanceMetrics, toggleMonitoring: toggleMonitoring} =
3607
+ }),
3671
3608
  /**
3672
3609
  * Performance Monitor Hook
3673
3610
  *
@@ -3702,7 +3639,13 @@ const PERFORMANCE_PRESET = {
3702
3639
  timestamp: 0,
3703
3640
  isAutoScaling: !0,
3704
3641
  lowFpsCount: 0
3705
- }), [manualOverride, setManualOverride] = useState(!1), [isEnabled, setIsEnabled] = useState(enabled), frameCountRef = useRef(0), lastFpsUpdateRef = useRef(0), lastFrameTimeRef = useRef(0), animationFrameRef = useRef(null), lowFpsCountRef = useRef(0), highFpsCountRef = useRef(0), qualityLevelRef = useRef("medium"), updateMetrics = useCallback((newMetrics => {
3642
+ }), [manualOverride, setManualOverride] = useState(!1), [isEnabled, setIsEnabled] = useState(enabled);
3643
+ // Sync external `enabled` prop changes into internal state
3644
+ useEffect((() => {
3645
+ setIsEnabled(enabled ?? !0);
3646
+ }), [ enabled ]);
3647
+ // Refs for frame tracking
3648
+ const frameCountRef = useRef(0), lastFpsUpdateRef = useRef(0), lastFrameTimeRef = useRef(0), animationFrameRef = useRef(null), lowFpsCountRef = useRef(0), highFpsCountRef = useRef(0), qualityLevelRef = useRef("medium"), updateMetrics = useCallback((newMetrics => {
3706
3649
  setMetrics((prev => ({
3707
3650
  ...prev,
3708
3651
  ...newMetrics,
@@ -3822,191 +3765,214 @@ const PERFORMANCE_PRESET = {
3822
3765
  /**
3823
3766
  * Reset to auto-scaling mode
3824
3767
  */ var fps, currentQuality;
3825
- return {
3826
- metrics: metrics,
3827
- recommendedQuality: (fps = metrics.fps, currentQuality = metrics.qualityLevel, fps >= 58 ? "high" : fps >= 45 ? "high" === currentQuality ? "high" : "medium" : "low"),
3828
- isUnderperforming: metrics.fps < minFps,
3829
- setQualityLevel: setQualityLevel,
3830
- resetAutoScaling: resetAutoScaling,
3831
- toggleMonitoring: toggleMonitoring
3832
- };
3768
+ fps = metrics.fps, currentQuality = metrics.qualityLevel, metrics.fps;
3833
3769
  }({
3834
3770
  enabled: debugPerformance,
3835
- // Enable when debugPerformance is true
3836
3771
  debug: !1,
3837
3772
  showOverlay: !1
3838
3773
  });
3839
- // Auto-start performance monitoring when debugPerformance is enabled
3840
- React.useEffect((() => {
3841
- debugPerformance && toggleMonitoring();
3842
- // eslint-disable-next-line react-hooks/exhaustive-deps
3843
- }), [ debugPerformance ]);
3844
- // Re-run when debugPerformance changes
3845
- const isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, rootLayoutStyle = useMemo((() => {
3846
- if (!isFixedOrSticky) return {};
3847
- const {position: p, top: t, left: l, right: r, bottom: b} = restStyle;
3848
- return {
3849
- ...p && {
3850
- position: p
3851
- },
3852
- ...void 0 !== t && {
3853
- top: t
3854
- },
3855
- ...void 0 !== l && {
3856
- left: l
3857
- },
3858
- ...void 0 !== r && {
3859
- right: r
3860
- },
3861
- ...void 0 !== b && {
3862
- bottom: b
3863
- }
3864
- };
3865
- }), [ isFixedOrSticky, restStyle ]);
3866
- // Calculate base style with transforms
3867
- // When layout is hoisted to the root, strip those props from the container
3868
- useMemo((() => {
3869
- if (isFixedOrSticky) {
3870
- const {position: _p, top: _t, left: _l, right: _r, bottom: _b, ...visualStyle} = restStyle;
3871
- return {
3872
- ...visualStyle
3873
- };
3774
+ const isOverLight = useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, containerStyle = useMemo((() => ({
3775
+ ...restStyle,
3776
+ ...void 0 !== customZIndex && {
3777
+ zIndex: customZIndex
3874
3778
  }
3779
+ })), [ restStyle, customZIndex ]), componentClassName = mergeClassNames(ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveWithoutEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className), positionStyles = useMemo((() =>
3780
+ /**
3781
+ * Returns the internal positioning context for effect layers relative to the root.
3782
+ */
3783
+ function(isFixedOrSticky, restStyle) {
3875
3784
  return {
3876
- ...restStyle
3785
+ position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
3786
+ top: 0,
3787
+ left: 0,
3788
+ right: "auto",
3789
+ bottom: "auto"
3877
3790
  };
3878
- }), [ isFixedOrSticky, restStyle ]);
3879
- // Build className with state modifiers
3880
- const componentClassName = [ ATOMIX_GLASS.BASE_CLASS, effectiveReducedMotion && `${ATOMIX_GLASS.BASE_CLASS}--reduced-motion`, effectiveHighContrast && `${ATOMIX_GLASS.BASE_CLASS}--high-contrast`, effectiveWithoutEffects && `${ATOMIX_GLASS.BASE_CLASS}--disabled-effects`, className ].filter(Boolean).join(" "), positionStyles = useMemo((() => ({
3881
- position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
3882
- top: isFixedOrSticky ? restStyle.top ?? 0 : 0,
3883
- left: isFixedOrSticky ? restStyle.left ?? 0 : 0,
3884
- right: isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
3885
- bottom: isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto"
3886
- })), [ isFixedOrSticky, restStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), adjustedSize = useMemo((() => {
3887
- // Keep a reference to positionStyles to avoid unused-variable lint,
3888
- // but sizing is driven by explicit width/height or measured size.
3889
- positionStyles.position;
3890
- const resolveLength = (value, measured) => void 0 !== value && isFixedOrSticky ? "number" == typeof value ? `${value}px` : value : measured > 0 && isFixedOrSticky ? `${measured}px` : "100%", effectiveWidth = width ?? restStyle.width, effectiveHeight = height ?? restStyle.height;
3791
+ }
3792
+ /**
3793
+ * Computes `--atomix-glass-width` and `--atomix-glass-height` values.
3794
+ *
3795
+ * Fixed/sticky elements prefer explicit dimensions or measured size; in-flow
3796
+ * elements default to `100%`.
3797
+ */ (isFixedOrSticky, restStyle)), [ isFixedOrSticky, restStyle ]), adjustedSize = useMemo((() => function(options) {
3798
+ const {width: width, height: height, restStyle: restStyle, glassSize: glassSize, isFixedOrSticky: isFixedOrSticky} = options, resolveLength = (value, measured) => void 0 !== value && isFixedOrSticky ? "number" == typeof value ? `${value}px` : value : measured > 0 && isFixedOrSticky ? `${measured}px` : "100%", effectiveWidth = width ?? restStyle.width, effectiveHeight = height ?? restStyle.height;
3891
3799
  return {
3892
3800
  width: resolveLength(effectiveWidth, glassSize.width),
3893
3801
  height: resolveLength(effectiveHeight, glassSize.height)
3894
3802
  };
3895
- }), [ width, height, restStyle.width, restStyle.height, positionStyles.position, glassSize.width, glassSize.height, isFixedOrSticky ]), gradientValues = useMemo((() => {
3896
- const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
3897
- return {
3898
- borderGradientAngle: GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER,
3899
- borderStop1: Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER),
3900
- borderStop2: Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER),
3901
- borderOpacities: [ GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH, GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW, GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH ],
3902
- hoverPositions: {
3903
- hover1: {
3904
- x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
3905
- y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
3906
- },
3907
- hover2: {
3908
- x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2,
3909
- y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2
3910
- },
3911
- hover3: {
3912
- x: GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
3913
- y: GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3
3914
- }
3915
- },
3916
- basePosition: {
3917
- x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
3918
- y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER
3919
- },
3920
- mx: mx,
3921
- my: my,
3922
- absMx: absMx,
3923
- absMy: absMy
3924
- };
3925
- }), [ mouseOffset.x, mouseOffset.y ]), clampedOverLightOpacity = Math.max(0, Math.min(1, overLightConfig?.opacity ?? .4)), clampedBorderOpacity = Math.max(0, Math.min(1, overLightConfig?.borderOpacity ?? 1)), opacityValues = useMemo((() => ({
3926
- hover1: isHovered || isActive ? .5 : 0,
3927
- hover2: isActive ? .5 : 0,
3928
- hover3: isHovered ? .4 : isActive ? .8 : 0,
3929
- base: isOverLight ? clampedOverLightOpacity || .4 : 0,
3930
- over: isOverLight ? 1.1 * (clampedOverLightOpacity || .4) : 0
3931
- })), [ isHovered, isActive, isOverLight, clampedOverLightOpacity ]), glassVars = useMemo((() => {
3932
- const whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE, blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK, {borderGradientAngle: borderGradientAngle, borderStop1: borderStop1, borderStop2: borderStop2, borderOpacities: borderOpacities, hoverPositions: hoverPositions, basePosition: basePosition, mx: mx, my: my, absMx: absMx, absMy: absMy} = gradientValues;
3803
+ }
3804
+ /**
3805
+ * Builds the CSS custom properties applied to the root `.c-atomix-glass` element.
3806
+ *
3807
+ * These variables drive layer geometry, transforms, and stacking offsets. They
3808
+ * must not include layout properties that would interfere with backdrop-filter.
3809
+ */ ({
3810
+ width: width,
3811
+ height: height,
3812
+ restStyle: restStyle,
3813
+ glassSize: glassSize,
3814
+ isFixedOrSticky: isFixedOrSticky
3815
+ })), [ width, height, restStyle, glassSize, isFixedOrSticky ]), glassVars = useMemo((() => function(input) {
3816
+ const {effectiveBorderRadius: effectiveBorderRadius, transformStyle: transformStyle, adjustedSize: adjustedSize, isOverLight: isOverLight, customZIndex: customZIndex, isFixedOrSticky: isFixedOrSticky, positionStyles: positionStyles, restStyle: restStyle, borderWidth: borderWidth = ATOMIX_GLASS.BORDER.DEFAULT_WIDTH} = input, layerPosition =
3817
+ /**
3818
+ * Resolves the `--atomix-glass-position` value for decorative layers.
3819
+ *
3820
+ * Fixed/sticky layers use the same positioning mode as the container; in-flow
3821
+ * layers default to the internal absolute positioning context.
3822
+ */
3823
+ function(isFixedOrSticky, positionStyles, restStyle) {
3824
+ return isFixedOrSticky ? `${function(style) {
3825
+ const {position: position, top: top, left: left, right: right, bottom: bottom, inset: inset} = style;
3826
+ return {
3827
+ ...null != position && {
3828
+ position: position
3829
+ },
3830
+ ...void 0 !== top && {
3831
+ top: top
3832
+ },
3833
+ ...void 0 !== left && {
3834
+ left: left
3835
+ },
3836
+ ...void 0 !== right && {
3837
+ right: right
3838
+ },
3839
+ ...void 0 !== bottom && {
3840
+ bottom: bottom
3841
+ },
3842
+ ...void 0 !== inset && {
3843
+ inset: inset
3844
+ }
3845
+ };
3846
+ }
3847
+ /**
3848
+ * Resolves inset custom properties for decorative layers (hover, borders, backgrounds).
3849
+ *
3850
+ * For fixed and sticky modes, insets mirror the container so sibling layers remain
3851
+ * aligned. In-flow modes, insets follow the consumer `style` when a non-default
3852
+ * `position` is provided.
3853
+ */ (restStyle).position ?? restStyle.position ?? "fixed"}` : `${positionStyles.position}`;
3854
+ }(isFixedOrSticky, positionStyles, restStyle), layerInsets = function(isFixedOrSticky, restStyle) {
3855
+ if (isFixedOrSticky) return {
3856
+ top: formatGlassInsetValue(restStyle.top, 0),
3857
+ left: formatGlassInsetValue(restStyle.left, 0),
3858
+ right: formatGlassInsetValue(restStyle.right, "auto"),
3859
+ bottom: formatGlassInsetValue(restStyle.bottom, "auto")
3860
+ };
3861
+ const position = restStyle.position;
3862
+ return null != position && "static" !== position && "relative" !== position ? {
3863
+ top: formatGlassInsetValue(restStyle.top, 0),
3864
+ left: formatGlassInsetValue(restStyle.left, 0),
3865
+ right: formatGlassInsetValue(restStyle.right, "auto"),
3866
+ bottom: formatGlassInsetValue(restStyle.bottom, "auto")
3867
+ } : {
3868
+ top: "0px",
3869
+ left: "0px",
3870
+ right: "auto",
3871
+ bottom: "auto"
3872
+ };
3873
+ }(isFixedOrSticky, restStyle);
3933
3874
  return {
3934
3875
  ...void 0 !== customZIndex && {
3935
3876
  "--atomix-glass-base-z-index": customZIndex
3936
3877
  },
3937
3878
  "--atomix-glass-radius": `${effectiveBorderRadius}px`,
3938
3879
  "--atomix-glass-transform": transformStyle || "none",
3939
- "--atomix-glass-container-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
3940
- "--atomix-glass-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
3941
- "--atomix-glass-top": `${isFixedOrSticky ? restStyle.top ?? 0 : 0}px`,
3942
- "--atomix-glass-left": `${isFixedOrSticky ? restStyle.left ?? 0 : 0}px`,
3943
- "--atomix-glass-right": isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
3944
- "--atomix-glass-bottom": isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto",
3880
+ "--atomix-glass-container-position": layerPosition,
3881
+ "--atomix-glass-position": layerPosition,
3882
+ "--atomix-glass-top": layerInsets.top,
3883
+ "--atomix-glass-left": layerInsets.left,
3884
+ "--atomix-glass-right": layerInsets.right,
3885
+ "--atomix-glass-bottom": layerInsets.bottom,
3945
3886
  "--atomix-glass-width": adjustedSize.width,
3946
3887
  "--atomix-glass-height": adjustedSize.height,
3947
- "--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.125rem)",
3948
- "--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay",
3949
- "--atomix-glass-border-gradient-1": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * clampedBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * clampedBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
3950
- "--atomix-glass-border-gradient-2": `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * clampedBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * clampedBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`,
3951
- "--atomix-glass-hover-1-opacity": opacityValues.hover1,
3952
- "--atomix-glass-hover-1-gradient": isOverLight ? `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_STOP}%)`,
3953
- "--atomix-glass-hover-2-opacity": opacityValues.hover2,
3954
- "--atomix-glass-hover-2-gradient": isOverLight ? `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`,
3955
- "--atomix-glass-hover-3-opacity": opacityValues.hover3,
3956
- "--atomix-glass-hover-3-gradient": isOverLight ? `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)` : `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`,
3957
- "--atomix-glass-base-opacity": opacityValues.base,
3958
- "--atomix-glass-base-gradient": isOverLight ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`,
3959
- "--atomix-glass-overlay-opacity": opacityValues.over,
3960
- "--atomix-glass-overlay-gradient": isOverLight ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)` : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`,
3961
- "--atomix-glass-overlay-highlight-opacity": opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
3962
- "--atomix-glass-overlay-highlight-bg": `radial-gradient(circle at ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_X}% ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.POSITION_Y}%, rgba(255, 255, 255, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.WHITE_OPACITY}) 0%, transparent ${ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.STOP}%)`
3888
+ // Aliases maintained for backward compatibility and consumer overrides.
3889
+ "--atomix-glass-container-width": adjustedSize.width,
3890
+ "--atomix-glass-container-height": adjustedSize.height,
3891
+ [ATOMIX_GLASS.BORDER.WIDTH_CSS_VAR]: borderWidth,
3892
+ "--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay"
3963
3893
  };
3964
- }), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, clampedBorderOpacity, customZIndex, isFixedOrSticky, positionStyles.position, rootLayoutStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), renderBackgroundLayer = layerType => jsx("div", {
3965
- className: [ ATOMIX_GLASS.BACKGROUND_LAYER_CLASS, "dark" === layerType ? ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS, isOverLight ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS ].filter(Boolean).join(" ")
3894
+ }
3895
+ /**
3896
+ * Applies mode-specific multipliers and accessibility overrides to container effects.
3897
+ */ ({
3898
+ effectiveBorderRadius: effectiveBorderRadius,
3899
+ transformStyle: transformStyle,
3900
+ adjustedSize: adjustedSize,
3901
+ isOverLight: isOverLight,
3902
+ customZIndex: customZIndex,
3903
+ isFixedOrSticky: isFixedOrSticky,
3904
+ positionStyles: positionStyles,
3905
+ restStyle: restStyle,
3906
+ borderWidth: resolvedBorder.width
3907
+ })), [ effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, customZIndex, isFixedOrSticky, positionStyles, restStyle, resolvedBorder.width ]), containerEffects = useMemo((() => function(options) {
3908
+ const {MULTIPLIERS: MULTIPLIERS, SATURATION: SATURATION} = ATOMIX_GLASS.CONSTANTS, zeroMouse = {
3909
+ x: 0,
3910
+ y: 0
3911
+ }, resolveSaturation = () => options.effectiveHighContrast ? SATURATION.HIGH_CONTRAST : options.isOverLight ? options.saturation * options.saturationBoost : options.saturation;
3912
+ if (options.effectiveWithoutEffects) return {
3913
+ displacementScale: 0,
3914
+ blurAmount: 0,
3915
+ saturation: resolveSaturation(),
3916
+ aberrationIntensity: 0,
3917
+ mouseOffset: zeroMouse,
3918
+ globalMousePosition: zeroMouse
3919
+ };
3920
+ let resolvedDisplacement = options.displacementScale;
3921
+ "shader" === options.mode ? resolvedDisplacement *= MULTIPLIERS.SHADER_DISPLACEMENT : options.isOverLight && (resolvedDisplacement *= MULTIPLIERS.OVER_LIGHT_DISPLACEMENT);
3922
+ let resolvedAberration = options.aberrationIntensity;
3923
+ return "shader" === options.mode && (resolvedAberration *= MULTIPLIERS.SHADER_ABERRATION),
3924
+ {
3925
+ displacementScale: resolvedDisplacement,
3926
+ blurAmount: options.blurAmount,
3927
+ saturation: resolveSaturation(),
3928
+ aberrationIntensity: resolvedAberration,
3929
+ mouseOffset: options.mouseOffset,
3930
+ globalMousePosition: options.globalMousePosition
3931
+ };
3932
+ }({
3933
+ displacementScale: displacementScale,
3934
+ blurAmount: blurAmount,
3935
+ saturation: saturation,
3936
+ aberrationIntensity: aberrationIntensity,
3937
+ mode: mode,
3938
+ effectiveWithoutEffects: effectiveWithoutEffects,
3939
+ effectiveHighContrast: effectiveHighContrast,
3940
+ isOverLight: isOverLight,
3941
+ saturationBoost: overLightConfig.saturationBoost,
3942
+ mouseOffset: mouseOffset,
3943
+ globalMousePosition: globalMousePosition
3944
+ })), [ displacementScale, blurAmount, saturation, aberrationIntensity, mode, effectiveWithoutEffects, effectiveHighContrast, isOverLight, overLightConfig.saturationBoost, mouseOffset, globalMousePosition ]), renderBackgroundLayer = layerType => jsx("div", {
3945
+ "aria-hidden": "true",
3946
+ className: mergeClassNames(ATOMIX_GLASS.BACKGROUND_LAYER_CLASS, "dark" === layerType ? ATOMIX_GLASS.BACKGROUND_LAYER_DARK_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_BLACK_CLASS, isOverLight ? ATOMIX_GLASS.BACKGROUND_LAYER_OVER_LIGHT_CLASS : ATOMIX_GLASS.BACKGROUND_LAYER_HIDDEN_CLASS)
3966
3947
  });
3967
- // Calculate position and size styles for internal layers
3968
- // When root is fixed/sticky, internal layers use absolute (relative to root)
3969
- return jsxs("div", {
3948
+ return jsxs("div", {
3970
3949
  ...rest,
3971
3950
  ref: mergedRef,
3972
3951
  className: componentClassName,
3973
- style: {
3974
- ...glassVars
3975
- },
3952
+ style: glassVars,
3976
3953
  role: role || (onClick ? "button" : void 0),
3977
3954
  tabIndex: onClick ? tabIndex ?? 0 : tabIndex,
3978
3955
  "aria-label": ariaLabel,
3979
3956
  "aria-describedby": ariaDescribedBy,
3980
3957
  "aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
3981
- "aria-pressed": onClick ? isActive : void 0,
3982
3958
  onKeyDown: onClick ? handleKeyDown : void 0,
3983
3959
  children: [ jsx(AtomixGlassContainer, {
3984
3960
  ref: glassRef,
3985
3961
  contentRef: contentRef,
3986
3962
  className: className,
3987
- style: {
3988
- ...restStyle
3989
- },
3963
+ style: containerStyle,
3990
3964
  borderRadius: effectiveBorderRadius,
3991
- displacementScale: effectiveWithoutEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
3992
- blurAmount: effectiveWithoutEffects ? 0 : blurAmount,
3993
- saturation: effectiveHighContrast ? ATOMIX_GLASS.CONSTANTS.SATURATION.HIGH_CONTRAST : isOverLight ? saturation * overLightConfig.saturationBoost : saturation,
3994
- aberrationIntensity: effectiveWithoutEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
3965
+ displacementScale: containerEffects.displacementScale,
3966
+ blurAmount: containerEffects.blurAmount,
3967
+ saturation: containerEffects.saturation,
3968
+ aberrationIntensity: containerEffects.aberrationIntensity,
3995
3969
  glassSize: glassSize,
3996
- padding: padding,
3997
- mouseOffset: effectiveWithoutEffects ? {
3998
- x: 0,
3999
- y: 0
4000
- } : mouseOffset,
4001
- globalMousePosition: effectiveWithoutEffects ? {
4002
- x: 0,
4003
- y: 0
4004
- } : globalMousePosition,
3970
+ mouseOffset: containerEffects.mouseOffset,
3971
+ globalMousePosition: containerEffects.globalMousePosition,
4005
3972
  onMouseEnter: handleMouseEnter,
4006
3973
  onMouseLeave: handleMouseLeave,
4007
3974
  onMouseDown: handleMouseDown,
4008
3975
  onMouseUp: handleMouseUp,
4009
- isHovered: isHovered,
4010
3976
  isActive: isActive,
4011
3977
  overLight: isOverLight,
4012
3978
  overLightConfig: {
@@ -4022,7 +3988,6 @@ const PERFORMANCE_PRESET = {
4022
3988
  shaderVariant: shaderVariant,
4023
3989
  withLiquidBlur: withLiquidBlur,
4024
3990
  isFixedOrSticky: isFixedOrSticky,
4025
- // Phase 1: Animation System props
4026
3991
  shaderTime: getShaderTime(),
4027
3992
  withTimeAnimation: withTimeAnimation,
4028
3993
  animationSpeed: animationSpeed,
@@ -4034,32 +3999,39 @@ const PERFORMANCE_PRESET = {
4034
3999
  children: children
4035
4000
  }), Boolean(onClick) && jsxs(Fragment, {
4036
4001
  children: [ jsx("div", {
4002
+ "aria-hidden": "true",
4037
4003
  className: ATOMIX_GLASS.HOVER_1_CLASS
4038
4004
  }), jsx("div", {
4005
+ "aria-hidden": "true",
4039
4006
  className: ATOMIX_GLASS.HOVER_2_CLASS
4040
4007
  }), jsx("div", {
4008
+ "aria-hidden": "true",
4041
4009
  className: ATOMIX_GLASS.HOVER_3_CLASS
4042
4010
  }) ]
4043
- }), renderBackgroundLayer("dark"), renderBackgroundLayer("black"), shouldRenderOverLightLayers && jsxs(Fragment, {
4011
+ }), [ "dark", "black" ].map((layerType => jsx(React.Fragment, {
4012
+ children: renderBackgroundLayer(layerType)
4013
+ }, layerType))), shouldRenderOverLightLayers && jsxs(Fragment, {
4044
4014
  children: [ jsx("div", {
4015
+ "aria-hidden": "true",
4045
4016
  className: ATOMIX_GLASS.BASE_LAYER_CLASS
4046
4017
  }), jsx("div", {
4018
+ "aria-hidden": "true",
4047
4019
  className: ATOMIX_GLASS.OVERLAY_LAYER_CLASS
4048
4020
  }), jsx("div", {
4021
+ "aria-hidden": "true",
4049
4022
  className: ATOMIX_GLASS.OVERLAY_HIGHLIGHT_CLASS
4050
4023
  }) ]
4051
- }), withBorder && jsxs(Fragment, {
4024
+ }), resolvedBorder.enabled && jsxs(Fragment, {
4052
4025
  children: [ jsx("span", {
4026
+ "aria-hidden": "true",
4053
4027
  className: ATOMIX_GLASS.BORDER_BACKDROP_CLASS
4054
4028
  }), jsx("span", {
4029
+ "aria-hidden": "true",
4055
4030
  className: ATOMIX_GLASS.BORDER_1_CLASS
4056
4031
  }), jsx("span", {
4032
+ "aria-hidden": "true",
4057
4033
  className: ATOMIX_GLASS.BORDER_2_CLASS
4058
4034
  }) ]
4059
- }), debugPerformance && performanceMetrics && jsx(PerformanceDashboard, {
4060
- metrics: performanceMetrics,
4061
- isVisible: !0,
4062
- onClose: () => {}
4063
4035
  }) ]
4064
4036
  });
4065
4037
  }));
@@ -4069,10 +4041,7 @@ const PERFORMANCE_PRESET = {
4069
4041
  * Default preset for most mobile devices
4070
4042
  */ AtomixGlassInner.displayName = "AtomixGlass";
4071
4043
 
4072
- /**
4073
- * AtomixGlass - wrapped with React.memo to prevent unnecessary re-renders.
4074
- * Ref is forwarded to the root `<div>` element.
4075
- */
4044
+ /** Memoized public export. Ref targets the root `.c-atomix-glass` wrapper. */
4076
4045
  const AtomixGlass = memo(AtomixGlassInner), ChartContext = createContext(null), Chart = memo( forwardRef((({children: children, type: type = "line", size: size = "md", variant: variant = "primary", title: title, subtitle: subtitle, loading: loading = !1, error: error, className: className = "", "aria-label": ariaLabel, onFullscreen: onFullscreen, onExport: onExport, onRefresh: onRefresh, showToolbar: showToolbar = !1, enableFullscreen: enableFullscreen = !1, enableExport: enableExport = !1, enableRefresh: enableRefresh = !1, exportFormats: exportFormats = [ "png", "svg", "csv" ], datasets: datasets, config: config,
4077
4046
  // Destructure non-DOM props to prevent passing to DOM element
4078
4047
  toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, customToolbarGroups: customToolbarGroups, data: data, showLegend: showLegend, interactive: interactive, fullscreen: fullscreen, onDataPointClick: onDataPointClick, onLegendItemClick: onLegendItemClick, glass: glass, ...props}, ref) => {