@shohojdhara/atomix 0.6.4 → 0.6.6

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 (83) hide show
  1. package/dist/atomix.css +117 -38
  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 +625 -846
  10. package/dist/charts.js.map +1 -1
  11. package/dist/core.d.ts +30 -1
  12. package/dist/core.js +659 -873
  13. package/dist/core.js.map +1 -1
  14. package/dist/forms.d.ts +30 -1
  15. package/dist/forms.js +1171 -1402
  16. package/dist/forms.js.map +1 -1
  17. package/dist/heavy.d.ts +31 -89
  18. package/dist/heavy.js +975 -1195
  19. package/dist/heavy.js.map +1 -1
  20. package/dist/index.d.ts +383 -140
  21. package/dist/index.esm.js +1567 -1679
  22. package/dist/index.esm.js.map +1 -1
  23. package/dist/index.js +1556 -1667
  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 -364
  31. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +32 -251
  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 +456 -22
  38. package/src/components/AtomixGlass/shader-utils.ts +19 -77
  39. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +158 -537
  40. package/src/components/AtomixGlass/stories/Border.stories.tsx +149 -0
  41. package/src/components/AtomixGlass/stories/Examples.stories.tsx +229 -89
  42. package/src/components/AtomixGlass/stories/Playground.stories.tsx +29 -340
  43. package/src/components/AtomixGlass/stories/argTypes.ts +30 -13
  44. package/src/components/AtomixGlass/stories/premium-presets.ts +206 -0
  45. package/src/components/AtomixGlass/stories/shared-components.tsx +52 -8
  46. package/src/components/Badge/Badge.tsx +4 -4
  47. package/src/components/Button/Button.tsx +2 -6
  48. package/src/components/Callout/Callout.test.tsx +4 -3
  49. package/src/components/Callout/Callout.tsx +2 -5
  50. package/src/components/Dropdown/Dropdown.tsx +3 -7
  51. package/src/components/Form/Checkbox.tsx +2 -8
  52. package/src/components/Form/Input.tsx +2 -9
  53. package/src/components/Form/Radio.tsx +2 -9
  54. package/src/components/Form/Select.test.tsx +6 -6
  55. package/src/components/Form/Select.tsx +2 -7
  56. package/src/components/Form/Textarea.stories.tsx +5 -5
  57. package/src/components/Form/Textarea.tsx +2 -9
  58. package/src/components/Messages/Messages.tsx +2 -8
  59. package/src/components/Modal/Modal.tsx +4 -5
  60. package/src/components/Navigation/Nav/Nav.tsx +2 -6
  61. package/src/components/Navigation/Navbar/Navbar.tsx +2 -9
  62. package/src/components/Navigation/SideMenu/SideMenu.tsx +2 -6
  63. package/src/components/Pagination/Pagination.tsx +2 -10
  64. package/src/components/Popover/Popover.tsx +2 -9
  65. package/src/components/Progress/Progress.tsx +2 -7
  66. package/src/components/Rating/Rating.tsx +2 -10
  67. package/src/components/Spinner/Spinner.tsx +2 -7
  68. package/src/components/Steps/Steps.tsx +2 -10
  69. package/src/components/Tabs/Tabs.tsx +2 -9
  70. package/src/components/Toggle/Toggle.tsx +2 -10
  71. package/src/components/Tooltip/Tooltip.tsx +2 -5
  72. package/src/lib/composables/useAtomixGlass.ts +42 -143
  73. package/src/lib/composables/useAtomixGlassStyles.ts +61 -77
  74. package/src/lib/composables/usePerformanceMonitor.ts +5 -66
  75. package/src/lib/constants/components.ts +363 -46
  76. package/src/lib/types/components.ts +33 -1
  77. package/src/styles/01-settings/_settings.atomix-glass.scss +66 -28
  78. package/src/styles/02-tools/_tools.button.scss +51 -42
  79. package/src/styles/02-tools/_tools.glass.scss +45 -3
  80. package/src/styles/06-components/_components.atomix-glass.scss +116 -79
  81. package/src/components/AtomixGlass/PerformanceDashboard.tsx +0 -171
  82. package/src/components/AtomixGlass/animation-system.ts +0 -578
  83. package/src/components/AtomixGlass/deprecated/AtomixGlass.deprecated.tsx +0 -390
package/dist/index.js CHANGED
@@ -1710,6 +1710,268 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1710
1710
  NEWSLETTER_BUTTON_TEXT: "Subscribe",
1711
1711
  BACK_TO_TOP_TEXT: "Back to Top"
1712
1712
  }
1713
+ }, GLASS_DEFAULTS = {
1714
+ /**
1715
+ * Button — compact interactive element.
1716
+ * Low displacement + no blur override so the backdrop token handles frosting.
1717
+ * Zero elasticity: the button itself handles press/hover feedback.
1718
+ */
1719
+ BUTTON: {
1720
+ displacementScale: 16,
1721
+ saturation: 180,
1722
+ elasticity: 0
1723
+ },
1724
+ /**
1725
+ * Badge — pill-shaped tag. Tiny surface, minimal distortion.
1726
+ * borderRadius is set dynamically at runtime; this serves as a safe fallback.
1727
+ */
1728
+ BADGE: {
1729
+ displacementScale: 14,
1730
+ borderRadius: 16,
1731
+ elasticity: 0
1732
+ },
1733
+ /**
1734
+ * Accordion — block-level content container.
1735
+ * Gentle displacement; zero elasticity so open/close animation isn't jittery.
1736
+ */
1737
+ ACCORDION: {
1738
+ displacementScale: 18,
1739
+ elasticity: 0
1740
+ },
1741
+ /**
1742
+ * Callout — notification / alert banner.
1743
+ * Slightly more frosted than Accordion to give it visual priority.
1744
+ */
1745
+ CALLOUT: {
1746
+ displacementScale: 22,
1747
+ borderRadius: 8,
1748
+ elasticity: 0
1749
+ },
1750
+ /**
1751
+ * Dropdown — floating menu. Small surface, should feel light and airy.
1752
+ * No blur override — rely on the backdrop token.
1753
+ */
1754
+ DROPDOWN: {
1755
+ displacementScale: 16,
1756
+ elasticity: 0
1757
+ },
1758
+ /**
1759
+ * EdgePanel — full-height side drawer.
1760
+ * Empty config: the panel inherits all ATOMIX_GLASS.DEFAULTS.
1761
+ * borderRadius and size are managed by the panel's own SCSS.
1762
+ */
1763
+ EDGE_PANEL: {},
1764
+ /**
1765
+ * Modal — full-screen overlay dialog. Shader mode for premium refraction.
1766
+ * displacementScale omitted — set dynamically from modal content height at runtime.
1767
+ * elasticity: 0 — modals should not wobble when the user interacts with them.
1768
+ */
1769
+ MODAL: {
1770
+ blurAmount: 28,
1771
+ elasticity: 0,
1772
+ mode: "shader",
1773
+ shaderMode: "premiumGlass"
1774
+ },
1775
+ /**
1776
+ * Navbar — sticky / fixed navigation bar spanning full width.
1777
+ * Shader gives the top edge a polished lens look.
1778
+ * borderRadius: 0 — navbars run edge-to-edge.
1779
+ * elasticity: 0 — no wobble on a fixed chrome element.
1780
+ */
1781
+ NAVBAR: {
1782
+ displacementScale: 24,
1783
+ blurAmount: 24,
1784
+ borderRadius: 0,
1785
+ elasticity: 0,
1786
+ mode: "shader",
1787
+ shaderVariant: "premiumGlass"
1788
+ },
1789
+ /**
1790
+ * Nav (inline nav list) — narrower than a full Navbar, standard mode is
1791
+ * enough. Rounded corners to match typical pill / tab nav designs.
1792
+ */
1793
+ NAV: {
1794
+ displacementScale: 24,
1795
+ blurAmount: 20,
1796
+ borderRadius: 8,
1797
+ elasticity: 0,
1798
+ mode: "shader"
1799
+ },
1800
+ /**
1801
+ * SideMenu — persistent sidebar navigation.
1802
+ * Moderate displacement; shader for depth. Zero elasticity.
1803
+ */
1804
+ SIDE_MENU: {
1805
+ displacementScale: 24,
1806
+ blurAmount: 24,
1807
+ borderRadius: 0,
1808
+ elasticity: 0,
1809
+ mode: "shader"
1810
+ },
1811
+ /**
1812
+ * Popover — small floating card above content.
1813
+ * Shader mode + aberration for the characteristic lens fringe.
1814
+ * Low displacement so the content remains readable.
1815
+ */
1816
+ POPOVER: {
1817
+ displacementScale: 22,
1818
+ blurAmount: 20,
1819
+ saturation: 180,
1820
+ aberrationIntensity: .35,
1821
+ borderRadius: 12,
1822
+ elasticity: 0,
1823
+ mode: "shader"
1824
+ },
1825
+ /**
1826
+ * Tooltip — tiny single-line label.
1827
+ * Higher displacement than other small elements because tooltips float
1828
+ * freely and the distortion reads as depth. No shader (too small to matter).
1829
+ */
1830
+ TOOLTIP: {
1831
+ displacementScale: 18,
1832
+ blurAmount: 16,
1833
+ elasticity: 0
1834
+ },
1835
+ /**
1836
+ * Tabs — tabbed content panel. Shader for the nav strip refraction.
1837
+ */
1838
+ TABS: {
1839
+ displacementScale: 24,
1840
+ blurAmount: 20,
1841
+ saturation: 180,
1842
+ aberrationIntensity: .35,
1843
+ borderRadius: 12,
1844
+ elasticity: 0,
1845
+ mode: "shader"
1846
+ },
1847
+ /**
1848
+ * Toggle — switch control. Compact, pill shape.
1849
+ * Relies on borderRadius from the component's own SCSS.
1850
+ */
1851
+ TOGGLE: {
1852
+ displacementScale: 16,
1853
+ elasticity: 0
1854
+ },
1855
+ /**
1856
+ * Form inputs (Input, Textarea, Select, Checkbox, Radio).
1857
+ * Unified glass feel for all form controls — subtle displacement,
1858
+ * rounded corners matching the $input-border-radius token (≈ 6px).
1859
+ */
1860
+ INPUT: {
1861
+ displacementScale: 18,
1862
+ borderRadius: 6,
1863
+ elasticity: 0
1864
+ },
1865
+ TEXTAREA: {
1866
+ displacementScale: 18,
1867
+ borderRadius: 6,
1868
+ elasticity: 0
1869
+ },
1870
+ SELECT: {
1871
+ displacementScale: 18,
1872
+ borderRadius: 6,
1873
+ elasticity: 0
1874
+ },
1875
+ CHECKBOX: {
1876
+ displacementScale: 14,
1877
+ borderRadius: 4,
1878
+ elasticity: 0
1879
+ },
1880
+ RADIO: {
1881
+ displacementScale: 14,
1882
+ borderRadius: 99,
1883
+ elasticity: 0
1884
+ },
1885
+ /**
1886
+ * Pagination — row of page buttons.
1887
+ * Thin, horizontal strip; low displacement so numbers stay legible.
1888
+ */
1889
+ PAGINATION: {
1890
+ displacementScale: 18,
1891
+ borderRadius: 8,
1892
+ elasticity: 0
1893
+ },
1894
+ /**
1895
+ * Progress — horizontal bar track.
1896
+ * Very low displacement — the bar should not distort.
1897
+ */
1898
+ PROGRESS: {
1899
+ displacementScale: 10,
1900
+ borderRadius: 99,
1901
+ elasticity: 0
1902
+ },
1903
+ /**
1904
+ * Rating — star picker.
1905
+ * Compact inline element; minimal glass, no shader needed.
1906
+ */
1907
+ RATING: {
1908
+ displacementScale: 16,
1909
+ elasticity: 0
1910
+ },
1911
+ /**
1912
+ * Spinner — loading indicator.
1913
+ * Barely-there glass; the spinner itself provides visual interest.
1914
+ */
1915
+ SPINNER: {
1916
+ displacementScale: 12,
1917
+ elasticity: 0
1918
+ },
1919
+ /**
1920
+ * Steps — horizontal / vertical stepper.
1921
+ * Moderate displacement so the connector lines remain crisp.
1922
+ */
1923
+ STEPS: {
1924
+ displacementScale: 22,
1925
+ borderRadius: 8,
1926
+ elasticity: 0
1927
+ },
1928
+ /**
1929
+ * Messages / chat bubbles.
1930
+ * Rounded pill-like bubbles; gentle glass to aid readability.
1931
+ */
1932
+ MESSAGES: {
1933
+ displacementScale: 20,
1934
+ borderRadius: 16,
1935
+ elasticity: 0
1936
+ }
1937
+ }, GLASS_BORDER_GRADIENT = {
1938
+ BASE_ANGLE: 135,
1939
+ ANGLE_MULTIPLIER: .5,
1940
+ VELOCITY_ANGLE_MULTIPLIER: .5,
1941
+ CHROMATIC_OFFSET: 1.5,
1942
+ STOP_1: {
1943
+ MIN: 10,
1944
+ BASE: 33,
1945
+ get MULTIPLIER() {
1946
+ return .009 * this.BASE;
1947
+ }
1948
+ },
1949
+ STOP_2: {
1950
+ MAX: 90,
1951
+ BASE: 66,
1952
+ get MULTIPLIER() {
1953
+ return .006 * this.BASE;
1954
+ }
1955
+ },
1956
+ OPACITY: {
1957
+ /** Matches $glass-border-1-opacity (0.08) */
1958
+ BASE_1: .08,
1959
+ get BASE_2() {
1960
+ return 3.33 * this.BASE_1;
1961
+ },
1962
+ get BASE_3() {
1963
+ return 2.66 * this.BASE_1;
1964
+ },
1965
+ get BASE_4() {
1966
+ return 5 * this.BASE_1;
1967
+ },
1968
+ get MULTIPLIER_LOW() {
1969
+ return .066 * this.BASE_1;
1970
+ },
1971
+ get MULTIPLIER_HIGH() {
1972
+ return .1 * this.BASE_1;
1973
+ }
1974
+ }
1713
1975
  }, ATOMIX_GLASS = {
1714
1976
  BASE_CLASS: "c-atomix-glass",
1715
1977
  CONTAINER_CLASS: "c-atomix-glass__container",
@@ -1721,6 +1983,22 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1721
1983
  BORDER_BACKDROP_CLASS: "c-atomix-glass__border-backdrop",
1722
1984
  BORDER_1_CLASS: "c-atomix-glass__border-1",
1723
1985
  BORDER_2_CLASS: "c-atomix-glass__border-2",
1986
+ /** Centralized liquid glass rim configuration */
1987
+ BORDER: {
1988
+ WIDTH_CSS_VAR: "--atomix-glass-border-width",
1989
+ DEFAULT_WIDTH: "0.5px",
1990
+ GRADIENT_CSS_VARS: {
1991
+ GRADIENT_1: "--atomix-glass-border-gradient-1",
1992
+ GRADIENT_2: "--atomix-glass-border-gradient-2"
1993
+ },
1994
+ GRADIENT: GLASS_BORDER_GRADIENT,
1995
+ OVER_LIGHT: {
1996
+ opacity: .7
1997
+ },
1998
+ DARK: {
1999
+ opacity: .35
2000
+ }
2001
+ },
1724
2002
  HOVER_1_CLASS: "c-atomix-glass__hover-1",
1725
2003
  HOVER_2_CLASS: "c-atomix-glass__hover-2",
1726
2004
  HOVER_3_CLASS: "c-atomix-glass__hover-3",
@@ -1749,25 +2027,22 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1749
2027
  SHADER: "c-atomix-glass--shader"
1750
2028
  },
1751
2029
  DEFAULTS: {
1752
- DISPLACEMENT_SCALE: 70,
2030
+ /** Subtle refraction — Apple UI chrome avoids heavy liquid distortion */
2031
+ DISPLACEMENT_SCALE: 28,
1753
2032
  get BLUR_AMOUNT() {
1754
- return .15 * this.DISPLACEMENT_SCALE;
1755
- // Dynamically computed based on displacement
1756
- },
1757
- get SATURATION() {
1758
- return 100 + .5 * this.DISPLACEMENT_SCALE;
1759
- // Saturate relative to intensity
1760
- },
2033
+ // Apple Music sidebar / player bar: ~20–40px frost (see $glass-backdrop-filter)
2034
+ return Math.max(20, .72 * this.DISPLACEMENT_SCALE);
2035
+ },
2036
+ /** Fixed 180% matches Apple's saturate(180%) backdrop recipe */
2037
+ SATURATION: 180,
1761
2038
  get ABERRATION_INTENSITY() {
1762
- return .03 * this.DISPLACEMENT_SCALE;
1763
- // Scale aberration with displacement
1764
- },
1765
- ELASTICITY: .15,
2039
+ return .02 * this.DISPLACEMENT_SCALE;
2040
+ },
2041
+ ELASTICITY: .05,
1766
2042
  get CORNER_RADIUS() {
1767
2043
  return 16;
1768
2044
  // Use 16 to match SCSS design system (was 20)
1769
2045
  },
1770
- PADDING: "0",
1771
2046
  MODE: "standard",
1772
2047
  OVER_LIGHT: !1,
1773
2048
  ENABLE_OVER_LIGHT_LAYERS: !0,
@@ -1783,19 +2058,29 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1783
2058
  },
1784
2059
  CONSTANTS: {
1785
2060
  ACTIVATION_ZONE: 200,
1786
- LERP_FACTOR: .08,
2061
+ LERP_FACTOR: .05,
2062
+ // Lower = more viscous, liquid-smooth tracking (Apple feel)
1787
2063
  SMOOTHSTEP_POWER: 2.5,
1788
2064
  MIN_BLUR: .1,
1789
2065
  MOUSE_INFLUENCE_DIVISOR: 100,
1790
2066
  EDGE_FADE_PIXELS: 2,
1791
- // Elasticity physics constants
1792
- ELASTICITY_TRANSLATION_FACTOR: .1,
2067
+ // Interaction intensity multipliers shared by the hook and the imperative style updater
2068
+ INTERACTION: {
2069
+ HOVER_INTENSITY: 1.4,
2070
+ ACTIVE_INTENSITY: 1.6
2071
+ },
2072
+ // Elasticity physics constants — Apple-tuned: soft springs, fast settling, minimal stretch
2073
+ ELASTICITY_TRANSLATION_FACTOR: .06,
2074
+ // Subtler elastic shift (was 0.1)
1793
2075
  ELASTICITY_DISTANCE_THRESHOLD: 200,
1794
2076
  ELASTICITY_COMPRESSION_FACTOR: .3,
1795
- ELASTICITY_STIFFNESS: .1,
1796
- ELASTICITY_DAMPING: .76,
2077
+ ELASTICITY_STIFFNESS: .06,
2078
+ // Softer springs = gentler motion (was 0.1)
2079
+ ELASTICITY_DAMPING: .88,
2080
+ // Fast settling, no wobble (was 0.76)
1797
2081
  ELASTICITY_VELOCITY_FACTOR: .65,
1798
- ELASTICITY_STRETCH_RATIO: .45,
2082
+ ELASTICITY_STRETCH_RATIO: .25,
2083
+ // Less visible surface tension stretch (was 0.45)
1799
2084
  ELASTICITY_MAGNIFICATION_BASE: 1.02,
1800
2085
  // Note: This default must match the SCSS variable --atomix-radius-md
1801
2086
  // @see src/styles/01-settings/_settings.global.scss
@@ -1809,55 +2094,16 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1809
2094
  },
1810
2095
  // Gradient calculation constants
1811
2096
  GRADIENT: {
1812
- BASE_ANGLE: 135,
1813
- // Base angle for border gradients (degrees)
1814
- ANGLE_MULTIPLIER: 1.2,
1815
- // Multiplier for mouse influence on angle
1816
- VELOCITY_ANGLE_MULTIPLIER: 2.5,
1817
- // How much velocity affects gradient rotation
1818
- CHROMATIC_OFFSET: 1.5,
1819
- // Degree offset for chromatic rim layers
1820
- BORDER_STOP_1: {
1821
- MIN: 10,
1822
- // Minimum percentage for border stop 1
1823
- BASE: 33,
1824
- // Base percentage for border stop 1
1825
- get MULTIPLIER() {
1826
- return .009 * this.BASE;
1827
- }
1828
- },
1829
- BORDER_STOP_2: {
1830
- MAX: 90,
1831
- // Maximum percentage for border stop 2
1832
- BASE: 66,
1833
- // Base percentage for border stop 2
1834
- get MULTIPLIER() {
1835
- return .006 * this.BASE;
1836
- }
1837
- },
1838
- BORDER_OPACITY: {
1839
- BASE_1: .12,
1840
- // Base opacity for border gradient 1
1841
- get BASE_2() {
1842
- return 3.33 * this.BASE_1;
1843
- },
1844
- // Base opacity for border gradient 2
1845
- get BASE_3() {
1846
- return 2.66 * this.BASE_1;
1847
- },
1848
- // Base opacity for border gradient 3
1849
- get BASE_4() {
1850
- return 5 * this.BASE_1;
1851
- },
1852
- // Base opacity for border gradient 4
1853
- get MULTIPLIER_LOW() {
1854
- return .066 * this.BASE_1;
1855
- },
1856
- // Low multiplier for mouse influence on opacity
1857
- get MULTIPLIER_HIGH() {
1858
- return .1 * this.BASE_1;
1859
- }
1860
- },
2097
+ BASE_ANGLE: GLASS_BORDER_GRADIENT.BASE_ANGLE,
2098
+ ANGLE_MULTIPLIER: GLASS_BORDER_GRADIENT.ANGLE_MULTIPLIER,
2099
+ VELOCITY_ANGLE_MULTIPLIER: GLASS_BORDER_GRADIENT.VELOCITY_ANGLE_MULTIPLIER,
2100
+ CHROMATIC_OFFSET: GLASS_BORDER_GRADIENT.CHROMATIC_OFFSET,
2101
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.STOP_1 */
2102
+ BORDER_STOP_1: GLASS_BORDER_GRADIENT.STOP_1,
2103
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.STOP_2 */
2104
+ BORDER_STOP_2: GLASS_BORDER_GRADIENT.STOP_2,
2105
+ /** @deprecated Use ATOMIX_GLASS.BORDER.GRADIENT.OPACITY */
2106
+ BORDER_OPACITY: GLASS_BORDER_GRADIENT.OPACITY,
1861
2107
  CENTER_POSITION: 50,
1862
2108
  // Center position percentage (50%)
1863
2109
  HOVER_POSITION: {
@@ -1890,8 +2136,8 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1890
2136
  return 2 * this.BLACK_STOP;
1891
2137
  },
1892
2138
  // End percentage for black hover 1
1893
- WHITE_START: .5,
1894
- // Start opacity for white hover 1
2139
+ WHITE_START: .35,
2140
+ // Gentler hover flash Apple hover is barely visible
1895
2141
  get WHITE_STOP() {
1896
2142
  return this.BLACK_END - 10;
1897
2143
  }
@@ -1909,8 +2155,8 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1909
2155
  return 2 * this.BLACK_STOP;
1910
2156
  },
1911
2157
  // End percentage for black hover 2
1912
- WHITE_START: 1,
1913
- // Start opacity for white hover 2
2158
+ WHITE_START: .7,
2159
+ // Gentler hover flash
1914
2160
  get WHITE_STOP() {
1915
2161
  return this.BLACK_END;
1916
2162
  }
@@ -1928,8 +2174,8 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1928
2174
  return 2 * this.BLACK_STOP;
1929
2175
  },
1930
2176
  // End percentage for black hover 3
1931
- WHITE_START: 1,
1932
- // Start opacity for white hover 3
2177
+ WHITE_START: .7,
2178
+ // Gentler hover flash
1933
2179
  get WHITE_STOP() {
1934
2180
  return this.BLACK_END;
1935
2181
  }
@@ -1939,13 +2185,13 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1939
2185
  BASE_GRADIENT: {
1940
2186
  ANGLE: 135,
1941
2187
  // Gradient angle in degrees
1942
- BLACK_START_BASE: .15,
2188
+ BLACK_START_BASE: .1,
1943
2189
  // Base start opacity for black
1944
2190
  get BLACK_START_MULTIPLIER() {
1945
2191
  return .02 * this.BLACK_START_BASE;
1946
2192
  },
1947
2193
  // Multiplier for mouse X influence on start
1948
- BLACK_MID_BASE: .1,
2194
+ BLACK_MID_BASE: .07,
1949
2195
  // Base mid opacity for black
1950
2196
  get BLACK_MID_MULTIPLIER() {
1951
2197
  return .02 * this.BLACK_MID_BASE;
@@ -1966,7 +2212,7 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1966
2212
  }
1967
2213
  },
1968
2214
  OVERLAY_GRADIENT: {
1969
- BLACK_START_BASE: .12,
2215
+ BLACK_START_BASE: .08,
1970
2216
  // Base start opacity for black overlay
1971
2217
  get BLACK_START_MULTIPLIER() {
1972
2218
  return .025 * this.BLACK_START_BASE;
@@ -1990,14 +2236,14 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
1990
2236
  return .416 * this.BLACK_START_BASE;
1991
2237
  }
1992
2238
  },
1993
- // Overlay highlight constants
2239
+ // Overlay highlight constants — Apple places specular at the top-center
1994
2240
  OVERLAY_HIGHLIGHT: {
1995
- POSITION_X: 20,
1996
- // X position percentage
1997
- POSITION_Y: 20,
1998
- // Y position percentage
1999
- WHITE_OPACITY: .4,
2000
- // White opacity in gradient
2241
+ POSITION_X: 50,
2242
+ // Centered horizontal — Apple's top-center specular
2243
+ POSITION_Y: 5,
2244
+ // Very top — catches light like a curved glass surface
2245
+ WHITE_OPACITY: .28,
2246
+ // Softer specular visible but not glaring
2001
2247
  get STOP() {
2002
2248
  return 150 * this.WHITE_OPACITY;
2003
2249
  },
@@ -2018,6 +2264,10 @@ const THEME_COLORS = [ "primary", "secondary", "success", "info", "warning", "er
2018
2264
  SATURATION: {
2019
2265
  HIGH_CONTRAST: 200
2020
2266
  },
2267
+ // Container shadows — hairline inner catch + soft floating lift (Apple player bar)
2268
+ CONTAINER_SHADOW: {
2269
+ LIGHT: "inset 0 0.5px 0 rgba(255, 255, 255, 0.32), inset 0 1px 2px rgba(255, 255, 255, 0.06), 0 4px 16px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.08)"
2270
+ },
2021
2271
  // Phase 1: Animation System Constants
2022
2272
  ANIMATION: {
2023
2273
  // Breathing effect timing (in milliseconds)
@@ -2114,120 +2364,472 @@ function useAccordion(initialProps) {
2114
2364
  };
2115
2365
  }
2116
2366
 
2117
- const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect => rect ? {
2118
- x: rect.left + rect.width / 2,
2119
- y: rect.top + rect.height / 2
2120
- } : {
2121
- x: 0,
2122
- y: 0
2123
- }, calculateMouseInfluence = mouseOffset => {
2124
- if (!mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y) return 0;
2125
- // Bounded calculation — keeps the glass effect subtle and stable
2126
- const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$2.MOUSE_INFLUENCE_DIVISOR;
2127
- return Math.min(.8, influence);
2128
- // Tighter cap to prevent blur/filter blow-out
2129
- }, 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 => {
2130
- if ("number" == typeof value) return Math.max(0, value);
2131
- if ("string" != typeof value || !value.trim()) return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
2132
- const trimmedValue = value.trim();
2133
- // Handle px values
2134
- if (trimmedValue.endsWith("px")) {
2135
- const parsed = parseFloat(trimmedValue);
2136
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
2137
- }
2138
- // Handle rem values (assume 16px = 1rem)
2139
- if (trimmedValue.endsWith("rem")) {
2140
- const parsed = parseFloat(trimmedValue);
2141
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
2142
- }
2143
- // Handle em values (assume 16px = 1em for simplicity)
2144
- if (trimmedValue.endsWith("em")) {
2145
- const parsed = parseFloat(trimmedValue);
2146
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
2147
- }
2148
- // Handle percentage (convert to approximate px value, assuming 200px container)
2149
- if (trimmedValue.endsWith("%")) {
2150
- const parsed = parseFloat(trimmedValue);
2151
- return isNaN(parsed) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
2152
- }
2153
- // Handle unitless numbers
2154
- const numValue = parseFloat(trimmedValue);
2155
- return isNaN(numValue) ? CONSTANTS$2.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
2156
- }, extractBorderRadiusFromElement = element => {
2157
- if (!element || !element.props) return null;
2158
- // Check inline styles first (highest priority)
2159
- if (element.props.style) {
2160
- const radiusFromStyle = (style => {
2161
- if (!style) return null;
2162
- // Check various border-radius properties
2163
- const borderRadius = style.borderRadius || style.borderTopLeftRadius || style.borderTopRightRadius || style.borderBottomLeftRadius || style.borderBottomRightRadius;
2164
- return void 0 !== borderRadius ? parseBorderRadiusValue(borderRadius) : null;
2165
- })(element.props.style);
2166
- if (null !== radiusFromStyle && radiusFromStyle > 0) return radiusFromStyle;
2167
- }
2168
- // If element has children, recursively check them
2169
- if (element.props.children) {
2170
- const childRadius = extractBorderRadiusFromChildren(element.props.children);
2171
- if (childRadius > 0 && childRadius !== CONSTANTS$2.DEFAULT_CORNER_RADIUS) return childRadius;
2172
- }
2173
- return null;
2174
- }, extractBorderRadiusFromChildren = children => {
2175
- if (!children) return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
2176
- try {
2177
- const childArray = React__default.default.Children.toArray(children);
2178
- for (let i = 0; i < childArray.length; i++) {
2179
- const child = childArray[i];
2180
- if ( React__default.default.isValidElement(child)) {
2181
- const radius = extractBorderRadiusFromElement(child);
2182
- if (null !== radius) return radius;
2367
+ var aCallable = aCallable$3, toObject = toObject$2, IndexedObject = indexedObject, lengthOfArrayLike = lengthOfArrayLike$2, $TypeError = TypeError, REDUCE_EMPTY = "Reduce of empty array with no initial value", createMethod = function(IS_RIGHT) {
2368
+ return function(that, callbackfn, argumentsLength, memo) {
2369
+ var O = toObject(that), self = IndexedObject(O), length = lengthOfArrayLike(O);
2370
+ if (aCallable(callbackfn), 0 === length && argumentsLength < 2) throw new $TypeError(REDUCE_EMPTY);
2371
+ var index = IS_RIGHT ? length - 1 : 0, i = IS_RIGHT ? -1 : 1;
2372
+ if (argumentsLength < 2) for (;;) {
2373
+ if (index in self) {
2374
+ memo = self[index], index += i;
2375
+ break;
2183
2376
  }
2377
+ if (index += i, IS_RIGHT ? index < 0 : length <= index) throw new $TypeError(REDUCE_EMPTY);
2184
2378
  }
2185
- } catch (error) {
2186
- // Silently handle errors
2187
- }
2188
- return CONSTANTS$2.DEFAULT_CORNER_RADIUS;
2189
- }, smoothstep = t => {
2190
- const clamped = Math.max(0, Math.min(1, t));
2191
- return clamped * clamped * (3 - 2 * clamped);
2192
- }, lerp$1 = (a, b, t) => a + (b - a) * t, softClamp = (value, max) => max <= 0 ? 0 : max * (1 - Math.exp(-value / max)), calculateSpring = (current, target, velocity, stiffness = .1, damping = .8) => {
2193
- const newVelocity = (velocity + (target - current) * stiffness) * damping;
2194
- return {
2195
- value: current + newVelocity,
2196
- velocity: newVelocity
2379
+ for (;IS_RIGHT ? index >= 0 : length > index; index += i) index in self && (memo = callbackfn(memo, self[index], index, O));
2380
+ return memo;
2197
2381
  };
2198
- }, getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
2199
- switch (mode) {
2200
- case "standard":
2201
- return displacementMap;
2382
+ }, arrayReduce = {
2383
+ // `Array.prototype.reduce` method
2384
+ // https://tc39.es/ecma262/#sec-array.prototype.reduce
2385
+ left: createMethod(!1),
2386
+ // `Array.prototype.reduceRight` method
2387
+ // https://tc39.es/ecma262/#sec-array.prototype.reduceright
2388
+ right: createMethod(!0)
2389
+ }, fails = fails$9, globalThis$1 = globalThis_1, userAgent = environmentUserAgent, classof = classofRaw$2, userAgentStartsWith = function(string) {
2390
+ return userAgent.slice(0, string.length) === string;
2391
+ }, environment = userAgentStartsWith("Bun/") ? "BUN" : userAgentStartsWith("Cloudflare-Workers") ? "CLOUDFLARE" : userAgentStartsWith("Deno/") ? "DENO" : userAgentStartsWith("Node.js/") ? "NODE" : globalThis$1.Bun && "string" == typeof Bun.version ? "BUN" : globalThis$1.Deno && "object" == typeof Deno.version ? "DENO" : "process" === classof(globalThis$1.process) ? "NODE" : globalThis$1.window && globalThis$1.document ? "BROWSER" : "REST", $reduce = arrayReduce.left;
2202
2392
 
2203
- case "polar":
2204
- return polarDisplacementMap;
2393
+ // `Array.prototype.reduce` method
2394
+ // https://tc39.es/ecma262/#sec-array.prototype.reduce
2395
+ _export({
2396
+ target: "Array",
2397
+ proto: !0,
2398
+ forced: !("NODE" === environment) && environmentV8Version > 79 && environmentV8Version < 83 || !function(METHOD_NAME, argument) {
2399
+ var method = [][METHOD_NAME];
2400
+ return !!method && fails((function() {
2401
+ // eslint-disable-next-line no-useless-call -- required for testing
2402
+ method.call(null, argument || function() {
2403
+ return 1;
2404
+ }, 1);
2405
+ }));
2406
+ }("reduce")
2407
+ }, {
2408
+ reduce: function(callbackfn /* , initialValue */) {
2409
+ var length = arguments.length;
2410
+ return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : void 0);
2411
+ }
2412
+ });
2205
2413
 
2206
- case "prominent":
2207
- return prominentDisplacementMap;
2414
+ var reduce$3 = getBuiltInPrototypeMethod$3("Array", "reduce"), isPrototypeOf = objectIsPrototypeOf, method = reduce$3, ArrayPrototype = Array.prototype, _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
2415
+ var own = it.reduce;
2416
+ return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.reduce ? method : own;
2417
+ }));
2208
2418
 
2209
- case "shader":
2210
- return shaderMapUrl || displacementMap;
2419
+ /**
2420
+ * Map SCSS tokens to CSS custom properties
2421
+ *
2422
+ * @example
2423
+ * const tokens = { '$primary-color': '#7AFFD7', '$spacing-md': '16px' }
2424
+ * const vars = mapSCSSTokensToCSSVars(tokens)
2425
+ * // Returns: { '--primary-color': '#7AFFD7', '--spacing-md': '16px' }
2426
+ */
2427
+ function mapSCSSTokensToCSSVars(tokens, options = {}) {
2428
+ const vars = {}, {prefix: prefix = "atomix", separator: separator = "-"} = options;
2429
+ return Object.entries(tokens).forEach((([key, value]) => {
2430
+ // Remove $ prefix from SCSS variables
2431
+ const normalizedKey = (key.startsWith("$") ? key.slice(1) : key).replace(/_/g, separator);
2432
+ // Convert underscores to separators
2433
+ vars[`--${prefix}${separator}${normalizedKey}`] = String(value);
2434
+ })), vars;
2435
+ }
2211
2436
 
2212
- default:
2213
- return console.warn("AtomixGlass: Invalid displacement mode"), displacementMap;
2214
- }
2215
- }, sharedShaderCache = new Map, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsxRuntime.jsx("svg", {
2216
- style: {
2217
- position: "absolute",
2218
- width: "100%",
2219
- height: "100%",
2220
- inset: 0
2221
- },
2222
- "aria-hidden": "true",
2223
- children: jsxRuntime.jsxs("defs", {
2224
- children: [ jsxRuntime.jsxs("radialGradient", {
2225
- id: `${id}-edge-mask`,
2226
- cx: "50%",
2227
- cy: "50%",
2228
- r: "50%",
2229
- children: [ jsxRuntime.jsx("stop", {
2230
- offset: "0%",
2437
+ /**
2438
+ * Apply CSS variables to an element
2439
+ *
2440
+ * @param vars - CSS variables to apply
2441
+ * @param element - Target element (defaults to document.documentElement)
2442
+ */ function applyCSSVariables(vars, element) {
2443
+ if ("undefined" == typeof window) return;
2444
+ // SSR safety
2445
+ const target = element || document.documentElement;
2446
+ Object.entries(vars).forEach((([key, value]) => {
2447
+ target.style.setProperty(key, String(value));
2448
+ }));
2449
+ }
2450
+
2451
+ /**
2452
+ * Remove CSS variables from an element
2453
+ *
2454
+ * @param varNames - Variable names to remove
2455
+ * @param element - Target element (defaults to document.documentElement)
2456
+ */ function removeCSSVariables(varNames, element) {
2457
+ if ("undefined" == typeof window) return;
2458
+ // SSR safety
2459
+ const target = element || document.documentElement;
2460
+ varNames.forEach((varName => {
2461
+ target.style.removeProperty(varName);
2462
+ }));
2463
+ }
2464
+
2465
+ /**
2466
+ * Get CSS variable value from an element
2467
+ *
2468
+ * @param varName - Variable name to get
2469
+ * @param element - Target element (defaults to document.documentElement)
2470
+ * @returns Variable value or null if not found
2471
+ */ function getCSSVariable(varName, element) {
2472
+ if ("undefined" == typeof window) return null;
2473
+ // SSR safety
2474
+ const target = element || document.documentElement;
2475
+ return getComputedStyle(target).getPropertyValue(varName).trim() || null;
2476
+ }
2477
+
2478
+ /**
2479
+ * Convert CSS variable object to inline style object
2480
+ *
2481
+ * @example
2482
+ * const vars = { '--atomix-button-bg': '#000' }
2483
+ * const style = cssVarsToStyle(vars)
2484
+ * // Returns: { '--atomix-button-bg': '#000' } as React.CSSProperties
2485
+ */ function cssVarsToStyle(vars) {
2486
+ var _context;
2487
+ return _reduceInstanceProperty(_context = Object.entries(vars)).call(_context, ((acc, [key, value]) => (acc[key] = "number" == typeof value ? `${value}px` : value,
2488
+ acc)), {});
2489
+ }
2490
+
2491
+ /**
2492
+ * Merge multiple CSS variable objects
2493
+ * Later objects override earlier ones
2494
+ */ function mergeCSSVars(...varObjects) {
2495
+ return _reduceInstanceProperty(varObjects).call(varObjects, ((acc, vars) => (vars && Object.assign(acc, vars),
2496
+ acc)), {});
2497
+ }
2498
+
2499
+ /**
2500
+ * Validate CSS variable name format
2501
+ */ function isValidCSSVariableName(name) {
2502
+ return /^--[a-z0-9-]+$/.test(name);
2503
+ }
2504
+
2505
+ /**
2506
+ * Extract component name from CSS variable name
2507
+ *
2508
+ * @example
2509
+ * extractComponentName('--atomix-button-bg')
2510
+ * // Returns: 'button'
2511
+ */ function extractComponentName(varName, prefix = "atomix") {
2512
+ const regex = new RegExp(`^--${prefix}-([a-z0-9]+)-`), match = varName.match(regex);
2513
+ return match ? match[1] ?? null : null;
2514
+ }
2515
+
2516
+ /**
2517
+ * Component Utilities
2518
+ *
2519
+ * Helper functions for component development with the new customization system
2520
+ */
2521
+ /**
2522
+ * Merge multiple class names
2523
+ */ function mergeClassNames(...classes) {
2524
+ return classes.filter(Boolean).join(" ");
2525
+ }
2526
+
2527
+ /**
2528
+ * Apply part styles to element props
2529
+ */ function applyPartStyles(baseProps, partStyles) {
2530
+ return partStyles ? {
2531
+ ...baseProps,
2532
+ className: mergeClassNames(baseProps.className, partStyles.className),
2533
+ style: {
2534
+ ...baseProps.style,
2535
+ ...partStyles.style
2536
+ }
2537
+ } : baseProps;
2538
+ }
2539
+
2540
+ /**
2541
+ * Create style object from CSS variables
2542
+ */ function createCSSVarStyle(cssVars, baseStyle) {
2543
+ return cssVars ? {
2544
+ ...cssVarsToStyle(cssVars),
2545
+ ...baseStyle
2546
+ } : baseStyle || {};
2547
+ }
2548
+
2549
+ function mergeComponentProps(baseProps, customization) {
2550
+ const {className: className, style: style, cssVars: cssVars, parts: parts} = customization, cssVarStyle = cssVars ? cssVarsToStyle(cssVars) : {};
2551
+ // Merge CSS variables into style
2552
+ return {
2553
+ ...baseProps,
2554
+ className: mergeClassNames(baseProps.className, className),
2555
+ style: {
2556
+ ...cssVarStyle,
2557
+ ...baseProps.style,
2558
+ ...style
2559
+ }
2560
+ };
2561
+ }
2562
+
2563
+ /**
2564
+ * Get part styles from parts object
2565
+ */ function getPartStyles(parts, partName) {
2566
+ return parts?.[partName];
2567
+ }
2568
+
2569
+ /**
2570
+ * Create element props with part styles
2571
+ */ function createPartProps(baseClassName, partStyles, additionalProps) {
2572
+ return {
2573
+ ...additionalProps,
2574
+ className: mergeClassNames(baseClassName, partStyles?.className),
2575
+ style: {
2576
+ ...partStyles?.style,
2577
+ ...additionalProps?.style
2578
+ }
2579
+ };
2580
+ }
2581
+
2582
+ /**
2583
+ * Check if component has customization
2584
+ */ function hasCustomization(props) {
2585
+ return Boolean(props.parts || props.cssVars || props.slots);
2586
+ }
2587
+
2588
+ /**
2589
+ * Create data attributes for debugging
2590
+ */ function createDebugAttrs(componentName, variant) {
2591
+ return "undefined" == typeof process || "development" !== process.env?.NODE_ENV ? {} : {
2592
+ "data-component": componentName,
2593
+ ...variant && {
2594
+ "data-variant": variant
2595
+ }
2596
+ };
2597
+ }
2598
+
2599
+ /**
2600
+ * Generate a UUID v4
2601
+ */ function generateUUID() {
2602
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c => {
2603
+ const r = 16 * Math.random() | 0;
2604
+ return ("x" === c ? r : 3 & r | 8).toString(16);
2605
+ }));
2606
+ }
2607
+
2608
+ /**
2609
+ * Check if a URL is a YouTube URL
2610
+ */ function isYouTubeUrl(url) {
2611
+ return /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/.test(url);
2612
+ }
2613
+
2614
+ /**
2615
+ * Extract YouTube video ID from URL
2616
+ */ function extractYouTubeId(url) {
2617
+ if (!isYouTubeUrl(url)) return null;
2618
+ const patterns = [ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, /youtube\.com\/.*[?&]v=([^&\n?#]+)/ ];
2619
+ for (const pattern of patterns) {
2620
+ const match = url.match(pattern);
2621
+ if (match && match[1]) return match[1];
2622
+ }
2623
+ return null;
2624
+ }
2625
+
2626
+ /**
2627
+ * Utility to merge multiple React refs into one
2628
+ */ function setRef(ref, value) {
2629
+ "function" == typeof ref ? ref(value) : ref && (
2630
+ // This is safe because we're checking that ref exists first
2631
+ ref.current = value);
2632
+ }
2633
+
2634
+ /**
2635
+ * Combines two React refs into a single ref function
2636
+ * This is used when you need to use and forward a ref at the same time
2637
+ */ function useForkRef(refA, refB) {
2638
+ return React__default.default.useMemo((() => null == refA && null == refB ? null : refValue => {
2639
+ setRef(refA, refValue), setRef(refB, refValue);
2640
+ }), [ refA, refB ]);
2641
+ }
2642
+
2643
+ const {CONSTANTS: CONSTANTS$3} = ATOMIX_GLASS, calculateElementCenter = rect => rect ? {
2644
+ x: rect.left + rect.width / 2,
2645
+ y: rect.top + rect.height / 2
2646
+ } : {
2647
+ x: 0,
2648
+ y: 0
2649
+ }, calculateMouseInfluence = mouseOffset => {
2650
+ if (!mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y) return 0;
2651
+ // Clamp influence to keep mouse response subtle and stable.
2652
+ const influence = Math.sqrt(mouseOffset.x * mouseOffset.x + mouseOffset.y * mouseOffset.y) / CONSTANTS$3.MOUSE_INFLUENCE_DIVISOR;
2653
+ return Math.min(.8, influence);
2654
+ // Tighter cap to prevent blur/filter blow-out
2655
+ }, 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 => {
2656
+ if ("number" == typeof value) return Math.max(0, value);
2657
+ if ("string" != typeof value || !value.trim()) return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
2658
+ const trimmedValue = value.trim();
2659
+ // Handle px values
2660
+ if (trimmedValue.endsWith("px")) {
2661
+ const parsed = parseFloat(trimmedValue);
2662
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, parsed);
2663
+ }
2664
+ // Handle rem values (assume 16px = 1rem)
2665
+ if (trimmedValue.endsWith("rem")) {
2666
+ const parsed = parseFloat(trimmedValue);
2667
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
2668
+ }
2669
+ // Handle em values (assume 16px = 1em for simplicity)
2670
+ if (trimmedValue.endsWith("em")) {
2671
+ const parsed = parseFloat(trimmedValue);
2672
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, 16 * parsed);
2673
+ }
2674
+ // Handle percentage (convert to approximate px value, assuming 200px container)
2675
+ if (trimmedValue.endsWith("%")) {
2676
+ const parsed = parseFloat(trimmedValue);
2677
+ return isNaN(parsed) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, parsed / 100 * 200);
2678
+ }
2679
+ // Handle unitless numbers
2680
+ const numValue = parseFloat(trimmedValue);
2681
+ return isNaN(numValue) ? CONSTANTS$3.DEFAULT_CORNER_RADIUS : Math.max(0, numValue);
2682
+ }, extractBorderRadiusFromElement = element => {
2683
+ if (!element || !element.props) return null;
2684
+ // Check inline styles first (highest priority)
2685
+ if (element.props.style) {
2686
+ const radiusFromStyle = (style => {
2687
+ if (!style) return null;
2688
+ // Check various border-radius properties
2689
+ const borderRadius = style.borderRadius || style.borderTopLeftRadius || style.borderTopRightRadius || style.borderBottomLeftRadius || style.borderBottomRightRadius;
2690
+ return void 0 !== borderRadius ? parseBorderRadiusValue(borderRadius) : null;
2691
+ })(element.props.style);
2692
+ if (null !== radiusFromStyle && radiusFromStyle > 0) return radiusFromStyle;
2693
+ }
2694
+ // If element has children, recursively check them
2695
+ if (element.props.children) {
2696
+ const childRadius = extractBorderRadiusFromChildren(element.props.children);
2697
+ if (childRadius > 0 && childRadius !== CONSTANTS$3.DEFAULT_CORNER_RADIUS) return childRadius;
2698
+ }
2699
+ return null;
2700
+ }, extractBorderRadiusFromChildren = children => {
2701
+ if (!children) return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
2702
+ try {
2703
+ const childArray = React__default.default.Children.toArray(children);
2704
+ for (let i = 0; i < childArray.length; i++) {
2705
+ const child = childArray[i];
2706
+ if ( React__default.default.isValidElement(child)) {
2707
+ const radius = extractBorderRadiusFromElement(child);
2708
+ if (null !== radius) return radius;
2709
+ }
2710
+ }
2711
+ } catch (error) {
2712
+ // Silently handle errors
2713
+ }
2714
+ return CONSTANTS$3.DEFAULT_CORNER_RADIUS;
2715
+ }, smoothstep = t => {
2716
+ const clamped = Math.max(0, Math.min(1, t));
2717
+ return clamped * clamped * (3 - 2 * clamped);
2718
+ }, lerp = (a, b, t) => a + (b - a) * t, softClamp = (value, max) => max <= 0 ? 0 : max * (1 - Math.exp(-value / max)), clamp = (value, min, max) => "number" != typeof value || isNaN(value) ? min : Math.max(min, Math.min(max, value)), smoothstepEdge = (edge0, edge1, x) => "number" != typeof edge0 || "number" != typeof edge1 || "number" != typeof x ? 0 : smoothstep((x - edge0) / (edge1 - edge0)), easeInOutCubic = t => {
2719
+ if ("number" != typeof t || isNaN(t)) return 0;
2720
+ const clamped = Math.max(0, Math.min(1, t));
2721
+ return clamped < .5 ? 4 * clamped * clamped * clamped : 1 - Math.pow(-2 * clamped + 2, 3) / 2;
2722
+ }, easeOutQuart = t => {
2723
+ if ("number" != typeof t || isNaN(t)) return 0;
2724
+ const clamped = Math.max(0, Math.min(1, t));
2725
+ return 1 - Math.pow(1 - clamped, 4);
2726
+ }, vec2Length = (x, y) => {
2727
+ if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
2728
+ const maxComponent = Math.max(Math.abs(x), Math.abs(y));
2729
+ if (0 === maxComponent) return 0;
2730
+ const scaledX = x / maxComponent, scaledY = y / maxComponent;
2731
+ return maxComponent * Math.sqrt(scaledX * scaledX + scaledY * scaledY);
2732
+ }, getInteractionIntensity = (isHovered, isActive) => ({
2733
+ hoverIntensity: isHovered ? CONSTANTS$3.INTERACTION.HOVER_INTENSITY : 1,
2734
+ activeIntensity: isActive ? CONSTANTS$3.INTERACTION.ACTIVE_INTENSITY : 1
2735
+ })
2736
+ /**
2737
+ * Spring-damper integration helper
2738
+ * Calculates the next value based on velocity, stiffness, and damping.
2739
+ */ , calculateSpring = (current, target, velocity, stiffness = .1, damping = .8) => {
2740
+ const newVelocity = (velocity + (target - current) * stiffness) * damping;
2741
+ return {
2742
+ value: current + newVelocity,
2743
+ velocity: newVelocity
2744
+ };
2745
+ };
2746
+
2747
+ /**
2748
+ * Calculate element center from bounding rect
2749
+ */
2750
+ /**
2751
+ * Normalizes a layout inset for use in CSS custom properties.
2752
+ *
2753
+ * @param value - Raw inset from `style` (number, px string, or `auto`).
2754
+ * @param fallback - Value used when `value` is undefined.
2755
+ */
2756
+ function formatGlassInsetValue(value, fallback = "auto") {
2757
+ return void 0 === value ? "number" == typeof fallback ? `${fallback}px` : String(fallback) : "auto" === value ? "auto" : "number" == typeof value ? `${value}px` : value;
2758
+ }
2759
+
2760
+ /**
2761
+ * Determines whether the glass should use fixed/sticky layout semantics.
2762
+ *
2763
+ * @param explicit - Value of the `isFixedOrSticky` prop.
2764
+ * @param position - `position` from the consumer `style` object.
2765
+ */
2766
+ /** Coerces a value to a finite number, returning `fallback` when invalid. */
2767
+ function toSafeNumber(value, fallback = 0) {
2768
+ return "number" != typeof value || isNaN(value) ? fallback : value;
2769
+ }
2770
+
2771
+ /**
2772
+ * Calculates the target frame rate for shader time-animation loops.
2773
+ *
2774
+ * Balances visual quality against distortion complexity and `animationSpeed`.
2775
+ */
2776
+ /**
2777
+ * Computes per-channel displacement scale for the SVG chromatic-aberration filter.
2778
+ */
2779
+ function getChromaticDisplacementScale(mode, displacementScale, aberrationIntensity, channelFactor) {
2780
+ return displacementScale * (("shader" === mode ? 1 : -1) - aberrationIntensity * channelFactor);
2781
+ }
2782
+
2783
+ /**
2784
+ * Get displacement map URL based on mode
2785
+ */ const getDisplacementMap = (mode, displacementMap, polarDisplacementMap, prominentDisplacementMap, shaderMapUrl) => {
2786
+ switch (mode) {
2787
+ case "standard":
2788
+ return displacementMap;
2789
+
2790
+ case "polar":
2791
+ return polarDisplacementMap;
2792
+
2793
+ case "prominent":
2794
+ return prominentDisplacementMap;
2795
+
2796
+ case "shader":
2797
+ return shaderMapUrl || displacementMap;
2798
+
2799
+ default:
2800
+ return console.warn("AtomixGlass: Invalid displacement mode"), displacementMap;
2801
+ }
2802
+ }, sharedShaderCache = new Map, CHROMATIC_CHANNELS = [ {
2803
+ result: "RED_DISPLACED",
2804
+ channelResult: "RED_CHANNEL",
2805
+ aberrationFactor: 0,
2806
+ colorMatrix: "1 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 1 0"
2807
+ }, {
2808
+ result: "GREEN_DISPLACED",
2809
+ channelResult: "GREEN_CHANNEL",
2810
+ aberrationFactor: .02,
2811
+ colorMatrix: "0 0 0 0 0\n0 1 0 0 0\n0 0 0 0 0\n0 0 0 1 0"
2812
+ }, {
2813
+ result: "BLUE_DISPLACED",
2814
+ channelResult: "BLUE_CHANNEL",
2815
+ aberrationFactor: .03,
2816
+ colorMatrix: "0 0 0 0 0\n0 0 0 0 0\n0 0 1 0 0\n0 0 0 1 0"
2817
+ } ], FILTER_SVG_STYLE = {
2818
+ position: "absolute",
2819
+ width: "100%",
2820
+ height: "100%",
2821
+ inset: 0
2822
+ }, GlassFilterComponent = ({id: id, displacementScale: displacementScale, aberrationIntensity: aberrationIntensity, mode: mode, shaderMapUrl: shaderMapUrl, blurAmount: blurAmount}) => jsxRuntime.jsx("svg", {
2823
+ style: FILTER_SVG_STYLE,
2824
+ "aria-hidden": "true",
2825
+ children: jsxRuntime.jsxs("defs", {
2826
+ children: [ jsxRuntime.jsxs("radialGradient", {
2827
+ id: `${id}-edge-mask`,
2828
+ cx: "50%",
2829
+ cy: "50%",
2830
+ r: "50%",
2831
+ children: [ jsxRuntime.jsx("stop", {
2832
+ offset: "0%",
2231
2833
  stopColor: "black",
2232
2834
  stopOpacity: "0"
2233
2835
  }), jsxRuntime.jsx("stop", {
@@ -2272,43 +2874,21 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
2272
2874
  dx: "0",
2273
2875
  dy: "0",
2274
2876
  result: "CENTER_ORIGINAL"
2275
- }), jsxRuntime.jsx("feDisplacementMap", {
2276
- in: "SourceGraphic",
2277
- in2: "DISPLACEMENT_MAP",
2278
- scale: displacementScale * ("shader" === mode ? 1 : -1),
2279
- xChannelSelector: "R",
2280
- yChannelSelector: "B",
2281
- result: "RED_DISPLACED"
2282
- }), jsxRuntime.jsx("feColorMatrix", {
2283
- in: "RED_DISPLACED",
2284
- type: "matrix",
2285
- values: "1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
2286
- result: "RED_CHANNEL"
2287
- }), jsxRuntime.jsx("feDisplacementMap", {
2288
- in: "SourceGraphic",
2289
- in2: "DISPLACEMENT_MAP",
2290
- scale: displacementScale * (("shader" === mode ? 1 : -1) - .02 * aberrationIntensity),
2291
- xChannelSelector: "R",
2292
- yChannelSelector: "B",
2293
- result: "GREEN_DISPLACED"
2294
- }), jsxRuntime.jsx("feColorMatrix", {
2295
- in: "GREEN_DISPLACED",
2296
- type: "matrix",
2297
- values: "0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0",
2298
- result: "GREEN_CHANNEL"
2299
- }), jsxRuntime.jsx("feDisplacementMap", {
2300
- in: "SourceGraphic",
2301
- in2: "DISPLACEMENT_MAP",
2302
- scale: displacementScale * (("shader" === mode ? 1 : -1) - .03 * aberrationIntensity),
2303
- xChannelSelector: "R",
2304
- yChannelSelector: "B",
2305
- result: "BLUE_DISPLACED"
2306
- }), jsxRuntime.jsx("feColorMatrix", {
2307
- in: "BLUE_DISPLACED",
2308
- type: "matrix",
2309
- values: "0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0",
2310
- result: "BLUE_CHANNEL"
2311
- }), jsxRuntime.jsx("feBlend", {
2877
+ }), CHROMATIC_CHANNELS.map((channel => jsxRuntime.jsxs(React__default.default.Fragment, {
2878
+ children: [ jsxRuntime.jsx("feDisplacementMap", {
2879
+ in: "SourceGraphic",
2880
+ in2: "DISPLACEMENT_MAP",
2881
+ scale: getChromaticDisplacementScale(mode, displacementScale, aberrationIntensity, channel.aberrationFactor),
2882
+ xChannelSelector: "R",
2883
+ yChannelSelector: "B",
2884
+ result: channel.result
2885
+ }), jsxRuntime.jsx("feColorMatrix", {
2886
+ in: channel.result,
2887
+ type: "matrix",
2888
+ values: channel.colorMatrix,
2889
+ result: channel.channelResult
2890
+ }) ]
2891
+ }, channel.channelResult))), jsxRuntime.jsx("feBlend", {
2312
2892
  in: "GREEN_CHANNEL",
2313
2893
  in2: "BLUE_CHANNEL",
2314
2894
  mode: "screen",
@@ -2349,21 +2929,22 @@ const {CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, calculateElementCenter = rect =>
2349
2929
  });
2350
2930
 
2351
2931
  /**
2352
- * Calculate distance between two points
2932
+ * Module-level LRU cache for shader displacement maps.
2933
+ *
2934
+ * Shared across all `AtomixGlassContainer` instances so identical size and
2935
+ * variant combinations are generated once.
2353
2936
  */ GlassFilterComponent.displayName = "GlassFilter";
2354
2937
 
2355
- // Memoize component to prevent unnecessary re-renders
2938
+ /** Shallow prop comparison to avoid redundant SVG filter regeneration. */
2356
2939
  const GlassFilter = React.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 = React.forwardRef((({children: children, className: className = "", style: style, displacementScale: displacementScale = 25, blurAmount: blurAmount = .0625, saturation: saturation = 180, aberrationIntensity: aberrationIntensity = 2, mouseOffset: mouseOffset = {
2357
2940
  x: 0,
2358
2941
  y: 0
2359
- }, 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 = {
2942
+ }, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, isActive: isActive = !1, overLight: overLight = !1, overLightConfig: overLightConfig = {}, borderRadius: borderRadius = 0, glassSize: glassSize = {
2360
2943
  width: 0,
2361
2944
  height: 0
2362
- }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, isFixedOrSticky: isFixedOrSticky = !1,
2363
- // Phase 1: Animation System props
2364
- 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) => {
2945
+ }, onClick: onClick, mode: mode = "standard", effectiveWithoutEffects: effectiveWithoutEffects = !1, effectiveReducedMotion: effectiveReducedMotion = !1, shaderVariant: shaderVariant = "liquidGlass", withLiquidBlur: withLiquidBlur = !1, isFixedOrSticky: isFixedOrSticky = !1, withTimeAnimation: withTimeAnimation = !1, animationSpeed: animationSpeed = 1, withMultiLayerDistortion: withMultiLayerDistortion = !1, distortionOctaves: distortionOctaves = 3, distortionLacunarity: distortionLacunarity = 2, distortionGain: distortionGain = .5, distortionQuality: distortionQuality = "medium", contentRef: contentRef}, ref) => {
2365
2946
  // React 18 useId — stable, unique, and SSR-safe (no module-level counter)
2366
- const rawId = React.useId(), filterId = React.useMemo((() => `atomix-glass-filter-${rawId.replace(/:/g, "")}`), [ rawId ]), [shaderMapUrl, setShaderMapUrl] = React.useState(""), shaderGeneratorRef = React.useRef(null), shaderUtilsRef = React.useRef(null), shaderDebounceTimeoutRef = React.useRef(null), shaderUpdateTimeoutRef = React.useRef(null), animationFrameRef = React.useRef(null);
2947
+ const rawId = React.useId(), filterId = React.useMemo((() => `atomix-glass-filter-${rawId.replace(/:/g, "")}`), [ rawId ]), containerRef = useForkRef(ref, null), [shaderMapUrl, setShaderMapUrl] = React.useState(""), shaderGeneratorRef = React.useRef(null), shaderUtilsRef = React.useRef(null), shaderDebounceTimeoutRef = React.useRef(null), shaderUpdateTimeoutRef = React.useRef(null), animationFrameRef = React.useRef(null);
2367
2948
  // Lazy load shader utilities only when shader mode is needed
2368
2949
  React.useEffect((() => {
2369
2950
  "shader" === mode ?
@@ -2443,15 +3024,23 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
2443
3024
  shaderGeneratorRef.current = null;
2444
3025
  }
2445
3026
  };
2446
- }), [ mode, glassSize, shaderVariant ]),
2447
- // Phase 1: Time-Based Animation Loop - Continuous shader regeneration
2448
- React.useEffect((() => {
3027
+ }), [ mode, glassSize, shaderVariant ]), React.useEffect((() => {
2449
3028
  // Only run animations in shader mode with time animation enabled
2450
3029
  if ("shader" !== mode || !withTimeAnimation || effectiveReducedMotion || effectiveWithoutEffects)
2451
3030
  // Cancel any existing animation frame
2452
3031
  return void (null !== animationFrameRef.current && (cancelAnimationFrame(animationFrameRef.current),
2453
3032
  animationFrameRef.current = null));
2454
- 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)));
3033
+ const targetFps = function(options) {
3034
+ 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;
3035
+ return Math.max(12, Math.min(60, Math.round(baseFps * effectiveSpeed / complexity)));
3036
+ }({
3037
+ distortionQuality: distortionQuality,
3038
+ animationSpeed: animationSpeed,
3039
+ withMultiLayerDistortion: withMultiLayerDistortion,
3040
+ distortionOctaves: distortionOctaves,
3041
+ distortionLacunarity: distortionLacunarity,
3042
+ distortionGain: distortionGain
3043
+ }), frameInterval = 1e3 / targetFps;
2455
3044
  let lastUpdate = 0, isCancelled = !1;
2456
3045
  const animate = currentTime => {
2457
3046
  if (!isCancelled) {
@@ -2475,88 +3064,21 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
2475
3064
  };
2476
3065
  }), [ mode, withTimeAnimation, animationSpeed, displacementScale, withMultiLayerDistortion, distortionOctaves, distortionLacunarity, distortionGain, distortionQuality, effectiveReducedMotion, effectiveWithoutEffects, glassSize ]);
2477
3066
  // Removed forced reflow to avoid layout thrash and potential feedback sizing loops
2478
- const [rectCache, setRectCache] = React.useState(null);
2479
- React.useEffect((() => {
2480
- if (!ref || "function" == typeof ref) return;
2481
- const element = ref.current;
2482
- if (element) try {
2483
- setRectCache(element.getBoundingClientRect());
2484
- } catch (error) {
2485
- console.warn("AtomixGlassContainer: Error getting element bounds", error), setRectCache(null);
2486
- }
2487
- }), [ ref, glassSize ]);
2488
- const liquidBlur = React.useMemo((() => {
2489
- const defaultBlur = {
2490
- baseBlur: blurAmount,
2491
- edgeBlur: 1.25 * blurAmount,
2492
- centerBlur: 1.1 * blurAmount,
2493
- flowBlur: 1.2 * blurAmount
2494
- };
2495
- // Enhanced validation for liquid blur
2496
- if (!withLiquidBlur || !rectCache || !mouseOffset || "number" != typeof mouseOffset.x || "number" != typeof mouseOffset.y || isNaN(mouseOffset.x) || isNaN(mouseOffset.y)) return defaultBlur;
3067
+ const containerVars = React.useMemo((() => {
2497
3068
  try {
2498
- 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);
2499
- // NOTE: hover/active multipliers intentionally omitted here —
2500
- // they belong on opacity layers, not the blur filter itself.
2501
3069
  return {
2502
- baseBlur: clampBlur(baseBlur),
2503
- edgeBlur: clampBlur(edgeBlur),
2504
- centerBlur: clampBlur(centerBlur),
2505
- flowBlur: clampBlur(flowBlur)
2506
- };
2507
- } catch (error) {
2508
- return console.warn("AtomixGlassContainer: Error calculating liquid blur", error),
2509
- defaultBlur;
2510
- }
2511
- }), [ withLiquidBlur, blurAmount, mouseOffset, rectCache ]), backdropStyle = React.useMemo((() => {
2512
- try {
2513
- 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;
2514
- // Validate blur values before using them
2515
- return !withLiquidBlur || effectiveReducedMotion || effectiveWithoutEffects || area > 18e4 ? {
2516
- 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})`
2517
- } : {
2518
- 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})`
2519
- };
2520
- // Single-pass fallback: stronger radius to match perceived blur of multi-pass
2521
- } catch (error) {
2522
- return console.warn("AtomixGlassContainer: Error calculating backdrop style", error),
2523
- {
2524
- backdropFilter: `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`
2525
- };
2526
- }
2527
- }), [ liquidBlur, saturation, blurAmount, rectCache, effectiveReducedMotion, effectiveWithoutEffects, withLiquidBlur, overLightConfig ]), containerVars = React.useMemo((() => {
2528
- try {
2529
- // Safe extraction of mouse offset values
2530
- 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;
2531
- return {
2532
- "--atomix-glass-container-radius": `${"number" != typeof borderRadius || isNaN(borderRadius) ? 0 : borderRadius}px`,
2533
- "--atomix-glass-container-backdrop": backdropStyle?.backdropFilter || "none",
2534
- "--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",
2535
- "--atomix-glass-container-shadow-opacity": effectiveWithoutEffects ? 0 : 1,
2536
- // Background and shadow values use design token-aligned RGB values
2537
- "--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",
2538
- "--atomix-glass-container-text-shadow": overLight ? "0px 1px 2px rgba(255, 255, 255, 0.15)" : "0px 2px 12px rgba(0, 0, 0, 0.4)",
2539
- "--atomix-glass-container-box-shadow": overLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0px 12px 40px rgba(0, 0, 0, 0.25)"
3070
+ "--atomix-glass-container-radius": `${"number" != typeof borderRadius || isNaN(borderRadius) ? 0 : borderRadius}px`
2540
3071
  };
2541
3072
  } catch (error) {
2542
3073
  return console.warn("AtomixGlassContainer: Error generating container variables", error),
2543
3074
  {
2544
- "--atomix-glass-container-padding": "0 0",
2545
- "--atomix-glass-container-radius": "0px",
2546
- "--atomix-glass-container-backdrop": "none",
2547
- "--atomix-glass-container-shadow": "none",
2548
- "--atomix-glass-container-shadow-opacity": 1,
2549
- "--atomix-glass-container-bg": "none",
2550
- "--atomix-glass-container-text-shadow": "none"
3075
+ "--atomix-glass-container-radius": "0px"
2551
3076
  };
2552
3077
  }
2553
- }), [ borderRadius, backdropStyle, mouseOffset, overLight, effectiveWithoutEffects, overLightConfig ]);
3078
+ }), [ borderRadius ]);
2554
3079
  return jsxRuntime.jsx("div", {
2555
- ref: el => {
2556
- // Handle forwarded ref
2557
- "function" == typeof ref ? ref(el) : ref && (ref.current = el);
2558
- },
2559
- className: `${ATOMIX_GLASS.CONTAINER_CLASS} ${className} ${isActive ? ATOMIX_GLASS.CLASSES.ACTIVE : ""} ${overLight ? ATOMIX_GLASS.CLASSES.OVER_LIGHT : ""}`,
3080
+ ref: containerRef,
3081
+ className: mergeClassNames(ATOMIX_GLASS.CONTAINER_CLASS, className, isActive && ATOMIX_GLASS.CLASSES.ACTIVE, overLight && ATOMIX_GLASS.CLASSES.OVER_LIGHT),
2560
3082
  style: {
2561
3083
  ...style,
2562
3084
  ...containerVars
@@ -2574,8 +3096,8 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
2574
3096
  blurAmount: blurAmount,
2575
3097
  mode: mode,
2576
3098
  id: filterId,
2577
- displacementScale: "number" != typeof displacementScale || isNaN(displacementScale) ? 0 : displacementScale,
2578
- aberrationIntensity: "number" != typeof aberrationIntensity || isNaN(aberrationIntensity) ? 0 : aberrationIntensity,
3099
+ displacementScale: toSafeNumber(displacementScale),
3100
+ aberrationIntensity: toSafeNumber(aberrationIntensity),
2579
3101
  shaderMapUrl: shaderMapUrl
2580
3102
  }), jsxRuntime.jsx("div", {
2581
3103
  className: ATOMIX_GLASS.FILTER_OVERLAY_CLASS,
@@ -2597,8 +3119,12 @@ shaderTime: shaderTime, withTimeAnimation: withTimeAnimation = !1, animationSpee
2597
3119
  });
2598
3120
  }));
2599
3121
 
2600
- // ─── Blur multiplier constants (module-level, never change at runtime) ────────
2601
- AtomixGlassContainer.displayName = "AtomixGlassContainer";
3122
+ /**
3123
+ * Internal glass surface that owns backdrop-filter, SVG distortion, and content.
3124
+ *
3125
+ * Layout and stacking styles are applied via the `style` prop from the parent.
3126
+ * The root wrapper supplies CSS custom properties only.
3127
+ */ AtomixGlassContainer.displayName = "AtomixGlassContainer";
2602
3128
 
2603
3129
  // Singleton instance
2604
3130
  const globalMouseTracker = new
@@ -2713,28 +3239,32 @@ class {
2713
3239
  */ getSubscriberCount() {
2714
3240
  return this.listeners.size;
2715
3241
  }
2716
- }, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
3242
+ }, {BORDER: BORDER, CONSTANTS: CONSTANTS$2} = ATOMIX_GLASS, BORDER_GRADIENT = BORDER.GRADIENT, WHITE = CONSTANTS$2.PALETTE.WHITE, updateAtomixGlassStyles = (wrapperElement, containerElement, params) => {
2717
3243
  if (!wrapperElement && !containerElement) return;
2718
3244
  if (!validateGlassSize(params.glassSize)) return;
2719
- 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 = {
3245
+ 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: hoverIntensity, activeIntensity: activeIntensity} = getInteractionIntensity(isHovered, isActive), overLightConfig = {
2720
3246
  opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
2721
3247
  contrast: Math.min(1.6, baseOverLightConfig.contrast + .1 * mouseInfluence),
2722
3248
  brightness: Math.min(1.1, baseOverLightConfig.brightness + .05 * mouseInfluence),
2723
3249
  shadowIntensity: Math.min(1.2, Math.max(.5, baseOverLightConfig.shadowIntensity + .2 * mouseInfluence)),
2724
3250
  borderOpacity: Math.min(1, Math.max(.3, baseOverLightConfig.borderOpacity + .1 * mouseInfluence)),
2725
3251
  saturationBoost: baseOverLightConfig.saturationBoost
2726
- }, 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) => {
2727
- if (!pos1 || !pos2 || "number" != typeof pos1.x || "number" != typeof pos1.y || "number" != typeof pos2.x || "number" != typeof pos2.y) return 0;
2728
- const deltaX = pos1.x - pos2.x, deltaY = pos1.y - pos2.y;
2729
- return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
2730
- })({
2731
- x: 0,
2732
- y: 0
2733
- }, elasticTranslation), tensionFactor = smoothstep(stretchMagnitude / 80), lightingContrast = Math.min(1.8, overLightConfig.contrast + .2 * tensionFactor), lightingBrightness = Math.min(1.2, overLightConfig.brightness + .1 * tensionFactor);
3252
+ }, scaleX = directionalScale.x * scaleBase, scaleY = directionalScale.y * scaleBase, transformStyle = effectiveWithoutEffects ? `scale(${scaleBase})` : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) scaleX(${scaleX}) scaleY(${scaleY})`, tensionFactor =
3253
+ /**
3254
+ * Computes tension factor from elastic translation magnitude (0–1).
3255
+ */
3256
+ function(elasticTranslation) {
3257
+ const magnitude = Math.hypot(elasticTranslation.x, elasticTranslation.y);
3258
+ return smoothstep(magnitude / 80);
3259
+ }
3260
+ /**
3261
+ * Updates the styles of the AtomixGlass wrapper and container elements imperatively
3262
+ * to avoid React re-renders on mouse movement.
3263
+ */ (elasticTranslation), lightingContrast = Math.min(1.8, overLightConfig.contrast + .2 * tensionFactor), lightingBrightness = Math.min(1.2, overLightConfig.brightness + .1 * tensionFactor);
2734
3264
  // Calculate mouse influence
2735
3265
  // Update Wrapper Styles (glassVars)
2736
3266
  if (wrapperElement) {
2737
- 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 = {
3267
+ 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 = {
2738
3268
  hover1: {
2739
3269
  x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
2740
3270
  y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
@@ -2751,28 +3281,55 @@ class {
2751
3281
  x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
2752
3282
  y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER
2753
3283
  }, opacityValues = {
2754
- hover1: isHovered || isActive ? .5 : 0,
2755
- hover2: isActive ? .5 : 0,
2756
- hover3: isHovered ? .4 : isActive ? .8 : 0,
2757
- base: isOverLight ? overLightConfig.opacity : 0,
2758
- over: isOverLight ? 1.1 * overLightConfig.opacity : 0
3284
+ // hover-1: ambient highlight glow present on hover and during press
3285
+ hover1: isHovered || isActive ? 1 : 0,
3286
+ // hover-2: press depth shadow only fires on active (mousedown)
3287
+ hover2: isActive ? 1 : 0,
3288
+ // hover-3: global soft-light surface shift half-strength on hover, full on press
3289
+ hover3: isActive ? 1 : isHovered ? .55 : 0,
3290
+ // Dark chrome: faint smoky tint; over-light keeps stronger fill
3291
+ base: isOverLight ? overLightConfig.opacity : .14,
3292
+ over: isOverLight ? 1.1 * overLightConfig.opacity : .1
2759
3293
  }, style = wrapperElement.style;
2760
3294
  style.setProperty("--atomix-glass-transform", transformStyle || "none");
2761
3295
  // Parallax for content (liquid refraction feel)
2762
3296
  const parallaxFactor = .38 + .12 * tensionFactor;
2763
3297
  style.setProperty("--atomix-glass-child-parallax", `translate(${elasticTranslation.x * -parallaxFactor}px, ${elasticTranslation.y * -parallaxFactor}px)`),
2764
- style.setProperty("--atomix-glass-contrast", lightingContrast.toString()), style.setProperty("--atomix-glass-brightness", lightingBrightness.toString()),
3298
+ style.setProperty("--atomix-glass-contrast", lightingContrast.toString()), style.setProperty("--atomix-glass-brightness", lightingBrightness.toString());
2765
3299
  // ── Chromatic Rim Lighting ──────────────────────────────────────
2766
- // Layer 1: Core White/Blue highlight
2767
- 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%)`),
2768
- // Layer 2: Subtle Red/Warm highlight (offset angle)
2769
- 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%)`),
2770
- // Hover gradients
2771
- 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}%)`),
2772
- 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}%)`),
2773
- 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}%)`),
2774
- 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})`),
2775
- 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})`),
3300
+ const borderVars = ATOMIX_GLASS.BORDER.GRADIENT_CSS_VARS;
3301
+ if (borderAnimated && !effectiveWithoutEffects) {
3302
+ const borderCssVars =
3303
+ /**
3304
+ * Builds animated chromatic rim CSS variables for border layers 1 and 2.
3305
+ * When empty, SCSS static conic/linear fallbacks apply.
3306
+ */
3307
+ function(params) {
3308
+ 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%)`;
3309
+ return {
3310
+ [BORDER.GRADIENT_CSS_VARS.GRADIENT_1]: gradient1,
3311
+ [BORDER.GRADIENT_CSS_VARS.GRADIENT_2]: gradient2
3312
+ };
3313
+ }({
3314
+ mouseOffset: mouseOffset,
3315
+ mouseVelocity: mouseVelocity,
3316
+ elasticVelocity: elasticVelocity,
3317
+ borderOpacity: overLightConfig.borderOpacity,
3318
+ opacityMultiplier: borderOpacityMultiplier,
3319
+ tensionFactor: tensionFactor
3320
+ });
3321
+ style.setProperty(borderVars.GRADIENT_1, borderCssVars[borderVars.GRADIENT_1] ?? ""),
3322
+ style.setProperty(borderVars.GRADIENT_2, borderCssVars[borderVars.GRADIENT_2] ?? "");
3323
+ } else style.removeProperty(borderVars.GRADIENT_1), style.removeProperty(borderVars.GRADIENT_2);
3324
+ // Hover gradients — cursor-relative radial positions for realistic light tracking.
3325
+ // hover-1: white overlay highlight following cursor (works on both dark + light)
3326
+ 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%)`),
3327
+ // hover-2: press depth — darkens at cursor with multiply blend, isOverLight uses stronger black
3328
+ 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%)`),
3329
+ // hover-3: full-surface soft-light tint; linear gradient angled with cursor X
3330
+ 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%)`),
3331
+ 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%)`),
3332
+ 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%)`),
2776
3333
  // Opacities
2777
3334
  style.setProperty("--atomix-glass-hover-1-opacity", opacityValues.hover1.toString()),
2778
3335
  style.setProperty("--atomix-glass-hover-2-opacity", opacityValues.hover2.toString()),
@@ -2809,151 +3366,16 @@ class {
2809
3366
  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})`;
2810
3367
  // Container variables
2811
3368
  const style = containerElement.style;
2812
- style.setProperty("--atomix-glass-container-padding", padding), style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
3369
+ style.setProperty("--atomix-glass-container-radius", `${effectiveBorderRadius}px`),
2813
3370
  style.setProperty("--atomix-glass-container-backdrop", backdropFilterString),
2814
3371
  // Shadows
2815
- 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"),
3372
+ 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),
2816
3373
  style.setProperty("--atomix-glass-container-shadow-opacity", effectiveWithoutEffects ? "0" : "1"),
2817
3374
  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"),
2818
3375
  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)"),
2819
- 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)");
3376
+ style.setProperty("--atomix-glass-container-box-shadow", isOverLight ? "0px 16px 70px rgba(0, 0, 0, 0.75)" : "0 4px 16px rgba(0, 0, 0, 0.14), 0 1px 4px rgba(0, 0, 0, 0.10)");
2820
3377
  }
2821
- };
2822
-
2823
- /**
2824
- * Updates the styles of the AtomixGlass wrapper and container elements imperatively
2825
- * to avoid React re-renders on mouse movement.
2826
- */
2827
- /**
2828
- * Animation System for AtomixGlass Component
2829
- *
2830
- * Implements Phase 1 features from the AtomixGlass Feature Implementation Roadmap:
2831
- * - Feature 1.1: Time-Based Animation System
2832
- * - Feature 1.2: Multi-Layer Distortion System (FBM)
2833
- *
2834
- * @packageDocumentation
2835
- */
2836
- // ============================================================================
2837
- // Noise Functions for FBM (Feature 1.2)
2838
- // ============================================================================
2839
- /**
2840
- * Perlin noise implementation for smooth gradient noise
2841
- *
2842
- * @param x - X coordinate
2843
- * @param y - Y coordinate
2844
- * @returns Noise value in range [0, 1]
2845
- */
2846
- function perlinNoise(x, y) {
2847
- // Simplified Perlin noise using pseudo-random gradients
2848
- const X = 255 & Math.floor(x), Y = 255 & Math.floor(y), xf = x - Math.floor(x), yf = y - Math.floor(y), u = fade(xf), v = fade(yf), A = p[X] + Y & 255, B = p[X + 1] + Y & 255, ga = grad(p[A], xf, yf), gb = grad(p[B], xf - 1, yf), gc = grad(p[A + 1 & 255], xf, yf - 1), gd = grad(p[B + 1 & 255], xf - 1, yf - 1), lerpX1 = lerp(ga, gb, u), lerpX2 = lerp(gc, gd, u);
2849
- // Scale to [0, 1] range
2850
- return (lerp(lerpX1, lerpX2, v) + 1) / 2;
2851
- }
2852
-
2853
- // ============================================================================
2854
- // Fractal Brownian Motion (FBM) Engine (Feature 1.2)
2855
- // ============================================================================
2856
- /**
2857
- * Creates an FBM engine with configurable parameters
2858
- *
2859
- * @param config - FBM configuration (octaves, lacunarity, gain)
2860
- * @returns Object with fbm function
2861
- *
2862
- * @example
2863
- * ```typescript
2864
- * const fbmEngine = createFBMEngine({ octaves: 5, lacunarity: 2, gain: 0.5 });
2865
- *
2866
- * // Generate noise at position (0.5, 0.5) with time animation
2867
- * const noiseValue = fbmEngine.fbm(0.5, 0.5, Date.now());
2868
- * ```
2869
- */ function createFBMEngine(config) {
2870
- /**
2871
- * Fractal Brownian Motion function
2872
- * Combines multiple octaves of noise for complex, natural patterns
2873
- *
2874
- * @param x - X coordinate
2875
- * @param y - Y coordinate
2876
- * @param time - Optional time value for animation
2877
- * @returns FBM noise value in range [0, 1]
2878
- */
2879
- const fbm = (x, y, time = 0) => {
2880
- let value = 0, amplitude = .5, frequency = 1, phase = .001 * time;
2881
- // Convert to seconds for reasonable animation speed
2882
- for (let i = 0; i < config.octaves; i++)
2883
- // Apply time-based phase shift to all octaves
2884
- value += perlinNoise(x * frequency + phase, y * frequency + phase) * amplitude,
2885
- frequency *= config.lacunarity, // Increase frequency
2886
- amplitude *= config.gain;
2887
- return value;
2888
- };
2889
- /**
2890
- * Get FBM with simple time factor
2891
- */ return {
2892
- fbm: fbm,
2893
- fbmWithTime: (x, y, time) => fbm(x, y, time)
2894
- };
2895
- }
2896
-
2897
- /**
2898
- * Gets optimal FBM config based on quality preset
2899
- *
2900
- * @param quality - Quality preset level
2901
- * @returns FBM configuration for the quality level
2902
- */ const fbmEngineCache = new Map;
2903
-
2904
- // ============================================================================
2905
- // Shader Utility Functions for Time-Based Effects
2906
- // ============================================================================
2907
- /**
2908
- * Liquid glass distortion with time-based animation
2909
- * Uses FBM to create organic, flowing liquid effects
2910
- *
2911
- * @param uv - UV coordinates (normalized 0-1)
2912
- * @param time - Elapsed time in milliseconds
2913
- * @param config - FBM configuration
2914
- * @returns Distorted UV coordinates
2915
- */ function liquidGlassWithTime(uv, time, config) {
2916
- const configKey = `${config.octaves}-${config.lacunarity}-${config.gain}`;
2917
- let fbmEngine = fbmEngineCache.get(configKey);
2918
- fbmEngine || (fbmEngine = createFBMEngine(config), fbmEngineCache.set(configKey, fbmEngine));
2919
- // Animate noise with time
2920
- const animatedNoise = fbmEngine.fbmWithTime(2 * uv.x + 1e-4 * time, 2 * uv.y + 15e-5 * time, time);
2921
- return {
2922
- x: uv.x + .04 * (animatedNoise - .5),
2923
- y: uv.y + .04 * (animatedNoise - .5)
2924
- };
2925
- }
2926
-
2927
- // ============================================================================
2928
- // Helper Functions
2929
- // ============================================================================
2930
- /**
2931
- * Fade curve for smooth interpolation (Perlin's fade function)
2932
- */ function fade(t) {
2933
- return t * t * t * (t * (6 * t - 15) + 10);
2934
- }
2935
-
2936
- /**
2937
- * Linear interpolation
2938
- */ function lerp(a, b, t) {
2939
- return a + t * (b - a);
2940
- }
2941
-
2942
- /**
2943
- * Gradient calculation for Perlin noise
2944
- */ function grad(hash, x, y) {
2945
- const h = 15 & hash, u = h < 8 ? x : y, v = h < 4 ? y : 12 === h || 14 === h ? x : 0;
2946
- return (1 & h ? -u : u) + (2 & h ? -v : v);
2947
- }
2948
-
2949
- /**
2950
- * Permutation table for Perlin noise
2951
- */ const p = (() => {
2952
- const permutation = [];
2953
- for (let i = 0; i < 256; i++) permutation[i] = Math.floor(256 * Math.random());
2954
- // Duplicate for overflow handling
2955
- return [ ...permutation, ...permutation ];
2956
- })(), {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS;
3378
+ }, {CONSTANTS: CONSTANTS$1} = ATOMIX_GLASS;
2957
3379
 
2958
3380
  const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new WeakMap, setCachedBackgroundDetection = (parentElement, overLightConfig, result, threshold) => {
2959
3381
  parentElement && backgroundDetectionCache.set(parentElement, {
@@ -2968,12 +3390,35 @@ const {CONSTANTS: CONSTANTS} = ATOMIX_GLASS, backgroundDetectionCache = new Weak
2968
3390
  * Composable hook for AtomixGlass component logic
2969
3391
  * Manages all state, calculations, and event handlers
2970
3392
  */
2971
- 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:
2972
- // Default priority
2973
- // Phase 1: Animation System Props
2974
- 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}) {
3393
+ 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}) {
2975
3394
  // State
2976
- const [isHovered, setIsHovered] = React.useState(!1), [isActive, setIsActive] = React.useState(!1), cachedRectRef = React.useRef(null), internalGlobalMousePositionRef = React.useRef({
3395
+ const [isHovered, setIsHovered] = React.useState(!1), [isActive, setIsActive] = React.useState(!1), resolvedBorder = React.useMemo((() =>
3396
+ /**
3397
+ * Resolves `border` and legacy `withBorder` into a single configuration object.
3398
+ */
3399
+ function(border, withBorder) {
3400
+ const legacyDefault = withBorder ?? !0;
3401
+ return void 0 === border ? {
3402
+ enabled: legacyDefault,
3403
+ width: BORDER.DEFAULT_WIDTH,
3404
+ opacityMultiplier: 1,
3405
+ animated: !0
3406
+ } : "boolean" == typeof border ? {
3407
+ enabled: border,
3408
+ width: BORDER.DEFAULT_WIDTH,
3409
+ opacityMultiplier: 1,
3410
+ animated: !0
3411
+ } : {
3412
+ enabled: border.enabled ?? legacyDefault,
3413
+ width: (value = border.width, void 0 === value ? BORDER.DEFAULT_WIDTH : "number" == typeof value ? `${value}px` : value),
3414
+ opacityMultiplier: border.opacity ?? 1,
3415
+ animated: !1 !== border.animated
3416
+ };
3417
+ /**
3418
+ * Formats border width for CSS custom properties.
3419
+ */
3420
+ var value;
3421
+ }(border, withBorder)), [ border, withBorder ]), cachedRectRef = React.useRef(null), internalGlobalMousePositionRef = React.useRef({
2977
3422
  x: 0,
2978
3423
  y: 0
2979
3424
  }), internalMouseOffsetRef = React.useRef({
@@ -2997,52 +3442,10 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
2997
3442
  }), scaleVelocityRef = React.useRef({
2998
3443
  x: 0,
2999
3444
  y: 0
3000
- });
3001
- React.useRef(0);
3002
- const mouseVelocityRef = React.useRef({
3445
+ }), mouseVelocityRef = React.useRef({
3003
3446
  x: 0,
3004
3447
  y: 0
3005
- }), [userPrefersReducedMotion, setUserPrefersReducedMotion] = React.useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = React.useState(!1), [detectedOverLight, setDetectedOverLight] = React.useState(!1), animationFrameIdRef = React.useRef(null), animationStartTimeRef = React.useRef(0), elapsedTimeRef = React.useRef(0), shaderTimeRef = React.useRef(0), fbmConfig = React.useMemo((() => {
3006
- // If quality preset is provided, use it as base
3007
- const preset = (quality = distortionQuality, ATOMIX_GLASS.CONSTANTS.DISTORTION_QUALITY_PRESETS[quality]);
3008
- // Override with custom values if provided
3009
- var quality;
3010
- return {
3011
- octaves: distortionOctaves ?? preset.octaves,
3012
- lacunarity: distortionLacunarity ?? preset.lacunarity,
3013
- gain: distortionGain ?? preset.gain
3014
- };
3015
- }), [ distortionQuality, distortionOctaves, distortionLacunarity, distortionGain ]), fbmEngine = React.useMemo((() => withMultiLayerDistortion ? createFBMEngine(fbmConfig) : null), [ withMultiLayerDistortion, fbmConfig ]), effectiveReducedMotion = React.useMemo((() => reducedMotion || userPrefersReducedMotion), [ reducedMotion, userPrefersReducedMotion ]), effectiveWithTimeAnimation = React.useMemo((() => withTimeAnimation && !effectiveReducedMotion), [ withTimeAnimation, effectiveReducedMotion ]);
3016
- /**
3017
- * Animation loop for time-based effects
3018
- */
3019
- React.useEffect((() => {
3020
- if (!effectiveWithTimeAnimation || "undefined" == typeof window) return;
3021
- let lastFrameTime = performance.now();
3022
- /**
3023
- * Animation frame handler
3024
- */ const animate = currentTime => {
3025
- // Calculate delta time
3026
- const deltaTime = currentTime - lastFrameTime;
3027
- lastFrameTime = currentTime;
3028
- // Apply animation speed multiplier
3029
- const scaledDelta = deltaTime * animationSpeed;
3030
- elapsedTimeRef.current += scaledDelta, shaderTimeRef.current = elapsedTimeRef.current,
3031
- // Continue animation loop
3032
- animationFrameIdRef.current = requestAnimationFrame(animate);
3033
- };
3034
- // Start animation
3035
- // Cleanup
3036
- return animationStartTimeRef.current = performance.now(), animationFrameIdRef.current = requestAnimationFrame(animate),
3037
- () => {
3038
- null !== animationFrameIdRef.current && (cancelAnimationFrame(animationFrameIdRef.current),
3039
- animationFrameIdRef.current = null);
3040
- };
3041
- }), [ effectiveWithTimeAnimation, animationSpeed ]);
3042
- /**
3043
- * Get current shader time for animations
3044
- */
3045
- const getShaderTime = React.useCallback((() => shaderTimeRef.current), []), applyTimeBasedDistortion = React.useCallback((uv => effectiveWithTimeAnimation && fbmEngine ? liquidGlassWithTime(uv, shaderTimeRef.current, fbmConfig) : uv), [ effectiveWithTimeAnimation, fbmEngine, fbmConfig ]), effectiveBorderRadius = React.useMemo((() => void 0 !== borderRadius ? Math.max(0, borderRadius) : Math.max(0, dynamicBorderRadius)), [ borderRadius, dynamicBorderRadius ]), {glassSize: glassSize} = function({glassRef: glassRef, effectiveBorderRadius: effectiveBorderRadius, cachedRectRef: cachedRectRef}) {
3448
+ }), [userPrefersReducedMotion, setUserPrefersReducedMotion] = React.useState(!1), [userPrefersHighContrast, setUserPrefersHighContrast] = React.useState(!1), [detectedOverLight, setDetectedOverLight] = React.useState(!1), effectiveReducedMotion = React.useMemo((() => reducedMotion || userPrefersReducedMotion), [ reducedMotion, userPrefersReducedMotion ]), effectiveBorderRadius = React.useMemo((() => void 0 !== borderRadius ? Math.max(0, borderRadius) : Math.max(0, dynamicBorderRadius)), [ borderRadius, dynamicBorderRadius ]), {glassSize: glassSize} = function({glassRef: glassRef, effectiveBorderRadius: effectiveBorderRadius, cachedRectRef: cachedRectRef}) {
3046
3449
  const [glassSize, setGlassSize] = React.useState({
3047
3450
  width: 270,
3048
3451
  height: 69
@@ -3102,9 +3505,6 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
3102
3505
  effectiveBorderRadius: effectiveBorderRadius,
3103
3506
  cachedRectRef: cachedRectRef
3104
3507
  }), effectiveHighContrast = React.useMemo((() => highContrast || userPrefersHighContrast), [ highContrast, userPrefersHighContrast ]), effectiveWithoutEffects = React.useMemo((() => withoutEffects || effectiveReducedMotion), [ withoutEffects, effectiveReducedMotion ]), globalMousePosition = externalGlobalMousePosition || internalGlobalMousePositionRef.current, mouseOffset = externalMouseOffset || internalMouseOffsetRef.current;
3105
- /**
3106
- * Apply time-based distortion to UV coordinates
3107
- */
3108
3508
  // Extract border-radius from children
3109
3509
  React.useEffect((() => {
3110
3510
  const extractRadius = () => {
@@ -3264,25 +3664,26 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
3264
3664
  * Get effective overLight value based on configuration
3265
3665
  */
3266
3666
  const getEffectiveOverLight = React.useCallback((() => "boolean" == typeof overLight ? overLight : ("auto" === overLight || "object" == typeof overLight && null !== overLight) && detectedOverLight), [ overLight, detectedOverLight ]), validateConfigValue = React.useCallback(((value, min, max, defaultValue) => "number" != typeof value || isNaN(value) || !isFinite(value) ? defaultValue : Math.min(max, Math.max(min, value))), []), overLightConfig = React.useMemo((() => {
3267
- const isOverLight = getEffectiveOverLight(), hoverIntensity = isHovered ? 1.4 : 1, activeIntensity = isActive ? 1.6 : 1, baseConfig = {
3667
+ const isOverLight = getEffectiveOverLight(), {hoverIntensity: hoverIntensity, activeIntensity: activeIntensity} = getInteractionIntensity(isHovered, isActive), baseConfig = {
3268
3668
  isOverLight: isOverLight,
3269
3669
  threshold: .7,
3270
3670
  opacity: isOverLight ? Math.min(.6, Math.max(.2, .5 * hoverIntensity * activeIntensity)) : 0,
3271
- contrast: 1.4,
3272
- brightness: .9,
3273
- saturationBoost: 1.3,
3274
- // Fixed value dynamic saturation amplifies perceived displacement
3275
- shadowIntensity: .9,
3276
- borderOpacity: .7
3671
+ // Dark UI (Apple Music): neutral contrast + slight brightness lift
3672
+ contrast: isOverLight ? 1.4 : 1.02,
3673
+ brightness: isOverLight ? .9 : 1.02,
3674
+ saturationBoost: isOverLight ? 1.3 : 1,
3675
+ shadowIntensity: isOverLight ? .9 : 1,
3676
+ borderOpacity: isOverLight ? ATOMIX_GLASS.BORDER.OVER_LIGHT.opacity : ATOMIX_GLASS.BORDER.DARK.opacity
3277
3677
  };
3278
3678
  if ("object" == typeof overLight && null !== overLight) {
3279
- 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 = {
3679
+ 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 = {
3280
3680
  ...baseConfig,
3281
3681
  threshold: validatedThreshold,
3282
3682
  opacity: validatedOpacity * hoverIntensity * activeIntensity,
3283
3683
  contrast: validatedContrast,
3284
3684
  brightness: validatedBrightness,
3285
- saturationBoost: validatedSaturationBoost
3685
+ saturationBoost: validatedSaturationBoost,
3686
+ borderOpacity: validatedBorderOpacity
3286
3687
  };
3287
3688
  return "undefined" == typeof process || process.env, finalConfig;
3288
3689
  }
@@ -3307,8 +3708,8 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
3307
3708
  };
3308
3709
  const curG = internalGlobalMousePositionRef.current, tgtG = targetGlobalMousePositionRef.current;
3309
3710
  internalGlobalMousePositionRef.current = {
3310
- x: lerp$1(curG.x, tgtG.x, CONSTANTS.LERP_FACTOR),
3311
- y: lerp$1(curG.y, tgtG.y, CONSTANTS.LERP_FACTOR)
3711
+ x: lerp(curG.x, tgtG.x, CONSTANTS.LERP_FACTOR),
3712
+ y: lerp(curG.y, tgtG.y, CONSTANTS.LERP_FACTOR)
3312
3713
  };
3313
3714
  // ── Calculate Elastic Physics ─────────────────────────────────────
3314
3715
  let targetElasticTranslation = {
@@ -3380,12 +3781,13 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
3380
3781
  withLiquidBlur: withLiquidBlur,
3381
3782
  blurAmount: blurAmount,
3382
3783
  saturation: saturation,
3383
- padding: padding,
3384
- isFixedOrSticky: isFixedOrSticky
3784
+ isFixedOrSticky: isFixedOrSticky,
3785
+ borderAnimated: resolvedBorder.animated,
3786
+ borderOpacityMultiplier: resolvedBorder.opacityMultiplier
3385
3787
  }), 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);
3386
3788
  };
3387
3789
  lerpRafRef.current = requestAnimationFrame(tick);
3388
- }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, padding, isFixedOrSticky, stopLerpLoop ]), handleGlobalMousePosition = React.useCallback((globalPos => {
3790
+ }), [ glassRef, wrapperRef, glassSize, isHovered, isActive, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, onClick, withLiquidBlur, blurAmount, saturation, isFixedOrSticky, resolvedBorder.animated, resolvedBorder.opacityMultiplier, stopLerpLoop ]), handleGlobalMousePosition = React.useCallback((globalPos => {
3389
3791
  if (externalGlobalMousePosition && externalMouseOffset) return;
3390
3792
  if (effectiveWithoutEffects) return;
3391
3793
  const container = mouseContainer?.current || glassRef.current;
@@ -3449,9 +3851,10 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
3449
3851
  withLiquidBlur: withLiquidBlur,
3450
3852
  blurAmount: blurAmount,
3451
3853
  saturation: saturation,
3452
- padding: padding
3854
+ borderAnimated: resolvedBorder.animated,
3855
+ borderOpacityMultiplier: resolvedBorder.opacityMultiplier
3453
3856
  });
3454
- }), [ isHovered, isActive, glassSize, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, wrapperRef, glassRef, externalMouseOffset, externalGlobalMousePosition, withLiquidBlur, blurAmount, saturation, padding, onClick ]);
3857
+ }), [ isHovered, isActive, glassSize, overLightConfig, effectiveBorderRadius, effectiveWithoutEffects, effectiveReducedMotion, elasticity, wrapperRef, glassRef, externalMouseOffset, externalGlobalMousePosition, withLiquidBlur, blurAmount, saturation, resolvedBorder.animated, resolvedBorder.opacityMultiplier, onClick ]);
3455
3858
  // Event handlers
3456
3859
  const handleMouseEnter = React.useCallback((() => setIsHovered(!0)), []), handleMouseLeave = React.useCallback((() => setIsHovered(!1)), []), handleMouseDown = React.useCallback((() => setIsActive(!0)), []), handleMouseUp = React.useCallback((() => setIsActive(!1)), []), handleKeyDown = React.useCallback((e => {
3457
3860
  !onClick || "Enter" !== e.key && " " !== e.key || (e.preventDefault(), onClick());
@@ -3471,9 +3874,8 @@ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION, animationSpeed: a
3471
3874
  mouseOffset: mouseOffset,
3472
3875
  // This is now static (refs or props) unless prop changes
3473
3876
  overLightConfig: overLightConfig,
3877
+ resolvedBorder: resolvedBorder,
3474
3878
  transformStyle: transformStyle,
3475
- getShaderTime: getShaderTime,
3476
- applyTimeBasedDistortion: applyTimeBasedDistortion,
3477
3879
  handleMouseEnter: handleMouseEnter,
3478
3880
  handleMouseLeave: handleMouseLeave,
3479
3881
  handleMouseDown: handleMouseDown,
@@ -3751,7 +4153,13 @@ function usePerformanceMonitor$1(config = {}) {
3751
4153
  timestamp: 0,
3752
4154
  isAutoScaling: !0,
3753
4155
  lowFpsCount: 0
3754
- }), [manualOverride, setManualOverride] = React.useState(!1), [isEnabled, setIsEnabled] = React.useState(enabled), frameCountRef = React.useRef(0), lastFpsUpdateRef = React.useRef(0), lastFrameTimeRef = React.useRef(0), animationFrameRef = React.useRef(null), lowFpsCountRef = React.useRef(0), highFpsCountRef = React.useRef(0), qualityLevelRef = React.useRef("medium"), updateMetrics = React.useCallback((newMetrics => {
4156
+ }), [manualOverride, setManualOverride] = React.useState(!1), [isEnabled, setIsEnabled] = React.useState(enabled);
4157
+ // Sync external `enabled` prop changes into internal state
4158
+ React.useEffect((() => {
4159
+ setIsEnabled(enabled ?? !0);
4160
+ }), [ enabled ]);
4161
+ // Refs for frame tracking
4162
+ const frameCountRef = React.useRef(0), lastFpsUpdateRef = React.useRef(0), lastFrameTimeRef = React.useRef(0), animationFrameRef = React.useRef(null), lowFpsCountRef = React.useRef(0), highFpsCountRef = React.useRef(0), qualityLevelRef = React.useRef("medium"), updateMetrics = React.useCallback((newMetrics => {
3755
4163
  setMetrics((prev => ({
3756
4164
  ...prev,
3757
4165
  ...newMetrics,
@@ -3872,163 +4280,15 @@ function usePerformanceMonitor$1(config = {}) {
3872
4280
  * Reset to auto-scaling mode
3873
4281
  */ var fps, currentQuality;
3874
4282
  return {
3875
- metrics: metrics,
3876
- recommendedQuality: (fps = metrics.fps, currentQuality = metrics.qualityLevel, fps >= 58 ? "high" : fps >= 45 ? "high" === currentQuality ? "high" : "medium" : "low"),
3877
- isUnderperforming: metrics.fps < minFps,
3878
- setQualityLevel: setQualityLevel,
3879
- resetAutoScaling: resetAutoScaling,
3880
- toggleMonitoring: toggleMonitoring
3881
- };
3882
- }
3883
-
3884
- /**
3885
- * Debug Overlay Component (Optional)
3886
- *
3887
- * Shows real-time performance metrics on screen.
3888
- * Only rendered when showOverlay is enabled.
3889
- */
3890
- /** Map an FPS value to a semantic color token string. */
3891
- const getQualityColor = quality => {
3892
- switch (quality) {
3893
- case "high":
3894
- return "var(--atomix-color-success, #4ade80)";
3895
-
3896
- case "medium":
3897
- return "var(--atomix-color-warning, #fbbf24)";
3898
-
3899
- case "low":
3900
- return "var(--atomix-color-danger, #ef4444)";
3901
-
3902
- default:
3903
- return "#9ca3af";
3904
- }
3905
- }, getFpsLabel = fps => fps >= 58 ? "Optimal" : fps >= 45 ? "Warning" : "Critical";
3906
-
3907
- /** Map a quality level string to a semantic color token string. */
3908
- // Inject keyframes once
3909
- if ("undefined" != typeof document) {
3910
- const styleId = "perf-dashboard-keyframes";
3911
- if (!document.getElementById(styleId)) {
3912
- const styleEl = document.createElement("style");
3913
- styleEl.id = styleId, styleEl.textContent = "\n@keyframes perf-dashboard-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n",
3914
- document.head.appendChild(styleEl);
3915
- }
4283
+ metrics: metrics,
4284
+ recommendedQuality: (fps = metrics.fps, currentQuality = metrics.qualityLevel, fps >= 58 ? "high" : fps >= 45 ? "high" === currentQuality ? "high" : "medium" : "low"),
4285
+ isUnderperforming: metrics.fps < minFps,
4286
+ setQualityLevel: setQualityLevel,
4287
+ resetAutoScaling: resetAutoScaling,
4288
+ toggleMonitoring: toggleMonitoring
4289
+ };
3916
4290
  }
3917
4291
 
3918
- /**
3919
- * PerformanceDashboard - Real-time performance monitoring overlay.
3920
- *
3921
- * Displays FPS, frame time, quality level, GPU memory, and auto-scaling status.
3922
- * Rendered only when `debugPerformance={true}` on the parent `AtomixGlass`.
3923
- */ const PerformanceDashboard = React.memo((({metrics: metrics, isVisible: isVisible = !0, onClose: onClose}) => {
3924
- if (!isVisible) return null;
3925
- const fpsColor = (fps = metrics.fps) >= 58 ? "var(--atomix-color-success, #4ade80)" : fps >= 45 ? "var(--atomix-color-warning, #fbbf24)" : "var(--atomix-color-danger, #ef4444)";
3926
- var fps;
3927
- const isCritical = metrics.fps < 45;
3928
- return jsxRuntime.jsxs("div", {
3929
- 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",
3930
- style: {
3931
- zIndex: 9999,
3932
- minWidth: "12.5rem",
3933
- // 200px
3934
- backgroundColor: "rgba(17, 24, 39, 0.95)",
3935
- backdropFilter: "blur(8px)",
3936
- transition: "opacity 0.3s ease"
3937
- },
3938
- children: [ jsxRuntime.jsxs("div", {
3939
- className: "u-flex u-items-center u-justify-between u-mb-2 u-pb-2 u-border-b u-border-white-alpha-10",
3940
- children: [ jsxRuntime.jsx("span", {
3941
- className: "u-text-sm u-font-bold u-text-white",
3942
- children: "Performance Monitor"
3943
- }), onClose && jsxRuntime.jsx("button", {
3944
- 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",
3945
- onClick: onClose,
3946
- "aria-label": "Close performance dashboard",
3947
- style: {
3948
- transition: "color 0.2s ease"
3949
- },
3950
- children: "×"
3951
- }) ]
3952
- }), jsxRuntime.jsxs("div", {
3953
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
3954
- children: [ jsxRuntime.jsx("span", {
3955
- className: "u-text-gray-400 u-me-3",
3956
- children: "FPS"
3957
- }), jsxRuntime.jsx("span", {
3958
- className: "u-font-bold",
3959
- style: {
3960
- color: fpsColor
3961
- },
3962
- children: Math.round(metrics.fps)
3963
- }) ]
3964
- }), jsxRuntime.jsxs("div", {
3965
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
3966
- children: [ jsxRuntime.jsx("span", {
3967
- className: "u-text-gray-400 u-me-3",
3968
- children: "Frame Time"
3969
- }), jsxRuntime.jsxs("span", {
3970
- className: "u-font-bold",
3971
- children: [ metrics.frameTime.toFixed(2), "ms" ]
3972
- }) ]
3973
- }), jsxRuntime.jsxs("div", {
3974
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
3975
- children: [ jsxRuntime.jsx("span", {
3976
- className: "u-text-gray-400 u-me-3",
3977
- children: "Quality"
3978
- }), jsxRuntime.jsx("span", {
3979
- className: "u-font-bold u-text-uppercase",
3980
- style: {
3981
- fontSize: "0.6875rem",
3982
- // 11px
3983
- color: getQualityColor(metrics.qualityLevel)
3984
- },
3985
- children: metrics.qualityLevel
3986
- }) ]
3987
- }), metrics.gpuMemory && jsxRuntime.jsxs("div", {
3988
- className: "u-flex u-items-center u-justify-between u-mb-1-5",
3989
- children: [ jsxRuntime.jsx("span", {
3990
- className: "u-text-gray-400 u-me-3",
3991
- children: "GPU Memory"
3992
- }), jsxRuntime.jsxs("span", {
3993
- className: "u-font-bold",
3994
- children: [ "~", Math.round(metrics.gpuMemory / 1024), "MB" ]
3995
- }) ]
3996
- }), metrics.isAutoScaling && jsxRuntime.jsx("div", {
3997
- className: "u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10 u-text-center",
3998
- style: {
3999
- fontSize: "0.625rem",
4000
- // 10px
4001
- color: "#6b7280"
4002
- },
4003
- children: "Auto-scaling active"
4004
- }), jsxRuntime.jsxs("div", {
4005
- className: "u-flex u-items-center u-gap-2 u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10",
4006
- children: [ jsxRuntime.jsx("div", {
4007
- className: "u-rounded-full",
4008
- style: {
4009
- width: "0.5rem",
4010
- height: "0.5rem",
4011
- flexShrink: 0,
4012
- backgroundColor: fpsColor,
4013
- ...isCritical && {
4014
- animation: "perf-dashboard-pulse 1s infinite"
4015
- }
4016
- }
4017
- }), jsxRuntime.jsx("span", {
4018
- className: "u-text-xs",
4019
- style: {
4020
- fontSize: "0.625rem",
4021
- // 10px
4022
- color: fpsColor
4023
- },
4024
- children: getFpsLabel(metrics.fps)
4025
- }) ]
4026
- }) ]
4027
- });
4028
- }));
4029
-
4030
- PerformanceDashboard.displayName = "PerformanceDashboard";
4031
-
4032
4292
  /**
4033
4293
  * Mobile optimization presets
4034
4294
  *
@@ -4038,8 +4298,7 @@ PerformanceDashboard.displayName = "PerformanceDashboard";
4038
4298
  /**
4039
4299
  * Performance preset - Maximum FPS, reduced quality
4040
4300
  * Best for low-end devices or when battery saving is priority
4041
- */
4042
- const PERFORMANCE_PRESET = {
4301
+ */ const PERFORMANCE_PRESET = {
4043
4302
  distortionOctaves: 2,
4044
4303
  // Minimal FBM layers
4045
4304
  displacementScale: 50,
@@ -4202,18 +4461,13 @@ function getDevicePreset(presetName) {
4202
4461
  getPixelRatio: () => "undefined" == typeof window ? 1 : window.devicePixelRatio || 1,
4203
4462
  /** Check if device has touch support */
4204
4463
  hasTouchSupport: () => "undefined" != typeof window && ("ontouchstart" in window || navigator.maxTouchPoints > 0)
4205
- }, AtomixGlassInner = React.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) {
4206
- const glassRef = React.useRef(null), contentRef = React.useRef(null), internalWrapperRef = React.useRef(null), mergedRef = React.useMemo((() =>
4207
- // Helper to merge refs
4208
- function(...refs) {
4209
- return node => {
4210
- refs.forEach((ref => {
4211
- "function" == typeof ref ? ref(node) : null != ref && (ref.current = node);
4212
- }));
4213
- };
4214
- }
4215
- // Internal implementation with forwardRef
4216
- (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({
4464
+ }, AtomixGlassInner = React.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) {
4465
+ const glassRef = React.useRef(null), contentRef = React.useRef(null), internalWrapperRef = React.useRef(null), mergedRef = useForkRef(ref, internalWrapperRef), {zIndex: customZIndex, ...restStyle} = style, isFixedOrSticky = (explicit = propsIsFixedOrSticky,
4466
+ position = restStyle.position, Boolean(explicit || "fixed" === position || "sticky" === position));
4467
+ var explicit, position;
4468
+ /**
4469
+ * Extracts layout-related properties from a React `CSSProperties` object.
4470
+ */ const {isHovered: isHovered, isActive: isActive, glassSize: glassSize, effectiveBorderRadius: effectiveBorderRadius, effectiveReducedMotion: effectiveReducedMotion, effectiveHighContrast: effectiveHighContrast, effectiveWithoutEffects: effectiveWithoutEffects, overLightConfig: overLightConfig, globalMousePosition: globalMousePosition, mouseOffset: mouseOffset, transformStyle: transformStyle, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleMouseDown: handleMouseDown, handleMouseUp: handleMouseUp, handleKeyDown: handleKeyDown, resolvedBorder: resolvedBorder} = useAtomixGlass({
4217
4471
  glassRef: glassRef,
4218
4472
  contentRef: contentRef,
4219
4473
  wrapperRef: internalWrapperRef,
@@ -4233,19 +4487,12 @@ function getDevicePreset(presetName) {
4233
4487
  blurAmount: blurAmount,
4234
4488
  saturation: saturation,
4235
4489
  withLiquidBlur: withLiquidBlur,
4236
- padding: padding,
4490
+ border: border,
4491
+ withBorder: withBorder,
4492
+ debugBorderRadius: debugBorderRadius,
4237
4493
  style: style,
4238
- isFixedOrSticky: isFixedOrSticky,
4239
- // Phase 1: Animation System props
4240
- withTimeAnimation: withTimeAnimation,
4241
- animationSpeed: animationSpeed,
4242
- withMultiLayerDistortion: withMultiLayerDistortion,
4243
- distortionOctaves: distortionOctaves,
4244
- distortionLacunarity: distortionLacunarity,
4245
- distortionGain: distortionGain,
4246
- distortionQuality: distortionQuality
4494
+ isFixedOrSticky: isFixedOrSticky
4247
4495
  });
4248
- // Responsive breakpoint system - automatically adjusts parameters based on viewport
4249
4496
  useResponsiveGlass({
4250
4497
  baseParams: {
4251
4498
  ...React.useMemo((() => getDevicePreset(devicePreset)), [ devicePreset ]),
@@ -4260,185 +4507,213 @@ function getDevicePreset(presetName) {
4260
4507
  breakpoints: MOBILE_OPTIMIZED_BREAKPOINTS,
4261
4508
  enabled: !disableResponsiveBreakpoints && "undefined" != typeof window,
4262
4509
  debug: !1
4263
- });
4264
- // Performance monitoring - tracks FPS, frame time, memory usage
4265
- const {metrics: performanceMetrics, toggleMonitoring: toggleMonitoring} = usePerformanceMonitor$1({
4510
+ }), usePerformanceMonitor$1({
4266
4511
  enabled: debugPerformance,
4267
- // Enable when debugPerformance is true
4268
4512
  debug: !1,
4269
4513
  showOverlay: !1
4270
4514
  });
4271
- // Auto-start performance monitoring when debugPerformance is enabled
4272
- React__default.default.useEffect((() => {
4273
- debugPerformance && toggleMonitoring();
4274
- // eslint-disable-next-line react-hooks/exhaustive-deps
4275
- }), [ debugPerformance ]);
4276
- // Re-run when debugPerformance changes
4277
- const isOverLight = React.useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, rootLayoutStyle = React.useMemo((() => {
4278
- if (!isFixedOrSticky) return {};
4279
- const {position: p, top: t, left: l, right: r, bottom: b} = restStyle;
4280
- return {
4281
- ...p && {
4282
- position: p
4283
- },
4284
- ...void 0 !== t && {
4285
- top: t
4286
- },
4287
- ...void 0 !== l && {
4288
- left: l
4289
- },
4290
- ...void 0 !== r && {
4291
- right: r
4292
- },
4293
- ...void 0 !== b && {
4294
- bottom: b
4295
- }
4296
- };
4297
- }), [ isFixedOrSticky, restStyle ]);
4298
- // Calculate base style with transforms
4299
- // When layout is hoisted to the root, strip those props from the container
4300
- React.useMemo((() => {
4301
- if (isFixedOrSticky) {
4302
- const {position: _p, top: _t, left: _l, right: _r, bottom: _b, ...visualStyle} = restStyle;
4303
- return {
4304
- ...visualStyle
4305
- };
4515
+ const isOverLight = React.useMemo((() => overLightConfig.isOverLight), [ overLightConfig.isOverLight ]), shouldRenderOverLightLayers = withOverLightLayers && isOverLight, containerStyle = React.useMemo((() => ({
4516
+ ...restStyle,
4517
+ ...void 0 !== customZIndex && {
4518
+ zIndex: customZIndex
4306
4519
  }
4520
+ })), [ 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 = React.useMemo((() =>
4521
+ /**
4522
+ * Returns the internal positioning context for effect layers relative to the root.
4523
+ */
4524
+ function(isFixedOrSticky, restStyle) {
4307
4525
  return {
4308
- ...restStyle
4526
+ position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
4527
+ top: 0,
4528
+ left: 0,
4529
+ right: "auto",
4530
+ bottom: "auto"
4309
4531
  };
4310
- }), [ isFixedOrSticky, restStyle ]);
4311
- // Build className with state modifiers
4312
- 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 = React.useMemo((() => ({
4313
- position: isFixedOrSticky ? "absolute" : restStyle.position || "absolute",
4314
- top: isFixedOrSticky ? restStyle.top ?? 0 : 0,
4315
- left: isFixedOrSticky ? restStyle.left ?? 0 : 0,
4316
- right: isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
4317
- bottom: isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto"
4318
- })), [ isFixedOrSticky, restStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), adjustedSize = React.useMemo((() => {
4319
- // Keep a reference to positionStyles to avoid unused-variable lint,
4320
- // but sizing is driven by explicit width/height or measured size.
4321
- positionStyles.position;
4322
- 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;
4532
+ }
4533
+ /**
4534
+ * Computes `--atomix-glass-width` and `--atomix-glass-height` values.
4535
+ *
4536
+ * Fixed/sticky elements prefer explicit dimensions or measured size; in-flow
4537
+ * elements default to `100%`.
4538
+ */ (isFixedOrSticky, restStyle)), [ isFixedOrSticky, restStyle ]), adjustedSize = React.useMemo((() => function(options) {
4539
+ 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;
4323
4540
  return {
4324
4541
  width: resolveLength(effectiveWidth, glassSize.width),
4325
4542
  height: resolveLength(effectiveHeight, glassSize.height)
4326
4543
  };
4327
- }), [ width, height, restStyle.width, restStyle.height, positionStyles.position, glassSize.width, glassSize.height, isFixedOrSticky ]), gradientValues = React.useMemo((() => {
4328
- const mx = mouseOffset.x, my = mouseOffset.y, absMx = Math.abs(mx), absMy = Math.abs(my), GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
4329
- return {
4330
- borderGradientAngle: GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER,
4331
- borderStop1: Math.max(GRADIENT.BORDER_STOP_1.MIN, GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER),
4332
- borderStop2: Math.min(GRADIENT.BORDER_STOP_2.MAX, GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER),
4333
- 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 ],
4334
- hoverPositions: {
4335
- hover1: {
4336
- x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
4337
- y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1
4338
- },
4339
- hover2: {
4340
- x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2,
4341
- y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2
4342
- },
4343
- hover3: {
4344
- x: GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
4345
- y: GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3
4346
- }
4347
- },
4348
- basePosition: {
4349
- x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
4350
- y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER
4351
- },
4352
- mx: mx,
4353
- my: my,
4354
- absMx: absMx,
4355
- absMy: absMy
4356
- };
4357
- }), [ 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 = React.useMemo((() => ({
4358
- hover1: isHovered || isActive ? .5 : 0,
4359
- hover2: isActive ? .5 : 0,
4360
- hover3: isHovered ? .4 : isActive ? .8 : 0,
4361
- base: isOverLight ? clampedOverLightOpacity || .4 : 0,
4362
- over: isOverLight ? 1.1 * (clampedOverLightOpacity || .4) : 0
4363
- })), [ isHovered, isActive, isOverLight, clampedOverLightOpacity ]), glassVars = React.useMemo((() => {
4364
- 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;
4544
+ }
4545
+ /**
4546
+ * Builds the CSS custom properties applied to the root `.c-atomix-glass` element.
4547
+ *
4548
+ * These variables drive layer geometry, transforms, and stacking offsets. They
4549
+ * must not include layout properties that would interfere with backdrop-filter.
4550
+ */ ({
4551
+ width: width,
4552
+ height: height,
4553
+ restStyle: restStyle,
4554
+ glassSize: glassSize,
4555
+ isFixedOrSticky: isFixedOrSticky
4556
+ })), [ width, height, restStyle, glassSize, isFixedOrSticky ]), glassVars = React.useMemo((() => function(input) {
4557
+ 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 =
4558
+ /**
4559
+ * Resolves the `--atomix-glass-position` value for decorative layers.
4560
+ *
4561
+ * Fixed/sticky layers use the same positioning mode as the container; in-flow
4562
+ * layers default to the internal absolute positioning context.
4563
+ */
4564
+ function(isFixedOrSticky, positionStyles, restStyle) {
4565
+ return isFixedOrSticky ? `${function(style) {
4566
+ const {position: position, top: top, left: left, right: right, bottom: bottom, inset: inset} = style;
4567
+ return {
4568
+ ...null != position && {
4569
+ position: position
4570
+ },
4571
+ ...void 0 !== top && {
4572
+ top: top
4573
+ },
4574
+ ...void 0 !== left && {
4575
+ left: left
4576
+ },
4577
+ ...void 0 !== right && {
4578
+ right: right
4579
+ },
4580
+ ...void 0 !== bottom && {
4581
+ bottom: bottom
4582
+ },
4583
+ ...void 0 !== inset && {
4584
+ inset: inset
4585
+ }
4586
+ };
4587
+ }
4588
+ /**
4589
+ * Resolves inset custom properties for decorative layers (hover, borders, backgrounds).
4590
+ *
4591
+ * For fixed and sticky modes, insets mirror the container so sibling layers remain
4592
+ * aligned. In-flow modes, insets follow the consumer `style` when a non-default
4593
+ * `position` is provided.
4594
+ */ (restStyle).position ?? restStyle.position ?? "fixed"}` : `${positionStyles.position}`;
4595
+ }(isFixedOrSticky, positionStyles, restStyle), layerInsets = function(isFixedOrSticky, restStyle) {
4596
+ if (isFixedOrSticky) return {
4597
+ top: formatGlassInsetValue(restStyle.top, 0),
4598
+ left: formatGlassInsetValue(restStyle.left, 0),
4599
+ right: formatGlassInsetValue(restStyle.right, "auto"),
4600
+ bottom: formatGlassInsetValue(restStyle.bottom, "auto")
4601
+ };
4602
+ const position = restStyle.position;
4603
+ return null != position && "static" !== position && "relative" !== position ? {
4604
+ top: formatGlassInsetValue(restStyle.top, 0),
4605
+ left: formatGlassInsetValue(restStyle.left, 0),
4606
+ right: formatGlassInsetValue(restStyle.right, "auto"),
4607
+ bottom: formatGlassInsetValue(restStyle.bottom, "auto")
4608
+ } : {
4609
+ top: "0px",
4610
+ left: "0px",
4611
+ right: "auto",
4612
+ bottom: "auto"
4613
+ };
4614
+ }(isFixedOrSticky, restStyle);
4365
4615
  return {
4366
4616
  ...void 0 !== customZIndex && {
4367
4617
  "--atomix-glass-base-z-index": customZIndex
4368
4618
  },
4369
4619
  "--atomix-glass-radius": `${effectiveBorderRadius}px`,
4370
4620
  "--atomix-glass-transform": transformStyle || "none",
4371
- "--atomix-glass-container-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
4372
- "--atomix-glass-position": `${isFixedOrSticky ? rootLayoutStyle.position : positionStyles.position}`,
4373
- "--atomix-glass-top": `${isFixedOrSticky ? restStyle.top ?? 0 : 0}px`,
4374
- "--atomix-glass-left": `${isFixedOrSticky ? restStyle.left ?? 0 : 0}px`,
4375
- "--atomix-glass-right": isFixedOrSticky ? restStyle.right ?? "auto" : "auto",
4376
- "--atomix-glass-bottom": isFixedOrSticky ? restStyle.bottom ?? "auto" : "auto",
4621
+ "--atomix-glass-container-position": layerPosition,
4622
+ "--atomix-glass-position": layerPosition,
4623
+ "--atomix-glass-top": layerInsets.top,
4624
+ "--atomix-glass-left": layerInsets.left,
4625
+ "--atomix-glass-right": layerInsets.right,
4626
+ "--atomix-glass-bottom": layerInsets.bottom,
4377
4627
  "--atomix-glass-width": adjustedSize.width,
4378
4628
  "--atomix-glass-height": adjustedSize.height,
4379
- "--atomix-glass-border-width": "var(--atomix-spacing-0-5, 0.125rem)",
4380
- "--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay",
4381
- "--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%)`,
4382
- "--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%)`,
4383
- "--atomix-glass-hover-1-opacity": opacityValues.hover1,
4384
- "--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}%)`,
4385
- "--atomix-glass-hover-2-opacity": opacityValues.hover2,
4386
- "--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}%)`,
4387
- "--atomix-glass-hover-3-opacity": opacityValues.hover3,
4388
- "--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}%)`,
4389
- "--atomix-glass-base-opacity": opacityValues.base,
4390
- "--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})`,
4391
- "--atomix-glass-overlay-opacity": opacityValues.over,
4392
- "--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})`,
4393
- "--atomix-glass-overlay-highlight-opacity": opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER,
4394
- "--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}%)`
4629
+ // Aliases maintained for backward compatibility and consumer overrides.
4630
+ "--atomix-glass-container-width": adjustedSize.width,
4631
+ "--atomix-glass-container-height": adjustedSize.height,
4632
+ [ATOMIX_GLASS.BORDER.WIDTH_CSS_VAR]: borderWidth,
4633
+ "--atomix-glass-blend-mode": isOverLight ? "multiply" : "overlay"
4634
+ };
4635
+ }
4636
+ /**
4637
+ * Applies mode-specific multipliers and accessibility overrides to container effects.
4638
+ */ ({
4639
+ effectiveBorderRadius: effectiveBorderRadius,
4640
+ transformStyle: transformStyle,
4641
+ adjustedSize: adjustedSize,
4642
+ isOverLight: isOverLight,
4643
+ customZIndex: customZIndex,
4644
+ isFixedOrSticky: isFixedOrSticky,
4645
+ positionStyles: positionStyles,
4646
+ restStyle: restStyle,
4647
+ borderWidth: resolvedBorder.width
4648
+ })), [ effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, customZIndex, isFixedOrSticky, positionStyles, restStyle, resolvedBorder.width ]), containerEffects = React.useMemo((() => function(options) {
4649
+ const {MULTIPLIERS: MULTIPLIERS, SATURATION: SATURATION} = ATOMIX_GLASS.CONSTANTS, zeroMouse = {
4650
+ x: 0,
4651
+ y: 0
4652
+ }, resolveSaturation = () => options.effectiveHighContrast ? SATURATION.HIGH_CONTRAST : options.isOverLight ? options.saturation * options.saturationBoost : options.saturation;
4653
+ if (options.effectiveWithoutEffects) return {
4654
+ displacementScale: 0,
4655
+ blurAmount: 0,
4656
+ saturation: resolveSaturation(),
4657
+ aberrationIntensity: 0,
4658
+ mouseOffset: zeroMouse,
4659
+ globalMousePosition: zeroMouse
4660
+ };
4661
+ let resolvedDisplacement = options.displacementScale;
4662
+ "shader" === options.mode ? resolvedDisplacement *= MULTIPLIERS.SHADER_DISPLACEMENT : options.isOverLight && (resolvedDisplacement *= MULTIPLIERS.OVER_LIGHT_DISPLACEMENT);
4663
+ let resolvedAberration = options.aberrationIntensity;
4664
+ return "shader" === options.mode && (resolvedAberration *= MULTIPLIERS.SHADER_ABERRATION),
4665
+ {
4666
+ displacementScale: resolvedDisplacement,
4667
+ blurAmount: options.blurAmount,
4668
+ saturation: resolveSaturation(),
4669
+ aberrationIntensity: resolvedAberration,
4670
+ mouseOffset: options.mouseOffset,
4671
+ globalMousePosition: options.globalMousePosition
4395
4672
  };
4396
- }), [ gradientValues, opacityValues, effectiveBorderRadius, transformStyle, adjustedSize, isOverLight, clampedBorderOpacity, customZIndex, isFixedOrSticky, positionStyles.position, rootLayoutStyle.position, restStyle.top, restStyle.left, restStyle.right, restStyle.bottom ]), renderBackgroundLayer = layerType => jsxRuntime.jsx("div", {
4397
- 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(" ")
4673
+ }({
4674
+ displacementScale: displacementScale,
4675
+ blurAmount: blurAmount,
4676
+ saturation: saturation,
4677
+ aberrationIntensity: aberrationIntensity,
4678
+ mode: mode,
4679
+ effectiveWithoutEffects: effectiveWithoutEffects,
4680
+ effectiveHighContrast: effectiveHighContrast,
4681
+ isOverLight: isOverLight,
4682
+ saturationBoost: overLightConfig.saturationBoost,
4683
+ mouseOffset: mouseOffset,
4684
+ globalMousePosition: globalMousePosition
4685
+ })), [ displacementScale, blurAmount, saturation, aberrationIntensity, mode, effectiveWithoutEffects, effectiveHighContrast, isOverLight, overLightConfig.saturationBoost, mouseOffset, globalMousePosition ]), renderBackgroundLayer = layerType => jsxRuntime.jsx("div", {
4686
+ "aria-hidden": "true",
4687
+ 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)
4398
4688
  });
4399
- // Calculate position and size styles for internal layers
4400
- // When root is fixed/sticky, internal layers use absolute (relative to root)
4401
- return jsxRuntime.jsxs("div", {
4689
+ return jsxRuntime.jsxs("div", {
4402
4690
  ...rest,
4403
4691
  ref: mergedRef,
4404
4692
  className: componentClassName,
4405
- style: {
4406
- ...glassVars
4407
- },
4693
+ style: glassVars,
4408
4694
  role: role || (onClick ? "button" : void 0),
4409
4695
  tabIndex: onClick ? tabIndex ?? 0 : tabIndex,
4410
4696
  "aria-label": ariaLabel,
4411
4697
  "aria-describedby": ariaDescribedBy,
4412
4698
  "aria-disabled": !(!onClick || !effectiveWithoutEffects) || !onClick && void 0,
4413
- "aria-pressed": onClick ? isActive : void 0,
4414
4699
  onKeyDown: onClick ? handleKeyDown : void 0,
4415
4700
  children: [ jsxRuntime.jsx(AtomixGlassContainer, {
4416
4701
  ref: glassRef,
4417
4702
  contentRef: contentRef,
4418
4703
  className: className,
4419
- style: {
4420
- ...restStyle
4421
- },
4704
+ style: containerStyle,
4422
4705
  borderRadius: effectiveBorderRadius,
4423
- displacementScale: effectiveWithoutEffects ? 0 : "shader" === mode ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_DISPLACEMENT : isOverLight ? displacementScale * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.OVER_LIGHT_DISPLACEMENT : displacementScale,
4424
- blurAmount: effectiveWithoutEffects ? 0 : blurAmount,
4425
- saturation: effectiveHighContrast ? ATOMIX_GLASS.CONSTANTS.SATURATION.HIGH_CONTRAST : isOverLight ? saturation * overLightConfig.saturationBoost : saturation,
4426
- aberrationIntensity: effectiveWithoutEffects ? 0 : "shader" === mode ? aberrationIntensity * ATOMIX_GLASS.CONSTANTS.MULTIPLIERS.SHADER_ABERRATION : aberrationIntensity,
4706
+ displacementScale: containerEffects.displacementScale,
4707
+ blurAmount: containerEffects.blurAmount,
4708
+ saturation: containerEffects.saturation,
4709
+ aberrationIntensity: containerEffects.aberrationIntensity,
4427
4710
  glassSize: glassSize,
4428
- padding: padding,
4429
- mouseOffset: effectiveWithoutEffects ? {
4430
- x: 0,
4431
- y: 0
4432
- } : mouseOffset,
4433
- globalMousePosition: effectiveWithoutEffects ? {
4434
- x: 0,
4435
- y: 0
4436
- } : globalMousePosition,
4711
+ mouseOffset: containerEffects.mouseOffset,
4712
+ globalMousePosition: containerEffects.globalMousePosition,
4437
4713
  onMouseEnter: handleMouseEnter,
4438
4714
  onMouseLeave: handleMouseLeave,
4439
4715
  onMouseDown: handleMouseDown,
4440
4716
  onMouseUp: handleMouseUp,
4441
- isHovered: isHovered,
4442
4717
  isActive: isActive,
4443
4718
  overLight: isOverLight,
4444
4719
  overLightConfig: {
@@ -4454,8 +4729,6 @@ function getDevicePreset(presetName) {
4454
4729
  shaderVariant: shaderVariant,
4455
4730
  withLiquidBlur: withLiquidBlur,
4456
4731
  isFixedOrSticky: isFixedOrSticky,
4457
- // Phase 1: Animation System props
4458
- shaderTime: getShaderTime(),
4459
4732
  withTimeAnimation: withTimeAnimation,
4460
4733
  animationSpeed: animationSpeed,
4461
4734
  withMultiLayerDistortion: withMultiLayerDistortion,
@@ -4466,42 +4739,129 @@ function getDevicePreset(presetName) {
4466
4739
  children: children
4467
4740
  }), Boolean(onClick) && jsxRuntime.jsxs(jsxRuntime.Fragment, {
4468
4741
  children: [ jsxRuntime.jsx("div", {
4742
+ "aria-hidden": "true",
4469
4743
  className: ATOMIX_GLASS.HOVER_1_CLASS
4470
4744
  }), jsxRuntime.jsx("div", {
4745
+ "aria-hidden": "true",
4471
4746
  className: ATOMIX_GLASS.HOVER_2_CLASS
4472
4747
  }), jsxRuntime.jsx("div", {
4748
+ "aria-hidden": "true",
4473
4749
  className: ATOMIX_GLASS.HOVER_3_CLASS
4474
4750
  }) ]
4475
- }), renderBackgroundLayer("dark"), renderBackgroundLayer("black"), shouldRenderOverLightLayers && jsxRuntime.jsxs(jsxRuntime.Fragment, {
4751
+ }), [ "dark", "black" ].map((layerType => jsxRuntime.jsx(React__default.default.Fragment, {
4752
+ children: renderBackgroundLayer(layerType)
4753
+ }, layerType))), shouldRenderOverLightLayers && jsxRuntime.jsxs(jsxRuntime.Fragment, {
4476
4754
  children: [ jsxRuntime.jsx("div", {
4755
+ "aria-hidden": "true",
4477
4756
  className: ATOMIX_GLASS.BASE_LAYER_CLASS
4478
4757
  }), jsxRuntime.jsx("div", {
4758
+ "aria-hidden": "true",
4479
4759
  className: ATOMIX_GLASS.OVERLAY_LAYER_CLASS
4480
4760
  }), jsxRuntime.jsx("div", {
4761
+ "aria-hidden": "true",
4481
4762
  className: ATOMIX_GLASS.OVERLAY_HIGHLIGHT_CLASS
4482
4763
  }) ]
4483
- }), withBorder && jsxRuntime.jsxs(jsxRuntime.Fragment, {
4764
+ }), resolvedBorder.enabled && jsxRuntime.jsxs(jsxRuntime.Fragment, {
4484
4765
  children: [ jsxRuntime.jsx("span", {
4766
+ "aria-hidden": "true",
4485
4767
  className: ATOMIX_GLASS.BORDER_BACKDROP_CLASS
4486
4768
  }), jsxRuntime.jsx("span", {
4769
+ "aria-hidden": "true",
4487
4770
  className: ATOMIX_GLASS.BORDER_1_CLASS
4488
4771
  }), jsxRuntime.jsx("span", {
4772
+ "aria-hidden": "true",
4489
4773
  className: ATOMIX_GLASS.BORDER_2_CLASS
4490
4774
  }) ]
4491
- }), debugPerformance && performanceMetrics && jsxRuntime.jsx(PerformanceDashboard, {
4492
- metrics: performanceMetrics,
4493
- isVisible: !0,
4494
- onClose: () => {}
4495
4775
  }) ]
4496
4776
  });
4497
4777
  }));
4498
4778
 
4499
- AtomixGlassInner.displayName = "AtomixGlass";
4500
-
4501
4779
  /**
4502
- * AtomixGlass - wrapped with React.memo to prevent unnecessary re-renders.
4503
- * Ref is forwarded to the root `<div>` element.
4780
+ * AtomixGlass - A high-performance glass morphism component with liquid distortion effects
4781
+ *
4782
+ * Features:
4783
+ * - Hardware-accelerated glass effects with SVG filters
4784
+ * - Mouse-responsive liquid distortion
4785
+ * - Dynamic border-radius extraction from children CSS properties
4786
+ * - Automatic light/dark theme detection via overLight prop
4787
+ * - Accessibility and performance optimizations
4788
+ * - Multiple displacement modes (standard, polar, prominent, shader)
4789
+ * - Design token integration for consistent theming
4790
+ * - Focus ring support for keyboard navigation
4791
+ * - Responsive breakpoints for mobile optimization
4792
+ * - Enhanced ARIA attributes for screen readers
4793
+ * - Time-based animation system with FBM distortion
4794
+ * - Device preset optimization for performance/quality balance
4795
+ *
4796
+ * Design System Compliance:
4797
+ * - Uses design tokens for opacity, spacing, and colors
4798
+ * - Follows BEM methodology for class naming
4799
+ * - Implements focus-ring mixin for accessibility
4800
+ * - Supports reduced motion and high contrast preferences
4801
+ *
4802
+ * Style architecture:
4803
+ * - Root (`.c-atomix-glass`): CSS custom properties for layer geometry and motion.
4804
+ * - Container (`.c-atomix-glass__container`): layout, z-index, and backdrop-filter.
4805
+ *
4806
+ * @example
4807
+ * // Basic usage with dynamic border-radius extraction
4808
+ * <AtomixGlass>
4809
+ * <div style={{ borderRadius: '12px' }}>Content with 12px radius</div>
4810
+ * </AtomixGlass>
4811
+ *
4812
+ * @example
4813
+ * // Manual border-radius override
4814
+ * <AtomixGlass borderRadius={20}>
4815
+ * <div>Content with 20px glass radius</div>
4816
+ * </AtomixGlass>
4817
+ *
4818
+ * @example
4819
+ * // Interactive glass with click handler
4820
+ * <AtomixGlass onClick={() => console.log('Clicked')} aria-label="Glass card">
4821
+ * <div>Clickable content</div>
4822
+ * </AtomixGlass>
4823
+ *
4824
+ * @example
4825
+ * // OverLight - Boolean mode (explicit control)
4826
+ * <AtomixGlass overLight={true}>
4827
+ * <div>Content on light background</div>
4828
+ * </AtomixGlass>
4829
+ *
4830
+ * @example
4831
+ * // OverLight - Auto-detection mode
4832
+ * <AtomixGlass overLight="auto">
4833
+ * <div>Content with auto-detected background</div>
4834
+ * </AtomixGlass>
4835
+ *
4836
+ * @example
4837
+ * // OverLight - Object config with custom settings
4838
+ * <AtomixGlass
4839
+ * overLight={{
4840
+ * threshold: 0.8,
4841
+ * opacity: 0.6,
4842
+ * contrast: 1.8,
4843
+ * brightness: 1.0,
4844
+ * saturationBoost: 1.5
4845
+ * }}
4846
+ * >
4847
+ * <div>Content with custom overLight config</div>
4848
+ * </AtomixGlass>
4849
+ *
4850
+ * @example
4851
+ * // Debug mode for overLight detection
4852
+ * <AtomixGlass overLight="auto" debugOverLight={true}>
4853
+ * <div>Content with debug logging enabled</div>
4854
+ * </AtomixGlass>
4855
+ *
4856
+ * @example
4857
+ * // Performance-optimized for mobile devices
4858
+ * <AtomixGlass devicePreset="performance" disableResponsiveBreakpoints={false}>
4859
+ * <div>Mobile-optimized glass effect</div>
4860
+ * </AtomixGlass>
4504
4861
  */
4862
+ /** Internal implementation; ref is forwarded to the root wrapper element. */ AtomixGlassInner.displayName = "AtomixGlass";
4863
+
4864
+ /** Memoized public export. Ref targets the root `.c-atomix-glass` wrapper. */
4505
4865
  const AtomixGlass = React.memo(AtomixGlassInner), DefaultIcon = () => jsxRuntime.jsx("i", {
4506
4866
  className: "c-accordion__icon",
4507
4867
  "aria-hidden": "true",
@@ -4629,10 +4989,7 @@ const AccordionImpl = React.memo((({title: title, children: children, defaultOp
4629
4989
  });
4630
4990
  if (glass) {
4631
4991
  // Default glass settings for accordions
4632
- const defaultGlassProps = {
4633
- displacementScale: 20,
4634
- elasticity: 0
4635
- }, glassProps = !0 === glass ? defaultGlassProps : {
4992
+ const defaultGlassProps = GLASS_DEFAULTS.ACCORDION, glassProps = !0 === glass ? defaultGlassProps : {
4636
4993
  ...defaultGlassProps,
4637
4994
  ...glass
4638
4995
  };
@@ -4665,40 +5022,15 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4665
5022
  d: "M12 6c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z",
4666
5023
  fill: color
4667
5024
  }) ]
4668
- }), smoothStep = (a, b, t) => {
4669
- // Add input validation
4670
- if ("number" != typeof a || "number" != typeof b || "number" != typeof t) return 0;
4671
- const clamped = Math.max(0, Math.min(1, (t - a) / (b - a)));
4672
- return clamped * clamped * (3 - 2 * clamped);
4673
- }, calculateLength = (x, y) => {
4674
- // Add input validation and error handling
4675
- if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
4676
- // Prevent potential overflow
4677
- const maxX = Math.max(Math.abs(x), Math.abs(y));
4678
- if (0 === maxX) return 0;
4679
- const scaledX = x / maxX, scaledY = y / maxX;
4680
- return maxX * Math.sqrt(scaledX * scaledX + scaledY * scaledY);
4681
- }, roundedRectSDF = (x, y, width, height, radius) => {
5025
+ }), roundedRectSDF = (x, y, width, height, radius) => {
4682
5026
  // Add input validation
4683
5027
  if ("number" != typeof x || "number" != typeof y || "number" != typeof width || "number" != typeof height || "number" != typeof radius) return 0;
4684
5028
  const qx = Math.abs(x) - width + radius, qy = Math.abs(y) - height + radius;
4685
- return Math.min(Math.max(qx, qy), 0) + calculateLength(Math.max(qx, 0), Math.max(qy, 0)) - radius;
5029
+ return Math.min(Math.max(qx, qy), 0) + vec2Length(Math.max(qx, 0), Math.max(qy, 0)) - radius;
4686
5030
  }, createTexture = (x, y) => ({
4687
5031
  x: "number" != typeof x || isNaN(x) ? .5 : Math.max(0, Math.min(1, x)),
4688
5032
  y: "number" != typeof y || isNaN(y) ? .5 : Math.max(0, Math.min(1, y))
4689
- }), validateVec2 = vec => vec && "number" == typeof vec.x && "number" == typeof vec.y && !isNaN(vec.x) && !isNaN(vec.y), clampValue = (value, min, max) =>
4690
- // Add input validation
4691
- "number" != typeof value || "number" != typeof min || "number" != typeof max || isNaN(value) ? min : isNaN(min) ? 0 : isNaN(max) ? 1 : Math.max(min, Math.min(max, value)), easeInOutCubic = t => {
4692
- // Add input validation
4693
- if ("number" != typeof t || isNaN(t)) return 0;
4694
- const clampedT = Math.max(0, Math.min(1, t));
4695
- return clampedT < .5 ? 4 * clampedT * clampedT * clampedT : 1 - Math.pow(-2 * clampedT + 2, 3) / 2;
4696
- }, easeOutQuart = t => {
4697
- // Add input validation
4698
- if ("number" != typeof t || isNaN(t)) return 0;
4699
- const clampedT = Math.max(0, Math.min(1, t));
4700
- return 1 - Math.pow(1 - clampedT, 4);
4701
- }, noise2D = (x, y) => {
5033
+ }), validateVec2 = vec => vec && "number" == typeof vec.x && "number" == typeof vec.y && !isNaN(vec.x) && !isNaN(vec.y), noise2D = (x, y) => {
4702
5034
  // Add input validation
4703
5035
  if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y)) return 0;
4704
5036
  const X = 255 & Math.floor(x), Y = 255 & Math.floor(y), xf = x - Math.floor(x), yf = y - Math.floor(y), u = easeInOutCubic(xf), v = easeInOutCubic(yf), hash = (i, j) => {
@@ -4737,13 +5069,13 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4737
5069
  x: .5,
4738
5070
  y: .5
4739
5071
  };
4740
- const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now(), mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = calculateLength(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(2 * mouseDistance, 1)), organicFlow = fbm(12 * (ix + .5 * mouseX) + time, 12 * (iy + .5 * mouseY) + .7 * time, 3) - .5, distanceToEdge = roundedRectSDF(ix, iy, .4, .3, .35), baseDisplacement = smoothStep(.8, 0, distanceToEdge - .05), radialDist = ((x, y, strength) => {
5072
+ const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now(), mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = vec2Length(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(2 * mouseDistance, 1)), organicFlow = fbm(12 * (ix + .5 * mouseX) + time, 12 * (iy + .5 * mouseY) + .7 * time, 3) - .5, distanceToEdge = roundedRectSDF(ix, iy, .4, .3, .35), baseDisplacement = smoothstepEdge(.8, 0, distanceToEdge - .05), radialDist = ((x, y, strength) => {
4741
5073
  // Add input validation
4742
5074
  if ("number" != typeof x || "number" != typeof y || isNaN(x) || isNaN(y) || isNaN(strength)) return {
4743
5075
  x: 0,
4744
5076
  y: 0
4745
5077
  };
4746
- const distance = calculateLength(x, y), distortion = Math.pow(Math.min(distance, 10), 2) * strength;
5078
+ const distance = vec2Length(x, y), distortion = Math.pow(Math.min(distance, 10), 2) * strength;
4747
5079
  // Limit distance to prevent extreme values
4748
5080
  return {
4749
5081
  x: x * (1 + distortion),
@@ -4755,7 +5087,7 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4755
5087
  x: 0,
4756
5088
  y: 0
4757
5089
  };
4758
- const distance = calculateLength(x, y);
5090
+ const distance = vec2Length(x, y);
4759
5091
  // Prevent division by zero and extreme values
4760
5092
  if (0 === distance) return {
4761
5093
  x: 0,
@@ -4766,8 +5098,8 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4766
5098
  x: Math.cos(angle) * distance * intensity,
4767
5099
  y: Math.sin(angle) * distance * intensity
4768
5100
  };
4769
- })(ix, iy, .015 * baseDisplacement), scaled = smoothStep(0, 1, 1.15 * baseDisplacement), finalX = ix + totalDistortionX + .5 * chromaticOffset.x, finalY = iy + totalDistortionY + .5 * chromaticOffset.y;
4770
- return createTexture(clampValue(finalX * scaled + .5, 0, 1), clampValue(finalY * scaled + .5, 0, 1));
5101
+ })(ix, iy, .015 * baseDisplacement), scaled = smoothstepEdge(0, 1, 1.15 * baseDisplacement), finalX = ix + totalDistortionX + .5 * chromaticOffset.x, finalY = iy + totalDistortionY + .5 * chromaticOffset.y;
5102
+ return createTexture(clamp(finalX * scaled + .5, 0, 1), clamp(finalY * scaled + .5, 0, 1));
4771
5103
  },
4772
5104
  // Premium Apple-style fluid glass with enhanced organic flow
4773
5105
  appleFluid: (uv, mousePosition) => {
@@ -4775,8 +5107,8 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4775
5107
  x: .5,
4776
5108
  y: .5
4777
5109
  };
4778
- const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .6, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = calculateLength(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(1.5 * mouseDistance, 1)), organicX = fbm(10 * (ix + .3 * mouseX) + time, 10 * (iy + .3 * mouseY), 5) - .5, organicY = fbm(10 * (ix - .3 * mouseX), 10 * (iy - .3 * mouseY) + .8 * time, 5) - .5, distanceToEdge = roundedRectSDF(ix, iy, .42, .32, .38), mask = smoothStep(.85, -.1, distanceToEdge), fluidVelocityX = Math.sin(6 * ix + 2 * time) * Math.cos(4 * iy - time) * .025, fluidVelocityY = Math.cos(4 * ix - time) * Math.sin(6 * iy + 2 * time) * .025, vortexAngle = Math.atan2(iy - mouseY, ix - mouseX), vortexStrength = mouseFalloff * mouseDistance * .08, vortexX = Math.cos(vortexAngle + time) * vortexStrength, totalY = iy + (.035 * organicY + fluidVelocityY + Math.sin(vortexAngle + time) * vortexStrength) * mask;
4779
- return createTexture(clampValue(ix + (.035 * organicX + fluidVelocityX + vortexX) * mask + .5, 0, 1), clampValue(totalY + .5, 0, 1));
5110
+ const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .6, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = vec2Length(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(1.5 * mouseDistance, 1)), organicX = fbm(10 * (ix + .3 * mouseX) + time, 10 * (iy + .3 * mouseY), 5) - .5, organicY = fbm(10 * (ix - .3 * mouseX), 10 * (iy - .3 * mouseY) + .8 * time, 5) - .5, distanceToEdge = roundedRectSDF(ix, iy, .42, .32, .38), mask = smoothstepEdge(.85, -.1, distanceToEdge), fluidVelocityX = Math.sin(6 * ix + 2 * time) * Math.cos(4 * iy - time) * .025, fluidVelocityY = Math.cos(4 * ix - time) * Math.sin(6 * iy + 2 * time) * .025, vortexAngle = Math.atan2(iy - mouseY, ix - mouseX), vortexStrength = mouseFalloff * mouseDistance * .08, vortexX = Math.cos(vortexAngle + time) * vortexStrength, totalY = iy + (.035 * organicY + fluidVelocityY + Math.sin(vortexAngle + time) * vortexStrength) * mask;
5111
+ return createTexture(clamp(ix + (.035 * organicX + fluidVelocityX + vortexX) * mask + .5, 0, 1), clamp(totalY + .5, 0, 1));
4780
5112
  },
4781
5113
  // High-end glass with advanced refraction and depth
4782
5114
  premiumGlass: (uv, mousePosition) => {
@@ -4784,7 +5116,7 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4784
5116
  x: .5,
4785
5117
  y: .5
4786
5118
  };
4787
- const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .4, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = calculateLength(mouseX, mouseY), centerDistance = calculateLength(ix, iy), refractionStrength = .3 * Math.pow(Math.min(centerDistance, 1), 1.5), refractionAngle = Math.atan2(iy, ix);
5119
+ const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .4, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = vec2Length(mouseX, mouseY), centerDistance = vec2Length(ix, iy), refractionStrength = .3 * Math.pow(Math.min(centerDistance, 1), 1.5), refractionAngle = Math.atan2(iy, ix);
4788
5120
  // Multi-layer depth effect
4789
5121
  let depthX = 0, depthY = 0;
4790
5122
  for (let layer = 0; layer < 3; layer++) {
@@ -4792,8 +5124,8 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4792
5124
  depthX += Math.sin(ix * layerScale + layerTime) * layerStrength, depthY += Math.cos(iy * layerScale - layerTime) * layerStrength;
4793
5125
  }
4794
5126
  // Glass refraction with mouse influence
4795
- const refractionX = Math.cos(refractionAngle) * refractionStrength * (1 + .5 * mouseDistance), refractionY = Math.sin(refractionAngle) * refractionStrength * (1 + .5 * mouseDistance), organicNoise = fbm(8 * ix + time, 8 * iy - time, 2) - .5, distanceToEdge = roundedRectSDF(ix, iy, .43, .33, .36), edgeMask = smoothStep(.9, -.05, distanceToEdge), finalY = iy + (refractionY + depthY + .015 * organicNoise) * edgeMask;
4796
- return createTexture(clampValue(ix + (refractionX + depthX + .015 * organicNoise) * edgeMask + .5, 0, 1), clampValue(finalY + .5, 0, 1));
5127
+ const refractionX = Math.cos(refractionAngle) * refractionStrength * (1 + .5 * mouseDistance), refractionY = Math.sin(refractionAngle) * refractionStrength * (1 + .5 * mouseDistance), organicNoise = fbm(8 * ix + time, 8 * iy - time, 2) - .5, distanceToEdge = roundedRectSDF(ix, iy, .43, .33, .36), edgeMask = smoothstepEdge(.9, -.05, distanceToEdge), finalY = iy + (refractionY + depthY + .015 * organicNoise) * edgeMask;
5128
+ return createTexture(clamp(ix + (refractionX + depthX + .015 * organicNoise) * edgeMask + .5, 0, 1), clamp(finalY + .5, 0, 1));
4797
5129
  },
4798
5130
  // Metallic liquid effect with shimmer
4799
5131
  liquidMetal: (uv, mousePosition) => {
@@ -4801,8 +5133,8 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4801
5133
  x: .5,
4802
5134
  y: .5
4803
5135
  };
4804
- const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * 1.2, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, wave1 = Math.sin(20 * ix + 4 * time) * Math.cos(15 * iy - 3 * time) * .02, wave2 = Math.cos(15 * ix - 2 * time) * Math.sin(20 * iy + 5 * time) * .015, shimmer = .025 * fbm(25 * ix + 2 * time, 25 * iy - 2 * time, 4), flowAngle = Math.atan2(iy - mouseY, ix - mouseX), flowDistance = calculateLength(ix - mouseX, iy - mouseY), flowEffect = .02 * Math.sin(15 * flowDistance - 6 * time) * easeOutQuart(1 - Math.min(2 * flowDistance, 1)), distanceToEdge = roundedRectSDF(ix, iy, .41, .31, .37), mask = smoothStep(.88, -.08, distanceToEdge), totalX = ix + (wave1 + shimmer + Math.cos(flowAngle) * flowEffect) * mask, totalY = iy + (wave2 + .8 * shimmer + Math.sin(flowAngle) * flowEffect) * mask;
4805
- return createTexture(clampValue(totalX + .5, 0, 1), clampValue(totalY + .5, 0, 1));
5136
+ const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * 1.2, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, wave1 = Math.sin(20 * ix + 4 * time) * Math.cos(15 * iy - 3 * time) * .02, wave2 = Math.cos(15 * ix - 2 * time) * Math.sin(20 * iy + 5 * time) * .015, shimmer = .025 * fbm(25 * ix + 2 * time, 25 * iy - 2 * time, 4), flowAngle = Math.atan2(iy - mouseY, ix - mouseX), flowDistance = vec2Length(ix - mouseX, iy - mouseY), flowEffect = .02 * Math.sin(15 * flowDistance - 6 * time) * easeOutQuart(1 - Math.min(2 * flowDistance, 1)), distanceToEdge = roundedRectSDF(ix, iy, .41, .31, .37), mask = smoothstepEdge(.88, -.08, distanceToEdge), totalX = ix + (wave1 + shimmer + Math.cos(flowAngle) * flowEffect) * mask, totalY = iy + (wave2 + .8 * shimmer + Math.sin(flowAngle) * flowEffect) * mask;
5137
+ return createTexture(clamp(totalX + .5, 0, 1), clamp(totalY + .5, 0, 1));
4806
5138
  },
4807
5139
  // basiBasi - Expert Premium Glass Shader
4808
5140
  // The most advanced shader with caustics, spectral dispersion, parallax depth, and volumetric effects
@@ -4811,7 +5143,7 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4811
5143
  x: .5,
4812
5144
  y: .5
4813
5145
  };
4814
- const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .5, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = calculateLength(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(1.2 * mouseDistance, 1)), causticIntensity = ((x, y, time, intensity = 1) =>
5146
+ const ix = uv.x - .5, iy = uv.y - .5, time = 8e-4 * Date.now() * .5, mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - .5 : 0, mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - .5 : 0, mouseDistance = vec2Length(mouseX, mouseY), mouseFalloff = easeOutQuart(1 - Math.min(1.2 * mouseDistance, 1)), causticIntensity = ((x, y, time, intensity = 1) =>
4815
5147
  // Add input validation
4816
5148
  "number" != typeof x || "number" != typeof y || "number" != typeof time || "number" != typeof intensity || isNaN(x) || isNaN(y) || isNaN(time) || isNaN(intensity) ? .5 : .5 * (Math.sin(8 * x + 2 * time) * Math.cos(8 * y - 2 * time) * .5 + Math.sin(8 * (x + .5) * 1.3 - 2 * time * .8) * Math.cos(8 * (y - .3) * 1.3 + 2 * time * .8) * .3 + Math.sin(8 * (x - .3) * .7 + 2 * time * 1.2) * Math.cos(8 * (y + .4) * .7 - 2 * time * 1.2) * .2 + 1) * intensity)(ix, iy, time, .8), causticDistortion = .02 * (causticIntensity - .5), refractionAngle = Math.atan2(iy, ix), spectralDispersion = ((x, y, angle) => {
4817
5149
  // Add input validation
@@ -4829,7 +5161,7 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4829
5161
  y: 0
4830
5162
  }
4831
5163
  };
4832
- const distance = calculateLength(x, y), dispersionStrength = Math.min(.025 * distance, 1), redOffset = .8 * dispersionStrength, greenOffset = 1 * dispersionStrength, blueOffset = 1.2 * dispersionStrength;
5164
+ const distance = vec2Length(x, y), dispersionStrength = Math.min(.025 * distance, 1), redOffset = .8 * dispersionStrength, greenOffset = 1 * dispersionStrength, blueOffset = 1.2 * dispersionStrength;
4833
5165
  return {
4834
5166
  r: {
4835
5167
  x: Math.cos(angle) * redOffset,
@@ -4869,8 +5201,8 @@ const Accordion = AccordionWithSubcomponents, AtomixLogo = ({height: height = 24
4869
5201
  return turbulence;
4870
5202
  })(6 * ix, 6 * iy, time, 6), turbulenceX = .012 * Math.cos(turbulence * Math.PI * 2), turbulenceY = .012 * Math.sin(turbulence * Math.PI * 2), microSurface = ((x, y, time) =>
4871
5203
  // Add input validation
4872
- "number" != typeof x || "number" != typeof y || "number" != typeof time || isNaN(x) || isNaN(y) || isNaN(time) ? .5 : .5 * (.7 * fbm(40 * x + .3 * time, 40 * y - .3 * time, 6) + .3 * fbm(80 * x, 80 * y, 4)))(ix, iy, time), microDetailX = .008 * (microSurface - .5), microDetailY = .008 * (microSurface - .5), centerDistance = calculateLength(ix, iy), dynamicRefraction = .35 * Math.pow(Math.min(centerDistance, 1), 1.8) * (1 + mouseFalloff * mouseDistance * .8), refractionX = Math.cos(refractionAngle) * dynamicRefraction, refractionY = Math.sin(refractionAngle) * dynamicRefraction, vortexAngle = Math.atan2(iy - mouseY, ix - mouseX), vortexDistance = calculateLength(ix - mouseX, iy - mouseY), vortexStrength = mouseFalloff * Math.sin(10 * vortexDistance - 3 * time) * .025, vortexX = Math.cos(vortexAngle + 2 * time) * vortexStrength, vortexY = Math.sin(vortexAngle + 2 * time) * vortexStrength, fluidX = Math.sin(10 * ix + 5 * mouseX + 2.5 * time) * Math.cos(8 * iy - 2 * time) * .018, fluidY = Math.cos(8 * ix - 2 * time) * Math.sin(10 * iy + 5 * mouseY + 2.5 * time) * .018, rippleEffect = (.012 * Math.sin(15 * Math.min(centerDistance, 10) - 4 * time) + .008 * Math.cos(20 * Math.min(centerDistance, 10) + 3 * time)) * mouseFalloff, rippleX = Math.cos(refractionAngle) * rippleEffect, rippleY = Math.sin(refractionAngle) * rippleEffect, distanceToEdge = roundedRectSDF(ix, iy, .44, .34, .39), edgeMask = smoothStep(.92, -.12, distanceToEdge), edgeSoftness = smoothStep(.85, .1, distanceToEdge), finalY = iy + (1.2 * refractionY + .8 * spectralY + 1.5 * parallaxY + .9 * scatteringY + 1 * turbulenceY + .6 * microDetailY + 1.3 * vortexY + 1.1 * fluidY + .7 * rippleY + .8 * causticDistortion) * edgeMask * edgeSoftness * .85;
4873
- return createTexture(clampValue(ix + (1.2 * refractionX + .8 * spectralX + 1.5 * parallaxX + .9 * scatteringX + 1 * turbulenceX + .6 * microDetailX + 1.3 * vortexX + 1.1 * fluidX + .7 * rippleX + causticDistortion) * edgeMask * edgeSoftness * .85 + .5, 0, 1), clampValue(finalY + .5, 0, 1));
5204
+ "number" != typeof x || "number" != typeof y || "number" != typeof time || isNaN(x) || isNaN(y) || isNaN(time) ? .5 : .5 * (.7 * fbm(40 * x + .3 * time, 40 * y - .3 * time, 6) + .3 * fbm(80 * x, 80 * y, 4)))(ix, iy, time), microDetailX = .008 * (microSurface - .5), microDetailY = .008 * (microSurface - .5), centerDistance = vec2Length(ix, iy), dynamicRefraction = .35 * Math.pow(Math.min(centerDistance, 1), 1.8) * (1 + mouseFalloff * mouseDistance * .8), refractionX = Math.cos(refractionAngle) * dynamicRefraction, refractionY = Math.sin(refractionAngle) * dynamicRefraction, vortexAngle = Math.atan2(iy - mouseY, ix - mouseX), vortexDistance = vec2Length(ix - mouseX, iy - mouseY), vortexStrength = mouseFalloff * Math.sin(10 * vortexDistance - 3 * time) * .025, vortexX = Math.cos(vortexAngle + 2 * time) * vortexStrength, vortexY = Math.sin(vortexAngle + 2 * time) * vortexStrength, fluidX = Math.sin(10 * ix + 5 * mouseX + 2.5 * time) * Math.cos(8 * iy - 2 * time) * .018, fluidY = Math.cos(8 * ix - 2 * time) * Math.sin(10 * iy + 5 * mouseY + 2.5 * time) * .018, rippleEffect = (.012 * Math.sin(15 * Math.min(centerDistance, 10) - 4 * time) + .008 * Math.cos(20 * Math.min(centerDistance, 10) + 3 * time)) * mouseFalloff, rippleX = Math.cos(refractionAngle) * rippleEffect, rippleY = Math.sin(refractionAngle) * rippleEffect, distanceToEdge = roundedRectSDF(ix, iy, .44, .34, .39), edgeMask = smoothstepEdge(.92, -.12, distanceToEdge), edgeSoftness = smoothstepEdge(.85, .1, distanceToEdge), finalY = iy + (1.2 * refractionY + .8 * spectralY + 1.5 * parallaxY + .9 * scatteringY + 1 * turbulenceY + .6 * microDetailY + 1.3 * vortexY + 1.1 * fluidY + .7 * rippleY + .8 * causticDistortion) * edgeMask * edgeSoftness * .85;
5205
+ return createTexture(clamp(ix + (1.2 * refractionX + .8 * spectralX + 1.5 * parallaxX + .9 * scatteringX + 1 * turbulenceX + .6 * microDetailX + 1.3 * vortexX + 1.1 * fluidX + .7 * rippleX + causticDistortion) * edgeMask * edgeSoftness * .85 + .5, 0, 1), clamp(finalY + .5, 0, 1));
4874
5206
  },
4875
5207
  // Aliases for compatibility
4876
5208
  plasma: (uv, mousePosition) => fragmentShaders.premiumGlass(uv, mousePosition),
@@ -4914,8 +5246,8 @@ var shaderUtils = Object.freeze({
4914
5246
  let dx = pos.x * w - x, dy = pos.y * h - y;
4915
5247
  // Apply edge smoothing for Apple-like effect
4916
5248
  const edgeX = 2 * Math.min(x / w, (w - x) / w), edgeY = 2 * Math.min(y / h, (h - y) / h), edgeFactor = Math.min(edgeX, edgeY);
4917
- dx *= smoothStep(0, .2, edgeFactor), dy *= smoothStep(0, .2, edgeFactor), maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy)),
4918
- rawValues.push(dx, dy);
5249
+ dx *= smoothstepEdge(0, .2, edgeFactor), dy *= smoothstepEdge(0, .2, edgeFactor),
5250
+ maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy)), rawValues.push(dx, dy);
4919
5251
  }
4920
5252
  // Improved normalization to prevent artifacts while maintaining intensity
4921
5253
  maxScale = Math.max(maxScale, 1);
@@ -4925,9 +5257,9 @@ var shaderUtils = Object.freeze({
4925
5257
  let rawIndex = 0;
4926
5258
  for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
4927
5259
  const dx = rawValues[rawIndex++] || 0, dy = rawValues[rawIndex++] || 0, edgeDistance = Math.min(x, y, w - x - 1, h - y - 1), edgeFactor = Math.min(1, edgeDistance / 2), r = dx * edgeFactor / maxScale + .5, g = dy * edgeFactor / maxScale + .5, pixelIndex = 4 * (y * w + x);
4928
- data[pixelIndex] = clampValue(255 * r, 0, 255), // Red channel (X displacement)
4929
- data[pixelIndex + 1] = clampValue(255 * g, 0, 255), // Green channel (Y displacement)
4930
- data[pixelIndex + 2] = clampValue(255 * g, 0, 255), // Blue channel (Y displacement for SVG filter compatibility)
5260
+ data[pixelIndex] = clamp(255 * r, 0, 255), // Red channel (X displacement)
5261
+ data[pixelIndex + 1] = clamp(255 * g, 0, 255), // Green channel (Y displacement)
5262
+ data[pixelIndex + 2] = clamp(255 * g, 0, 255), // Blue channel (Y displacement for SVG filter compatibility)
4931
5263
  data[pixelIndex + 3] = 255;
4932
5264
  }
4933
5265
  return this.context.putImageData(imageData, 0, 0), this.canvas.toDataURL();
@@ -4955,9 +5287,7 @@ var shaderUtils = Object.freeze({
4955
5287
  return this.canvasDPI;
4956
5288
  }
4957
5289
  },
4958
- createFBMEngine: createFBMEngine,
4959
- fragmentShaders: fragmentShaders,
4960
- liquidGlassWithTime: liquidGlassWithTime
5290
+ fragmentShaders: fragmentShaders
4961
5291
  });
4962
5292
 
4963
5293
  // Map string sizes to pixel values
@@ -5106,10 +5436,10 @@ const Badge = React.memo((({label: label, variant: variant = "primary", size: s
5106
5436
  if (glass) {
5107
5437
  // Default glass settings for badges
5108
5438
  const defaultGlassProps = {
5109
- displacementScale: 20,
5110
- borderRadius: ref.current?.getBoundingClientRect().width ? ref.current?.getBoundingClientRect().width / 2 : 16,
5111
- className: "c-badge--glass",
5112
- elasticity: 0
5439
+ ...GLASS_DEFAULTS.BADGE,
5440
+ // Override borderRadius dynamically if the ref is available
5441
+ borderRadius: ref.current?.getBoundingClientRect().width ? ref.current?.getBoundingClientRect().width / 2 : GLASS_DEFAULTS.BADGE.borderRadius,
5442
+ className: "c-badge--glass"
5113
5443
  }, glassProps = !0 === glass ? defaultGlassProps : {
5114
5444
  ...defaultGlassProps,
5115
5445
  ...glass
@@ -5360,12 +5690,7 @@ const Spinner = React.memo( React.forwardRef((({size: size = "md", variant: var
5360
5690
  })
5361
5691
  });
5362
5692
  if (glass) {
5363
- const defaultGlassProps = {
5364
- displacementScale: 20,
5365
- blurAmount: 1,
5366
- borderRadius: 999,
5367
- mode: "shader"
5368
- }, glassProps = !0 === glass ? defaultGlassProps : {
5693
+ const defaultGlassProps = GLASS_DEFAULTS.SPINNER, glassProps = !0 === glass ? defaultGlassProps : {
5369
5694
  ...defaultGlassProps,
5370
5695
  ...glass
5371
5696
  };
@@ -5467,60 +5792,6 @@ class ThemeNaming {
5467
5792
  }
5468
5793
  }
5469
5794
 
5470
- ThemeNaming.prefix = "atomix";
5471
-
5472
- var aCallable = aCallable$3, toObject = toObject$2, IndexedObject = indexedObject, lengthOfArrayLike = lengthOfArrayLike$2, $TypeError = TypeError, REDUCE_EMPTY = "Reduce of empty array with no initial value", createMethod = function(IS_RIGHT) {
5473
- return function(that, callbackfn, argumentsLength, memo) {
5474
- var O = toObject(that), self = IndexedObject(O), length = lengthOfArrayLike(O);
5475
- if (aCallable(callbackfn), 0 === length && argumentsLength < 2) throw new $TypeError(REDUCE_EMPTY);
5476
- var index = IS_RIGHT ? length - 1 : 0, i = IS_RIGHT ? -1 : 1;
5477
- if (argumentsLength < 2) for (;;) {
5478
- if (index in self) {
5479
- memo = self[index], index += i;
5480
- break;
5481
- }
5482
- if (index += i, IS_RIGHT ? index < 0 : length <= index) throw new $TypeError(REDUCE_EMPTY);
5483
- }
5484
- for (;IS_RIGHT ? index >= 0 : length > index; index += i) index in self && (memo = callbackfn(memo, self[index], index, O));
5485
- return memo;
5486
- };
5487
- }, arrayReduce = {
5488
- // `Array.prototype.reduce` method
5489
- // https://tc39.es/ecma262/#sec-array.prototype.reduce
5490
- left: createMethod(!1),
5491
- // `Array.prototype.reduceRight` method
5492
- // https://tc39.es/ecma262/#sec-array.prototype.reduceright
5493
- right: createMethod(!0)
5494
- }, fails = fails$9, globalThis$1 = globalThis_1, userAgent = environmentUserAgent, classof = classofRaw$2, userAgentStartsWith = function(string) {
5495
- return userAgent.slice(0, string.length) === string;
5496
- }, environment = userAgentStartsWith("Bun/") ? "BUN" : userAgentStartsWith("Cloudflare-Workers") ? "CLOUDFLARE" : userAgentStartsWith("Deno/") ? "DENO" : userAgentStartsWith("Node.js/") ? "NODE" : globalThis$1.Bun && "string" == typeof Bun.version ? "BUN" : globalThis$1.Deno && "object" == typeof Deno.version ? "DENO" : "process" === classof(globalThis$1.process) ? "NODE" : globalThis$1.window && globalThis$1.document ? "BROWSER" : "REST", $reduce = arrayReduce.left;
5497
-
5498
- // `Array.prototype.reduce` method
5499
- // https://tc39.es/ecma262/#sec-array.prototype.reduce
5500
- _export({
5501
- target: "Array",
5502
- proto: !0,
5503
- forced: !("NODE" === environment) && environmentV8Version > 79 && environmentV8Version < 83 || !function(METHOD_NAME, argument) {
5504
- var method = [][METHOD_NAME];
5505
- return !!method && fails((function() {
5506
- // eslint-disable-next-line no-useless-call -- required for testing
5507
- method.call(null, argument || function() {
5508
- return 1;
5509
- }, 1);
5510
- }));
5511
- }("reduce")
5512
- }, {
5513
- reduce: function(callbackfn /* , initialValue */) {
5514
- var length = arguments.length;
5515
- return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : void 0);
5516
- }
5517
- });
5518
-
5519
- var reduce$3 = getBuiltInPrototypeMethod$3("Array", "reduce"), isPrototypeOf = objectIsPrototypeOf, method = reduce$3, ArrayPrototype = Array.prototype, _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
5520
- var own = it.reduce;
5521
- return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.reduce ? method : own;
5522
- }));
5523
-
5524
5795
  /**
5525
5796
  * Render a slot with the given props
5526
5797
  *
@@ -5562,7 +5833,9 @@ function renderSlot(slot, props, fallback) {
5562
5833
 
5563
5834
  /**
5564
5835
  * Check if a value is a slot configuration
5565
- */ const Button = React__default.default.memo( React.forwardRef((({label: label, children: children, onClick: onClick, variant: variant = "primary", size: size = "md", disabled: disabled = !1, loading: loading = !1, loadingText: loadingText, icon: icon, iconName: iconName, iconSize: iconSize = "sm", iconPosition: iconPosition = "start", iconOnly: iconOnly = !1, rounded: rounded = !1, fullWidth: fullWidth = !1, block: block = !1, active: active = !1, selected: selected = !1, type: type = "button", className: className = "", as: Component = "button", href: href, target: target, glass: glass, onHover: onHover, onFocus: onFocus, onBlur: onBlur, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, tabIndex: tabIndex, style: style, linkComponent: linkComponent, slots: slots, ...props}, ref) => {
5836
+ */ ThemeNaming.prefix = "atomix";
5837
+
5838
+ const Button = React__default.default.memo( React.forwardRef((({label: label, children: children, onClick: onClick, variant: variant = "primary", size: size = "md", disabled: disabled = !1, loading: loading = !1, loadingText: loadingText, icon: icon, iconName: iconName, iconSize: iconSize = "sm", iconPosition: iconPosition = "start", iconOnly: iconOnly = !1, rounded: rounded = !1, fullWidth: fullWidth = !1, block: block = !1, active: active = !1, selected: selected = !1, type: type = "button", className: className = "", as: Component = "button", href: href, target: target, glass: glass, onHover: onHover, onFocus: onFocus, onBlur: onBlur, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, tabIndex: tabIndex, style: style, linkComponent: linkComponent, slots: slots, ...props}, ref) => {
5566
5839
  const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href), iconElement = iconName ? jsxRuntime.jsx(Icon, {
5567
5840
  name: iconName,
5568
5841
  size: iconSize
@@ -5670,11 +5943,7 @@ function renderSlot(slot, props, fallback) {
5670
5943
  // This is a safe fallback for disabled links.
5671
5944
  if (glass) {
5672
5945
  // Default glass props
5673
- const defaultGlassProps = {
5674
- displacementScale: 20,
5675
- blurAmount: 0,
5676
- saturation: 200
5677
- }, glassProps = !0 === glass ? defaultGlassProps : {
5946
+ const defaultGlassProps = GLASS_DEFAULTS.BUTTON, glassProps = !0 === glass ? defaultGlassProps : {
5678
5947
  ...defaultGlassProps,
5679
5948
  ...glass
5680
5949
  };
@@ -5873,11 +6142,7 @@ const CalloutComponentBase = ({title: title, children: children, icon: icon, var
5873
6142
  // Determine appropriate ARIA attributes based on variant
5874
6143
  if (glass) {
5875
6144
  // Default glass settings for callouts
5876
- const defaultGlassProps = {
5877
- displacementScale: 30,
5878
- borderRadius: 8,
5879
- elasticity: 0
5880
- }, glassProps = !0 === glass ? defaultGlassProps : {
6145
+ const defaultGlassProps = GLASS_DEFAULTS.CALLOUT, glassProps = !0 === glass ? defaultGlassProps : {
5881
6146
  ...defaultGlassProps,
5882
6147
  ...glass
5883
6148
  };
@@ -10695,15 +10960,7 @@ const range = (start, end) => {
10695
10960
  }) ]
10696
10961
  });
10697
10962
  if (glass) {
10698
- // Default glass settings for pagination
10699
- const defaultGlassProps = {
10700
- displacementScale: 60,
10701
- blurAmount: 1,
10702
- saturation: 160,
10703
- aberrationIntensity: .5,
10704
- borderRadius: 8,
10705
- mode: "shader"
10706
- }, glassProps = !0 === glass ? defaultGlassProps : {
10963
+ const defaultGlassProps = GLASS_DEFAULTS.PAGINATION, glassProps = !0 === glass ? defaultGlassProps : {
10707
10964
  ...defaultGlassProps,
10708
10965
  ...glass
10709
10966
  };
@@ -10767,14 +11024,7 @@ const Checkbox = React__default.default.memo( React.forwardRef((({label: label,
10767
11024
  style: style,
10768
11025
  children: inputElement
10769
11026
  }), glass) {
10770
- const defaultGlassProps = {
10771
- displacementScale: 40,
10772
- blurAmount: 1,
10773
- saturation: 160,
10774
- aberrationIntensity: .3,
10775
- borderRadius: 6,
10776
- mode: "shader"
10777
- }, glassProps = !0 === glass ? defaultGlassProps : {
11027
+ const defaultGlassProps = GLASS_DEFAULTS.CHECKBOX, glassProps = !0 === glass ? defaultGlassProps : {
10778
11028
  ...defaultGlassProps,
10779
11029
  ...glass
10780
11030
  };
@@ -11044,13 +11294,8 @@ const DropdownStyleContext = React.createContext({}), Dropdown = React.memo((f
11044
11294
  "aria-orientation": "vertical",
11045
11295
  "aria-hidden": !isOpen,
11046
11296
  onKeyDown: handleKeyDown,
11047
- children: glass ?
11048
- // Default glass settings for dropdowns
11049
- (() => {
11050
- const defaultGlassProps = {
11051
- displacementScale: 20,
11052
- elasticity: 0
11053
- }, glassProps = !0 === glass ? defaultGlassProps : {
11297
+ children: glass ? (() => {
11298
+ const defaultGlassProps = GLASS_DEFAULTS.DROPDOWN, glassProps = !0 === glass ? defaultGlassProps : {
11054
11299
  ...defaultGlassProps,
11055
11300
  ...glass
11056
11301
  };
@@ -12614,16 +12859,8 @@ const Input = React.memo( React.forwardRef((({type: type = "text", value: value
12614
12859
  ...style
12615
12860
  } : style
12616
12861
  });
12617
- if (glass) {
12618
- // Default glass settings for inputs
12619
- const defaultGlassProps = {
12620
- displacementScale: 60,
12621
- blurAmount: 1,
12622
- saturation: 180,
12623
- aberrationIntensity: .2,
12624
- borderRadius: 12,
12625
- mode: "shader"
12626
- }, glassProps = !0 === glass ? defaultGlassProps : {
12862
+ if (glass) {
12863
+ const defaultGlassProps = GLASS_DEFAULTS.INPUT, glassProps = !0 === glass ? defaultGlassProps : {
12627
12864
  ...defaultGlassProps,
12628
12865
  ...glass
12629
12866
  };
@@ -12807,498 +13044,292 @@ function useHero(initialProps) {
12807
13044
  customClass && classes.push(customClass), classes.join(" ");
12808
13045
  },
12809
13046
  generateContentColClass: (size = defaultProps.contentColSize || 5, customClass) => {
12810
- const classes = [ `o-grid__col o-grid__col--md-${size}` ];
12811
- // Handle mobile stacking order
12812
- return defaultProps.reverseOnMobile && ("right" === defaultProps.alignment || "center" === defaultProps.alignment ? classes.push("u-order-last u-order-md-first") : classes.push("u-order-first u-order-md-last")),
12813
- customClass && classes.push(customClass), classes.join(" ");
12814
- },
12815
- hasBackgroundImage: hasBackgroundImage,
12816
- hasForegroundImage: hasForegroundImage,
12817
- useGridLayout: useGridLayout,
12818
- heroRef: heroRef,
12819
- videoRef: videoRef,
12820
- applyParallaxEffect: applyParallaxEffect,
12821
- removeParallaxEffect: removeParallaxEffect,
12822
- backgroundSlider: backgroundSlider,
12823
- hasBackgroundSlider: hasBackgroundSlider
12824
- };
12825
- }
12826
-
12827
- /**
12828
- * Hook for River component functionality
12829
- * @param initialProps - Initial river props
12830
- * @returns River methods and state
12831
- */ function useRiver(initialProps) {
12832
- const defaultProps = {
12833
- center: !1,
12834
- breakout: !1,
12835
- reverse: !1,
12836
- imageAlt: "Image",
12837
- showOverlay: !0,
12838
- ...initialProps
12839
- };
12840
- /**
12841
- * Check if the river has a background image
12842
- */ return {
12843
- generateRiverClassNames: (baseClassName = "") => {
12844
- const classes = [ RIVER.SELECTORS.RIVER.replace(".", "") ];
12845
- // Add layout classes
12846
- return defaultProps.center && classes.push(RIVER.CLASSES.CENTER), defaultProps.breakout && classes.push(RIVER.CLASSES.BREAKOUT),
12847
- defaultProps.reverse && classes.push(RIVER.CLASSES.REVERSE), baseClassName && classes.push(baseClassName),
12848
- classes.join(" ");
12849
- },
12850
- generateContentClass: () => RIVER.SELECTORS.CONTENT.replace(".", ""),
12851
- generateVisualClass: () => RIVER.SELECTORS.VISUAL.replace(".", ""),
12852
- hasBackgroundImage: !!defaultProps.backgroundImageSrc,
12853
- hasForegroundImage: !!defaultProps.imageSrc,
12854
- textContent: "string" == typeof defaultProps.text ? [ defaultProps.text ] : defaultProps.text || []
12855
- };
12856
- }
12857
-
12858
- /**
12859
- * Navbar state and functionality
12860
- * @param initialProps - Initial navbar properties
12861
- * @returns Navbar state and methods
12862
- */ function useNavbar(initialProps) {
12863
- // Default navbar properties
12864
- const defaultProps = {
12865
- position: "static",
12866
- collapsible: !0,
12867
- backdrop: !1,
12868
- closeOnOutsideClick: !0,
12869
- closeOnEscape: !0,
12870
- "aria-label": "Main navigation",
12871
- ...initialProps
12872
- }, [isExpanded, setIsExpanded] = React.useState(defaultProps.expanded || !1);
12873
- // Local expanded state for when not controlled externally
12874
- return {
12875
- defaultProps: defaultProps,
12876
- isExpanded: isExpanded,
12877
- setIsExpanded: setIsExpanded,
12878
- generateNavbarClass: props => {
12879
- const {position: position = defaultProps.position, variant: variant, collapsible: collapsible = defaultProps.collapsible, className: className = ""} = props;
12880
- return `c-navbar ${"static" !== position ? `c-navbar--${position}` : ""} ${variant ? `c-navbar--${variant}` : ""} ${collapsible ? "c-navbar--collapsible" : ""} ${className}`.trim();
12881
- },
12882
- generateContainerStyle: width => width ? {
12883
- maxWidth: width
12884
- } : {},
12885
- generateCollapseClass: expanded => ("c-navbar__collapse " + (expanded ? "is-expanded" : "")).trim(),
12886
- toggleExpanded: () => {
12887
- const newState = !isExpanded;
12888
- setIsExpanded(newState), defaultProps.onToggle && defaultProps.onToggle(newState);
12889
- },
12890
- getExpandedState: controlled => void 0 !== controlled ? controlled : isExpanded
12891
- };
12892
- }
12893
-
12894
- /**
12895
- * Nav state and functionality
12896
- * @param initialProps - Initial nav properties
12897
- * @returns Nav state and methods
12898
- */ function useNav(initialProps) {
12899
- // Default nav properties
12900
- const defaultProps = {
12901
- alignment: "start",
12902
- variant: "default",
12903
- ...initialProps
12904
- };
12905
- /**
12906
- * Generate nav class based on properties
12907
- * @param props - Nav properties
12908
- * @returns Class string
12909
- */ return {
12910
- defaultProps: defaultProps,
12911
- generateNavClass: props => {
12912
- const {alignment: alignment = defaultProps.alignment, variant: variant = defaultProps.variant, className: className = ""} = props;
12913
- return `c-nav ${"start" !== alignment ? `c-nav--${alignment}` : ""} ${"default" !== variant ? `c-nav--${variant}` : ""} ${className}`.trim();
12914
- }
12915
- };
12916
- }
12917
-
12918
- /**
12919
- * Nav item state and functionality
12920
- * @param initialProps - Initial nav item properties
12921
- * @returns Nav item state and methods
12922
- */ function useNavItem(initialProps) {
12923
- // Default nav item properties
12924
- const defaultProps = {
12925
- dropdown: !1,
12926
- megaMenu: !1,
12927
- active: !1,
12928
- ...initialProps
12929
- };
12930
- /**
12931
- * Generate nav item class based on properties
12932
- * @param props - Nav item properties
12933
- * @returns Class string
12934
- */ return {
12935
- defaultProps: defaultProps,
12936
- generateNavItemClass: props => {
12937
- const {dropdown: dropdown = defaultProps.dropdown, megaMenu: megaMenu = defaultProps.megaMenu, active: active = defaultProps.active, disabled: disabled = defaultProps.disabled, className: className = ""} = props;
12938
- // Apply dropdown class only for regular dropdowns, not mega menus
12939
- return `c-nav__item ${dropdown && !megaMenu ? NAV.SELECTORS.DROPDOWN.replace(".", "") : ""} ${megaMenu ? "c-nav__item--mega-menu" : ""} ${active ? NAV.CLASSES.ACTIVE : ""} ${disabled ? NAV.CLASSES.DISABLED : ""} ${className}`.trim();
12940
- },
12941
- generateNavLinkClass: (active = !1, disabled = !1, className = "") => `c-nav__link ${active ? NAV.CLASSES.ACTIVE : ""} ${disabled ? "c-nav__link--disabled" : ""} ${className}`.trim(),
12942
- handleClick: handler => e => {
12943
- !defaultProps.disabled && handler ? handler() : e.preventDefault();
12944
- }
12945
- };
12946
- }
12947
-
12948
- /**
12949
- * Nav dropdown state and functionality
12950
- * @param initialProps - Initial dropdown properties
12951
- * @returns Dropdown state and methods
12952
- */ function useNavDropdown(initialProps) {
12953
- // Default dropdown properties
12954
- const defaultProps = {
12955
- alignment: "start",
12956
- megaMenu: !1,
12957
- ...initialProps
12958
- }, isInFixedBottomNavbar = () => null !== document.querySelector(".c-navbar--fixed-bottom");
12959
- /**
12960
- * Generate dropdown menu class based on properties
12961
- * @param props - Dropdown properties
12962
- * @returns Class string
12963
- */ return {
12964
- defaultProps: defaultProps,
12965
- generateDropdownMenuClass: props => {
12966
- const {alignment: alignment = defaultProps.alignment, megaMenu: megaMenu = defaultProps.megaMenu, className: className = ""} = props, baseClass = megaMenu ? NAV.SELECTORS.MEGA_MENU.replace(".", "") : NAV.SELECTORS.DROPDOWN_MENU.replace(".", "");
12967
- // Select the base class based on mega menu or regular dropdown
12968
- // Add alignment class if not default 'start'
12969
- let alignmentClass = "";
12970
- return "center" === alignment ? alignmentClass = `${baseClass}--center` : "end" === alignment && (alignmentClass = `${baseClass}--end`),
12971
- `${baseClass} ${alignmentClass} ${className}`.trim();
12972
- },
12973
- isInFixedBottomNavbar: isInFixedBottomNavbar,
12974
- getIconName: (isMegaMenu = !1) => isInFixedBottomNavbar() ? "CaretUp" : "CaretDown"
12975
- };
12976
- }
12977
-
12978
- /**
12979
- * SideMenu state and functionality
12980
- * @param initialProps - Initial side menu properties
12981
- * @returns SideMenu state and methods
12982
- */ function useSideMenu(initialProps) {
12983
- const {collapsible: collapsible = !0, collapsibleDesktop: collapsibleDesktop = !1, defaultCollapsedDesktop: defaultCollapsedDesktop = !1, isOpen: isOpen, onToggle: onToggle, disabled: disabled = !1} = initialProps || {}, [isOpenState, setIsOpenState] = React.useState(void 0 !== defaultCollapsedDesktop ? !defaultCollapsedDesktop : isOpen || !1), wrapperRef = React.useRef(null), innerRef = React.useRef(null), sideMenuRef = React.useRef(null);
12984
- // Local open state for when not controlled externally
12985
- // Update local state when external state changes
12986
- React.useEffect((() => {
12987
- void 0 !== isOpen ? setIsOpenState(isOpen) : void 0 !== defaultCollapsedDesktop && setIsOpenState(!defaultCollapsedDesktop);
12988
- }), [ isOpen, defaultCollapsedDesktop ]),
12989
- // Set initial height on mount
12990
- React.useEffect((() => {
12991
- const shouldCollapse = window.innerWidth < 768 ? collapsible : collapsibleDesktop, currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
12992
- if (shouldCollapse && wrapperRef.current && innerRef.current) {
12993
- // Use setTimeout to ensure DOM is fully rendered
12994
- const timeoutId = setTimeout((() => {
12995
- wrapperRef.current && innerRef.current && (wrapperRef.current.style.height = currentOpen ? `${innerRef.current.scrollHeight}px` : "0px");
12996
- }), 0);
12997
- return () => clearTimeout(timeoutId);
12998
- }
12999
- !shouldCollapse && wrapperRef.current && (wrapperRef.current.style.height = "auto");
13000
- }), [ collapsible, collapsibleDesktop, isOpen, isOpenState ]),
13001
- // Handle responsive behavior - vertical collapse for both mobile and desktop
13002
- React.useEffect((() => {
13003
- const handleResize = () => {
13004
- if (window.innerWidth < 768 ? collapsible : collapsibleDesktop) {
13005
- if (wrapperRef.current && innerRef.current) {
13006
- // Set proper height for vertical animation (both mobile and desktop)
13007
- const currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
13008
- // Use requestAnimationFrame to ensure DOM is ready
13009
- requestAnimationFrame((() => {
13010
- wrapperRef.current && innerRef.current && (wrapperRef.current.style.height = currentOpen ? `${innerRef.current.scrollHeight}px` : "0px");
13011
- }));
13012
- }
13013
- } else
13014
- // Not collapsible - always show content
13015
- wrapperRef.current && (wrapperRef.current.style.height = "auto");
13016
- }, timeoutId = setTimeout(handleResize, 0);
13017
- // Initial call with a small delay to ensure DOM is ready
13018
- return window.addEventListener("resize", handleResize), () => {
13019
- clearTimeout(timeoutId), window.removeEventListener("resize", handleResize);
13020
- };
13021
- }), [ collapsible, collapsibleDesktop, isOpen, onToggle, isOpenState ]),
13022
- // Update wrapper height when open state changes (both mobile and desktop)
13023
- React.useEffect((() => {
13024
- const shouldCollapse = window.innerWidth < 768 ? collapsible : collapsibleDesktop;
13025
- if (shouldCollapse && wrapperRef.current && innerRef.current) {
13026
- const currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
13027
- // Use requestAnimationFrame to ensure DOM is ready
13028
- requestAnimationFrame((() => {
13029
- wrapperRef.current && innerRef.current && (wrapperRef.current.style.height = currentOpen ? `${innerRef.current.scrollHeight}px` : "0px");
13030
- }));
13031
- } else !shouldCollapse && wrapperRef.current && (
13032
- // Not collapsible - always show content
13033
- wrapperRef.current.style.height = "auto");
13034
- }), [ isOpen, isOpenState, collapsible, collapsibleDesktop ]);
13035
- /**
13036
- * Generate side menu class based on properties
13037
- * @param props - Side menu properties
13038
- * @returns Class string
13039
- */
13040
- const handleToggle = () => {
13041
- if (disabled) return;
13042
- const newState = void 0 !== isOpen ? !isOpen : !isOpenState;
13043
- "function" == typeof onToggle ?
13044
- // Controlled component
13045
- onToggle(newState) :
13046
- // Uncontrolled component
13047
- setIsOpenState(newState);
13048
- }, getCurrentOpenState = () => void 0 !== isOpen ? isOpen : isOpenState;
13049
- /**
13050
- * Generate wrapper class
13051
- * @returns Class string
13052
- */ return {
13053
- isOpenState: getCurrentOpenState(),
13054
- wrapperRef: wrapperRef,
13055
- innerRef: innerRef,
13056
- sideMenuRef: sideMenuRef,
13057
- generateSideMenuClass: props => {
13058
- const {className: className = "", isOpen: isOpen = !1} = props, openClass = isOpen ? SIDE_MENU.CLASSES.IS_OPEN : "";
13059
- return `${SIDE_MENU.CLASSES.BASE} ${openClass} ${className}`.trim();
13060
- },
13061
- generateWrapperClass: () => SIDE_MENU.CLASSES.WRAPPER,
13062
- handleToggle: handleToggle,
13063
- handleDesktopCollapse: () => {
13064
- handleToggle();
13047
+ const classes = [ `o-grid__col o-grid__col--md-${size}` ];
13048
+ // Handle mobile stacking order
13049
+ return defaultProps.reverseOnMobile && ("right" === defaultProps.alignment || "center" === defaultProps.alignment ? classes.push("u-order-last u-order-md-first") : classes.push("u-order-first u-order-md-last")),
13050
+ customClass && classes.push(customClass), classes.join(" ");
13065
13051
  },
13066
- getCurrentOpenState: getCurrentOpenState
13052
+ hasBackgroundImage: hasBackgroundImage,
13053
+ hasForegroundImage: hasForegroundImage,
13054
+ useGridLayout: useGridLayout,
13055
+ heroRef: heroRef,
13056
+ videoRef: videoRef,
13057
+ applyParallaxEffect: applyParallaxEffect,
13058
+ removeParallaxEffect: removeParallaxEffect,
13059
+ backgroundSlider: backgroundSlider,
13060
+ hasBackgroundSlider: hasBackgroundSlider
13067
13061
  };
13068
13062
  }
13069
13063
 
13070
13064
  /**
13071
- * SideMenuItem state and functionality
13072
- * @param initialProps - Initial side menu item properties
13073
- * @returns SideMenuItem state and methods
13074
- */ function useSideMenuItem(initialProps) {
13075
- // Default side menu item properties
13065
+ * Hook for River component functionality
13066
+ * @param initialProps - Initial river props
13067
+ * @returns River methods and state
13068
+ */ function useRiver(initialProps) {
13076
13069
  const defaultProps = {
13077
- active: !1,
13078
- disabled: !1,
13070
+ center: !1,
13071
+ breakout: !1,
13072
+ reverse: !1,
13073
+ imageAlt: "Image",
13074
+ showOverlay: !0,
13079
13075
  ...initialProps
13080
13076
  };
13081
13077
  /**
13082
- * Generate side menu item class based on properties
13083
- * @returns Class string
13078
+ * Check if the river has a background image
13084
13079
  */ return {
13085
- defaultProps: defaultProps,
13086
- generateSideMenuItemClass: () => {
13087
- const {active: active = defaultProps.active, disabled: disabled = defaultProps.disabled, className: className = ""} = defaultProps, activeClass = active ? SIDE_MENU.CLASSES.ACTIVE : "", disabledClass = disabled ? SIDE_MENU.CLASSES.DISABLED : "";
13088
- return `${SIDE_MENU.CLASSES.LINK} ${activeClass} ${disabledClass} ${className}`.trim();
13080
+ generateRiverClassNames: (baseClassName = "") => {
13081
+ const classes = [ RIVER.SELECTORS.RIVER.replace(".", "") ];
13082
+ // Add layout classes
13083
+ return defaultProps.center && classes.push(RIVER.CLASSES.CENTER), defaultProps.breakout && classes.push(RIVER.CLASSES.BREAKOUT),
13084
+ defaultProps.reverse && classes.push(RIVER.CLASSES.REVERSE), baseClassName && classes.push(baseClassName),
13085
+ classes.join(" ");
13089
13086
  },
13090
- handleClick: handler => e => {
13091
- defaultProps.disabled ? e.preventDefault() : handler && handler(e);
13092
- }
13087
+ generateContentClass: () => RIVER.SELECTORS.CONTENT.replace(".", ""),
13088
+ generateVisualClass: () => RIVER.SELECTORS.VISUAL.replace(".", ""),
13089
+ hasBackgroundImage: !!defaultProps.backgroundImageSrc,
13090
+ hasForegroundImage: !!defaultProps.imageSrc,
13091
+ textContent: "string" == typeof defaultProps.text ? [ defaultProps.text ] : defaultProps.text || []
13093
13092
  };
13094
13093
  }
13095
13094
 
13096
13095
  /**
13097
- * Map SCSS tokens to CSS custom properties
13098
- *
13099
- * @example
13100
- * const tokens = { '$primary-color': '#7AFFD7', '$spacing-md': '16px' }
13101
- * const vars = mapSCSSTokensToCSSVars(tokens)
13102
- * // Returns: { '--primary-color': '#7AFFD7', '--spacing-md': '16px' }
13103
- */ function mapSCSSTokensToCSSVars(tokens, options = {}) {
13104
- const vars = {}, {prefix: prefix = "atomix", separator: separator = "-"} = options;
13105
- return Object.entries(tokens).forEach((([key, value]) => {
13106
- // Remove $ prefix from SCSS variables
13107
- const normalizedKey = (key.startsWith("$") ? key.slice(1) : key).replace(/_/g, separator);
13108
- // Convert underscores to separators
13109
- vars[`--${prefix}${separator}${normalizedKey}`] = String(value);
13110
- })), vars;
13111
- }
13112
-
13113
- /**
13114
- * Apply CSS variables to an element
13115
- *
13116
- * @param vars - CSS variables to apply
13117
- * @param element - Target element (defaults to document.documentElement)
13118
- */ function applyCSSVariables(vars, element) {
13119
- if ("undefined" == typeof window) return;
13120
- // SSR safety
13121
- const target = element || document.documentElement;
13122
- Object.entries(vars).forEach((([key, value]) => {
13123
- target.style.setProperty(key, String(value));
13124
- }));
13125
- }
13126
-
13127
- /**
13128
- * Remove CSS variables from an element
13129
- *
13130
- * @param varNames - Variable names to remove
13131
- * @param element - Target element (defaults to document.documentElement)
13132
- */ function removeCSSVariables(varNames, element) {
13133
- if ("undefined" == typeof window) return;
13134
- // SSR safety
13135
- const target = element || document.documentElement;
13136
- varNames.forEach((varName => {
13137
- target.style.removeProperty(varName);
13138
- }));
13139
- }
13140
-
13141
- /**
13142
- * Get CSS variable value from an element
13143
- *
13144
- * @param varName - Variable name to get
13145
- * @param element - Target element (defaults to document.documentElement)
13146
- * @returns Variable value or null if not found
13147
- */ function getCSSVariable(varName, element) {
13148
- if ("undefined" == typeof window) return null;
13149
- // SSR safety
13150
- const target = element || document.documentElement;
13151
- return getComputedStyle(target).getPropertyValue(varName).trim() || null;
13152
- }
13153
-
13154
- /**
13155
- * Convert CSS variable object to inline style object
13156
- *
13157
- * @example
13158
- * const vars = { '--atomix-button-bg': '#000' }
13159
- * const style = cssVarsToStyle(vars)
13160
- * // Returns: { '--atomix-button-bg': '#000' } as React.CSSProperties
13161
- */ function cssVarsToStyle(vars) {
13162
- var _context;
13163
- return _reduceInstanceProperty(_context = Object.entries(vars)).call(_context, ((acc, [key, value]) => (acc[key] = "number" == typeof value ? `${value}px` : value,
13164
- acc)), {});
13165
- }
13166
-
13167
- /**
13168
- * Merge multiple CSS variable objects
13169
- * Later objects override earlier ones
13170
- */ function mergeCSSVars(...varObjects) {
13171
- return _reduceInstanceProperty(varObjects).call(varObjects, ((acc, vars) => (vars && Object.assign(acc, vars),
13172
- acc)), {});
13173
- }
13174
-
13175
- /**
13176
- * Validate CSS variable name format
13177
- */ function isValidCSSVariableName(name) {
13178
- return /^--[a-z0-9-]+$/.test(name);
13179
- }
13180
-
13181
- /**
13182
- * Extract component name from CSS variable name
13183
- *
13184
- * @example
13185
- * extractComponentName('--atomix-button-bg')
13186
- * // Returns: 'button'
13187
- */ function extractComponentName(varName, prefix = "atomix") {
13188
- const regex = new RegExp(`^--${prefix}-([a-z0-9]+)-`), match = varName.match(regex);
13189
- return match ? match[1] ?? null : null;
13190
- }
13191
-
13192
- /**
13193
- * Component Utilities
13194
- *
13195
- * Helper functions for component development with the new customization system
13196
- */
13197
- /**
13198
- * Merge multiple class names
13199
- */ function mergeClassNames(...classes) {
13200
- return classes.filter(Boolean).join(" ");
13096
+ * Navbar state and functionality
13097
+ * @param initialProps - Initial navbar properties
13098
+ * @returns Navbar state and methods
13099
+ */ function useNavbar(initialProps) {
13100
+ // Default navbar properties
13101
+ const defaultProps = {
13102
+ position: "static",
13103
+ collapsible: !0,
13104
+ backdrop: !1,
13105
+ closeOnOutsideClick: !0,
13106
+ closeOnEscape: !0,
13107
+ "aria-label": "Main navigation",
13108
+ ...initialProps
13109
+ }, [isExpanded, setIsExpanded] = React.useState(defaultProps.expanded || !1);
13110
+ // Local expanded state for when not controlled externally
13111
+ return {
13112
+ defaultProps: defaultProps,
13113
+ isExpanded: isExpanded,
13114
+ setIsExpanded: setIsExpanded,
13115
+ generateNavbarClass: props => {
13116
+ const {position: position = defaultProps.position, variant: variant, collapsible: collapsible = defaultProps.collapsible, className: className = ""} = props;
13117
+ return `c-navbar ${"static" !== position ? `c-navbar--${position}` : ""} ${variant ? `c-navbar--${variant}` : ""} ${collapsible ? "c-navbar--collapsible" : ""} ${className}`.trim();
13118
+ },
13119
+ generateContainerStyle: width => width ? {
13120
+ maxWidth: width
13121
+ } : {},
13122
+ generateCollapseClass: expanded => ("c-navbar__collapse " + (expanded ? "is-expanded" : "")).trim(),
13123
+ toggleExpanded: () => {
13124
+ const newState = !isExpanded;
13125
+ setIsExpanded(newState), defaultProps.onToggle && defaultProps.onToggle(newState);
13126
+ },
13127
+ getExpandedState: controlled => void 0 !== controlled ? controlled : isExpanded
13128
+ };
13201
13129
  }
13202
13130
 
13203
13131
  /**
13204
- * Apply part styles to element props
13205
- */ function applyPartStyles(baseProps, partStyles) {
13206
- return partStyles ? {
13207
- ...baseProps,
13208
- className: mergeClassNames(baseProps.className, partStyles.className),
13209
- style: {
13210
- ...baseProps.style,
13211
- ...partStyles.style
13132
+ * Nav state and functionality
13133
+ * @param initialProps - Initial nav properties
13134
+ * @returns Nav state and methods
13135
+ */ function useNav(initialProps) {
13136
+ // Default nav properties
13137
+ const defaultProps = {
13138
+ alignment: "start",
13139
+ variant: "default",
13140
+ ...initialProps
13141
+ };
13142
+ /**
13143
+ * Generate nav class based on properties
13144
+ * @param props - Nav properties
13145
+ * @returns Class string
13146
+ */ return {
13147
+ defaultProps: defaultProps,
13148
+ generateNavClass: props => {
13149
+ const {alignment: alignment = defaultProps.alignment, variant: variant = defaultProps.variant, className: className = ""} = props;
13150
+ return `c-nav ${"start" !== alignment ? `c-nav--${alignment}` : ""} ${"default" !== variant ? `c-nav--${variant}` : ""} ${className}`.trim();
13212
13151
  }
13213
- } : baseProps;
13152
+ };
13214
13153
  }
13215
13154
 
13216
13155
  /**
13217
- * Create style object from CSS variables
13218
- */ function createCSSVarStyle(cssVars, baseStyle) {
13219
- return cssVars ? {
13220
- ...cssVarsToStyle(cssVars),
13221
- ...baseStyle
13222
- } : baseStyle || {};
13223
- }
13224
-
13225
- function mergeComponentProps(baseProps, customization) {
13226
- const {className: className, style: style, cssVars: cssVars, parts: parts} = customization, cssVarStyle = cssVars ? cssVarsToStyle(cssVars) : {};
13227
- // Merge CSS variables into style
13228
- return {
13229
- ...baseProps,
13230
- className: mergeClassNames(baseProps.className, className),
13231
- style: {
13232
- ...cssVarStyle,
13233
- ...baseProps.style,
13234
- ...style
13156
+ * Nav item state and functionality
13157
+ * @param initialProps - Initial nav item properties
13158
+ * @returns Nav item state and methods
13159
+ */ function useNavItem(initialProps) {
13160
+ // Default nav item properties
13161
+ const defaultProps = {
13162
+ dropdown: !1,
13163
+ megaMenu: !1,
13164
+ active: !1,
13165
+ ...initialProps
13166
+ };
13167
+ /**
13168
+ * Generate nav item class based on properties
13169
+ * @param props - Nav item properties
13170
+ * @returns Class string
13171
+ */ return {
13172
+ defaultProps: defaultProps,
13173
+ generateNavItemClass: props => {
13174
+ const {dropdown: dropdown = defaultProps.dropdown, megaMenu: megaMenu = defaultProps.megaMenu, active: active = defaultProps.active, disabled: disabled = defaultProps.disabled, className: className = ""} = props;
13175
+ // Apply dropdown class only for regular dropdowns, not mega menus
13176
+ return `c-nav__item ${dropdown && !megaMenu ? NAV.SELECTORS.DROPDOWN.replace(".", "") : ""} ${megaMenu ? "c-nav__item--mega-menu" : ""} ${active ? NAV.CLASSES.ACTIVE : ""} ${disabled ? NAV.CLASSES.DISABLED : ""} ${className}`.trim();
13177
+ },
13178
+ generateNavLinkClass: (active = !1, disabled = !1, className = "") => `c-nav__link ${active ? NAV.CLASSES.ACTIVE : ""} ${disabled ? "c-nav__link--disabled" : ""} ${className}`.trim(),
13179
+ handleClick: handler => e => {
13180
+ !defaultProps.disabled && handler ? handler() : e.preventDefault();
13235
13181
  }
13236
13182
  };
13237
13183
  }
13238
13184
 
13239
13185
  /**
13240
- * Get part styles from parts object
13241
- */ function getPartStyles(parts, partName) {
13242
- return parts?.[partName];
13186
+ * Nav dropdown state and functionality
13187
+ * @param initialProps - Initial dropdown properties
13188
+ * @returns Dropdown state and methods
13189
+ */ function useNavDropdown(initialProps) {
13190
+ // Default dropdown properties
13191
+ const defaultProps = {
13192
+ alignment: "start",
13193
+ megaMenu: !1,
13194
+ ...initialProps
13195
+ }, isInFixedBottomNavbar = () => null !== document.querySelector(".c-navbar--fixed-bottom");
13196
+ /**
13197
+ * Generate dropdown menu class based on properties
13198
+ * @param props - Dropdown properties
13199
+ * @returns Class string
13200
+ */ return {
13201
+ defaultProps: defaultProps,
13202
+ generateDropdownMenuClass: props => {
13203
+ const {alignment: alignment = defaultProps.alignment, megaMenu: megaMenu = defaultProps.megaMenu, className: className = ""} = props, baseClass = megaMenu ? NAV.SELECTORS.MEGA_MENU.replace(".", "") : NAV.SELECTORS.DROPDOWN_MENU.replace(".", "");
13204
+ // Select the base class based on mega menu or regular dropdown
13205
+ // Add alignment class if not default 'start'
13206
+ let alignmentClass = "";
13207
+ return "center" === alignment ? alignmentClass = `${baseClass}--center` : "end" === alignment && (alignmentClass = `${baseClass}--end`),
13208
+ `${baseClass} ${alignmentClass} ${className}`.trim();
13209
+ },
13210
+ isInFixedBottomNavbar: isInFixedBottomNavbar,
13211
+ getIconName: (isMegaMenu = !1) => isInFixedBottomNavbar() ? "CaretUp" : "CaretDown"
13212
+ };
13243
13213
  }
13244
13214
 
13245
13215
  /**
13246
- * Create element props with part styles
13247
- */ function createPartProps(baseClassName, partStyles, additionalProps) {
13248
- return {
13249
- ...additionalProps,
13250
- className: mergeClassNames(baseClassName, partStyles?.className),
13251
- style: {
13252
- ...partStyles?.style,
13253
- ...additionalProps?.style
13216
+ * SideMenu state and functionality
13217
+ * @param initialProps - Initial side menu properties
13218
+ * @returns SideMenu state and methods
13219
+ */ function useSideMenu(initialProps) {
13220
+ const {collapsible: collapsible = !0, collapsibleDesktop: collapsibleDesktop = !1, defaultCollapsedDesktop: defaultCollapsedDesktop = !1, isOpen: isOpen, onToggle: onToggle, disabled: disabled = !1} = initialProps || {}, [isOpenState, setIsOpenState] = React.useState(void 0 !== defaultCollapsedDesktop ? !defaultCollapsedDesktop : isOpen || !1), wrapperRef = React.useRef(null), innerRef = React.useRef(null), sideMenuRef = React.useRef(null);
13221
+ // Local open state for when not controlled externally
13222
+ // Update local state when external state changes
13223
+ React.useEffect((() => {
13224
+ void 0 !== isOpen ? setIsOpenState(isOpen) : void 0 !== defaultCollapsedDesktop && setIsOpenState(!defaultCollapsedDesktop);
13225
+ }), [ isOpen, defaultCollapsedDesktop ]),
13226
+ // Set initial height on mount
13227
+ React.useEffect((() => {
13228
+ const shouldCollapse = window.innerWidth < 768 ? collapsible : collapsibleDesktop, currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
13229
+ if (shouldCollapse && wrapperRef.current && innerRef.current) {
13230
+ // Use setTimeout to ensure DOM is fully rendered
13231
+ const timeoutId = setTimeout((() => {
13232
+ wrapperRef.current && innerRef.current && (wrapperRef.current.style.height = currentOpen ? `${innerRef.current.scrollHeight}px` : "0px");
13233
+ }), 0);
13234
+ return () => clearTimeout(timeoutId);
13254
13235
  }
13236
+ !shouldCollapse && wrapperRef.current && (wrapperRef.current.style.height = "auto");
13237
+ }), [ collapsible, collapsibleDesktop, isOpen, isOpenState ]),
13238
+ // Handle responsive behavior - vertical collapse for both mobile and desktop
13239
+ React.useEffect((() => {
13240
+ const handleResize = () => {
13241
+ if (window.innerWidth < 768 ? collapsible : collapsibleDesktop) {
13242
+ if (wrapperRef.current && innerRef.current) {
13243
+ // Set proper height for vertical animation (both mobile and desktop)
13244
+ const currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
13245
+ // Use requestAnimationFrame to ensure DOM is ready
13246
+ requestAnimationFrame((() => {
13247
+ wrapperRef.current && innerRef.current && (wrapperRef.current.style.height = currentOpen ? `${innerRef.current.scrollHeight}px` : "0px");
13248
+ }));
13249
+ }
13250
+ } else
13251
+ // Not collapsible - always show content
13252
+ wrapperRef.current && (wrapperRef.current.style.height = "auto");
13253
+ }, timeoutId = setTimeout(handleResize, 0);
13254
+ // Initial call with a small delay to ensure DOM is ready
13255
+ return window.addEventListener("resize", handleResize), () => {
13256
+ clearTimeout(timeoutId), window.removeEventListener("resize", handleResize);
13257
+ };
13258
+ }), [ collapsible, collapsibleDesktop, isOpen, onToggle, isOpenState ]),
13259
+ // Update wrapper height when open state changes (both mobile and desktop)
13260
+ React.useEffect((() => {
13261
+ const shouldCollapse = window.innerWidth < 768 ? collapsible : collapsibleDesktop;
13262
+ if (shouldCollapse && wrapperRef.current && innerRef.current) {
13263
+ const currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
13264
+ // Use requestAnimationFrame to ensure DOM is ready
13265
+ requestAnimationFrame((() => {
13266
+ wrapperRef.current && innerRef.current && (wrapperRef.current.style.height = currentOpen ? `${innerRef.current.scrollHeight}px` : "0px");
13267
+ }));
13268
+ } else !shouldCollapse && wrapperRef.current && (
13269
+ // Not collapsible - always show content
13270
+ wrapperRef.current.style.height = "auto");
13271
+ }), [ isOpen, isOpenState, collapsible, collapsibleDesktop ]);
13272
+ /**
13273
+ * Generate side menu class based on properties
13274
+ * @param props - Side menu properties
13275
+ * @returns Class string
13276
+ */
13277
+ const handleToggle = () => {
13278
+ if (disabled) return;
13279
+ const newState = void 0 !== isOpen ? !isOpen : !isOpenState;
13280
+ "function" == typeof onToggle ?
13281
+ // Controlled component
13282
+ onToggle(newState) :
13283
+ // Uncontrolled component
13284
+ setIsOpenState(newState);
13285
+ }, getCurrentOpenState = () => void 0 !== isOpen ? isOpen : isOpenState;
13286
+ /**
13287
+ * Generate wrapper class
13288
+ * @returns Class string
13289
+ */ return {
13290
+ isOpenState: getCurrentOpenState(),
13291
+ wrapperRef: wrapperRef,
13292
+ innerRef: innerRef,
13293
+ sideMenuRef: sideMenuRef,
13294
+ generateSideMenuClass: props => {
13295
+ const {className: className = "", isOpen: isOpen = !1} = props, openClass = isOpen ? SIDE_MENU.CLASSES.IS_OPEN : "";
13296
+ return `${SIDE_MENU.CLASSES.BASE} ${openClass} ${className}`.trim();
13297
+ },
13298
+ generateWrapperClass: () => SIDE_MENU.CLASSES.WRAPPER,
13299
+ handleToggle: handleToggle,
13300
+ handleDesktopCollapse: () => {
13301
+ handleToggle();
13302
+ },
13303
+ getCurrentOpenState: getCurrentOpenState
13255
13304
  };
13256
13305
  }
13257
13306
 
13258
13307
  /**
13259
- * Check if component has customization
13260
- */ function hasCustomization(props) {
13261
- return Boolean(props.parts || props.cssVars || props.slots);
13262
- }
13263
-
13264
- /**
13265
- * Create data attributes for debugging
13266
- */ function createDebugAttrs(componentName, variant) {
13267
- return "undefined" == typeof process || "development" !== process.env?.NODE_ENV ? {} : {
13268
- "data-component": componentName,
13269
- ...variant && {
13270
- "data-variant": variant
13308
+ * SideMenuItem state and functionality
13309
+ * @param initialProps - Initial side menu item properties
13310
+ * @returns SideMenuItem state and methods
13311
+ */ function useSideMenuItem(initialProps) {
13312
+ // Default side menu item properties
13313
+ const defaultProps = {
13314
+ active: !1,
13315
+ disabled: !1,
13316
+ ...initialProps
13317
+ };
13318
+ /**
13319
+ * Generate side menu item class based on properties
13320
+ * @returns Class string
13321
+ */ return {
13322
+ defaultProps: defaultProps,
13323
+ generateSideMenuItemClass: () => {
13324
+ const {active: active = defaultProps.active, disabled: disabled = defaultProps.disabled, className: className = ""} = defaultProps, activeClass = active ? SIDE_MENU.CLASSES.ACTIVE : "", disabledClass = disabled ? SIDE_MENU.CLASSES.DISABLED : "";
13325
+ return `${SIDE_MENU.CLASSES.LINK} ${activeClass} ${disabledClass} ${className}`.trim();
13326
+ },
13327
+ handleClick: handler => e => {
13328
+ defaultProps.disabled ? e.preventDefault() : handler && handler(e);
13271
13329
  }
13272
13330
  };
13273
13331
  }
13274
13332
 
13275
- /**
13276
- * Generate a UUID v4
13277
- */ function generateUUID() {
13278
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c => {
13279
- const r = 16 * Math.random() | 0;
13280
- return ("x" === c ? r : 3 & r | 8).toString(16);
13281
- }));
13282
- }
13283
-
13284
- /**
13285
- * Check if a URL is a YouTube URL
13286
- */ function isYouTubeUrl(url) {
13287
- return /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/.test(url);
13288
- }
13289
-
13290
- /**
13291
- * Extract YouTube video ID from URL
13292
- */ function extractYouTubeId(url) {
13293
- if (!isYouTubeUrl(url)) return null;
13294
- const patterns = [ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, /youtube\.com\/.*[?&]v=([^&\n?#]+)/ ];
13295
- for (const pattern of patterns) {
13296
- const match = url.match(pattern);
13297
- if (match && match[1]) return match[1];
13298
- }
13299
- return null;
13300
- }
13301
-
13302
13333
  /**
13303
13334
  * Font Preloading Utilities
13304
13335
  *
@@ -13664,48 +13695,11 @@ var composablesImport = Object.freeze({
13664
13695
  DeviceDetector: DeviceDetector,
13665
13696
  MOBILE_OPTIMIZED_BREAKPOINTS: MOBILE_OPTIMIZED_BREAKPOINTS,
13666
13697
  PERFORMANCE_PRESET: PERFORMANCE_PRESET,
13667
- PerformanceOverlay: function({metrics: metrics}) {
13668
- return null;
13669
- // Performance overlay removed - will be implemented as separate component
13670
- }
13671
- /**
13672
- * Utility to get quality multipliers for glass parameters
13673
- */ ,
13674
13698
  QUALITY_PRESET: QUALITY_PRESET,
13675
13699
  createBreakpoints: createBreakpoints$1,
13676
13700
  getDefaultBreakpoints: getDefaultBreakpoints,
13677
13701
  getDevicePreset: getDevicePreset,
13678
13702
  getMobileOptimizedParams: getMobileOptimizedParams,
13679
- getQualityMultipliers: function(quality) {
13680
- switch (quality) {
13681
- case "low":
13682
- return {
13683
- distortionOctaves: 2,
13684
- displacementScale: .6,
13685
- blurAmount: .7,
13686
- animationSpeed: .8,
13687
- chromaticIntensity: .5
13688
- };
13689
-
13690
- case "medium":
13691
- return {
13692
- distortionOctaves: 4,
13693
- displacementScale: .85,
13694
- blurAmount: .9,
13695
- animationSpeed: .95,
13696
- chromaticIntensity: .75
13697
- };
13698
-
13699
- case "high":
13700
- return {
13701
- distortionOctaves: 5,
13702
- displacementScale: 1,
13703
- blurAmount: 1,
13704
- animationSpeed: 1,
13705
- chromaticIntensity: 1
13706
- };
13707
- }
13708
- },
13709
13703
  useAccordion: useAccordion,
13710
13704
  useAtomixGlass: useAtomixGlass,
13711
13705
  useBadge: useBadge,
@@ -14008,12 +14002,7 @@ const SelectComponentBase = ({options: options, value: value, onChange: onChange
14008
14002
  });
14009
14003
  // Handle item selection
14010
14004
  if (glass) {
14011
- // Default glass settings for select components
14012
- const defaultGlassProps = {
14013
- displacementScale: 60,
14014
- blurAmount: 10,
14015
- mode: "shader"
14016
- }, glassProps = !0 === glass ? defaultGlassProps : {
14005
+ const defaultGlassProps = GLASS_DEFAULTS.SELECT, glassProps = !0 === glass ? defaultGlassProps : {
14017
14006
  ...defaultGlassProps,
14018
14007
  ...glass
14019
14008
  };
@@ -14063,15 +14052,7 @@ const Radio = React.memo((({label: label, checked: checked = !1, onChange: onCh
14063
14052
  }) ]
14064
14053
  });
14065
14054
  if (glass) {
14066
- // Default glass settings for radio buttons
14067
- const defaultGlassProps = {
14068
- displacementScale: 40,
14069
- blurAmount: 1,
14070
- saturation: 160,
14071
- aberrationIntensity: .3,
14072
- borderRadius: 6,
14073
- mode: "shader"
14074
- }, glassProps = !0 === glass ? defaultGlassProps : {
14055
+ const defaultGlassProps = GLASS_DEFAULTS.RADIO, glassProps = !0 === glass ? defaultGlassProps : {
14075
14056
  ...defaultGlassProps,
14076
14057
  ...glass
14077
14058
  };
@@ -14129,15 +14110,7 @@ const Textarea = React.memo( React.forwardRef((({value: value, defaultValue: de
14129
14110
  } : style
14130
14111
  });
14131
14112
  if (glass) {
14132
- // Default glass settings for textareas
14133
- const defaultGlassProps = {
14134
- displacementScale: 60,
14135
- blurAmount: 1,
14136
- saturation: 180,
14137
- aberrationIntensity: 1,
14138
- borderRadius: 8,
14139
- mode: "shader"
14140
- }, glassProps = !0 === glass ? defaultGlassProps : {
14113
+ const defaultGlassProps = GLASS_DEFAULTS.TEXTAREA, glassProps = !0 === glass ? defaultGlassProps : {
14141
14114
  ...defaultGlassProps,
14142
14115
  ...glass
14143
14116
  };
@@ -15260,12 +15233,7 @@ const Messages = ({messages: messages = [], otherAvatar: otherAvatar, selfAvatar
15260
15233
  };
15261
15234
  })({
15262
15235
  onSendMessage: onSendMessage
15263
- }), messagesId = id || `messages-${Math.random().toString(36).substr(2, 9)}`, inputId = `${messagesId}-input`, defaultGlassProps = {
15264
- displacementScale: 150,
15265
- borderRadius: 12,
15266
- elasticity: 0,
15267
- aberrationIntensity: 2
15268
- }, messagesClasses = `${MESSAGES.CLASSES.BASE} ${glass ? "c-messages--glass" : ""} ${disabled ? "is-disabled" : ""} ${className}`, messagesContent = jsxRuntime.jsxs(jsxRuntime.Fragment, {
15236
+ }), messagesId = id || `messages-${Math.random().toString(36).substr(2, 9)}`, inputId = `${messagesId}-input`, defaultGlassProps = GLASS_DEFAULTS.MESSAGES, messagesClasses = `${MESSAGES.CLASSES.BASE} ${glass ? "c-messages--glass" : ""} ${disabled ? "is-disabled" : ""} ${className}`, messagesContent = jsxRuntime.jsxs(jsxRuntime.Fragment, {
15269
15237
  children: [ jsxRuntime.jsx("div", {
15270
15238
  className: MESSAGES.CLASSES.BODY,
15271
15239
  style: bodyHeight ? {
@@ -15625,12 +15593,11 @@ const ModalImpl = React.memo((({children: children, isOpen: isOpen = !1, onOpen
15625
15593
  children: glass ?
15626
15594
  // Default glass settings for modals
15627
15595
  (() => {
15596
+ // Default glass settings for modals
15628
15597
  const defaultGlassProps = {
15629
- displacementScale: document.querySelector(".c-modal---glass .c-modal__content")?.clientHeight,
15630
- blurAmount: 2.2,
15631
- elasticity: 0,
15632
- mode: "shader",
15633
- shaderMode: "premiumGlass"
15598
+ ...GLASS_DEFAULTS.MODAL,
15599
+ // displacementScale driven by modal content height at runtime
15600
+ displacementScale: document.querySelector(".c-modal---glass .c-modal__content")?.clientHeight
15634
15601
  }, glassProps = !0 === glass ? defaultGlassProps : {
15635
15602
  ...defaultGlassProps,
15636
15603
  ...glass
@@ -15678,12 +15645,7 @@ const Modal = ModalWithSubcomponents, Nav = React.forwardRef((({children: child
15678
15645
  }))
15679
15646
  });
15680
15647
  if (glass) {
15681
- const defaultGlassProps = {
15682
- displacementScale: 60,
15683
- blurAmount: 1.5,
15684
- borderRadius: 8,
15685
- mode: "shader"
15686
- }, glassProps = !0 === glass ? defaultGlassProps : {
15648
+ const defaultGlassProps = GLASS_DEFAULTS.NAV, glassProps = !0 === glass ? defaultGlassProps : {
15687
15649
  ...defaultGlassProps,
15688
15650
  ...glass
15689
15651
  };
@@ -15709,24 +15671,7 @@ const Modal = ModalWithSubcomponents, Nav = React.forwardRef((({children: child
15709
15671
  * </NavDropdown>
15710
15672
  * </Nav>
15711
15673
  * ```
15712
- */
15713
- /**
15714
- * Utility to merge multiple React refs into one
15715
- */
15716
- function setRef(ref, value) {
15717
- "function" == typeof ref ? ref(value) : ref && (
15718
- // This is safe because we're checking that ref exists first
15719
- ref.current = value);
15720
- }
15721
-
15722
- /**
15723
- * Combines two React refs into a single ref function
15724
- * This is used when you need to use and forward a ref at the same time
15725
- */ function useForkRef(refA, refB) {
15726
- return React__default.default.useMemo((() => null == refA && null == refB ? null : refValue => {
15727
- setRef(refA, refValue), setRef(refB, refValue);
15728
- }), [ refA, refB ]);
15729
- }
15674
+ */ Nav.displayName = "Nav";
15730
15675
 
15731
15676
  /**
15732
15677
  * NavItem component represents a single navigation item that can be a link, dropdown trigger, or mega menu trigger.
@@ -15755,8 +15700,7 @@ function setRef(ref, value) {
15755
15700
  * </MegaMenu>
15756
15701
  * </NavItem>
15757
15702
  * ```
15758
- */ Nav.displayName = "Nav";
15759
-
15703
+ */
15760
15704
  const NavItem = React.forwardRef((({children: children, dropdown: dropdown = !1, megaMenu: megaMenu = !1, active: active = !1, href: href, target: target, onClick: onClick, className: className = "", disabled: disabled = !1, "aria-expanded": ariaExpanded, linkComponent: linkComponent}, ref) => {
15761
15705
  const {generateNavItemClass: generateNavItemClass, generateNavLinkClass: generateNavLinkClass, handleClick: handleClick} = useNavItem({
15762
15706
  dropdown: dropdown,
@@ -15929,14 +15873,7 @@ const Navbar = React.forwardRef((({brand: brand, children: children, variant: v
15929
15873
  });
15930
15874
  // Generate the container style
15931
15875
  if (glass) {
15932
- const defaultGlassProps = {
15933
- displacementScale: 30,
15934
- blurAmount: 2,
15935
- borderRadius: 0,
15936
- elasticity: 0,
15937
- mode: "shader",
15938
- shaderVariant: "premiumGlass"
15939
- }, glassProps = !0 === glass ? defaultGlassProps : {
15876
+ const defaultGlassProps = GLASS_DEFAULTS.NAVBAR, glassProps = !0 === glass ? defaultGlassProps : {
15940
15877
  ...defaultGlassProps,
15941
15878
  ...glass
15942
15879
  }, isFixed = "fixed" === position || "fixed-bottom" === position;
@@ -16294,12 +16231,7 @@ const SideMenu = React.forwardRef((({title: title, children: children, menuItem
16294
16231
  }) ]
16295
16232
  });
16296
16233
  if (glass) {
16297
- const defaultGlassProps = {
16298
- displacementScale: 70,
16299
- blurAmount: 2,
16300
- borderRadius: 12,
16301
- mode: "shader"
16302
- }, glassProps = !0 === glass ? defaultGlassProps : {
16234
+ const defaultGlassProps = GLASS_DEFAULTS.SIDE_MENU, glassProps = !0 === glass ? defaultGlassProps : {
16303
16235
  ...defaultGlassProps,
16304
16236
  ...glass
16305
16237
  };
@@ -17896,14 +17828,7 @@ const PopoverContext = React.createContext({
17896
17828
  children: [ glass ?
17897
17829
  // Default glass settings for popovers
17898
17830
  (() => {
17899
- const defaultGlassProps = {
17900
- displacementScale: 50,
17901
- blurAmount: 1,
17902
- saturation: 160,
17903
- aberrationIntensity: .5,
17904
- borderRadius: 8,
17905
- mode: "shader"
17906
- }, glassProps = !0 === glass ? defaultGlassProps : {
17831
+ const defaultGlassProps = GLASS_DEFAULTS.POPOVER, glassProps = !0 === glass ? defaultGlassProps : {
17907
17832
  ...defaultGlassProps,
17908
17833
  ...glass
17909
17834
  };
@@ -18128,15 +18053,7 @@ const calculateStarValue = (e, starValue, allowHalf) => {
18128
18053
  });
18129
18054
  // Generate stars
18130
18055
  if (glass) {
18131
- // Default glass settings for ratings
18132
- const defaultGlassProps = {
18133
- displacementScale: 60,
18134
- blurAmount: 1,
18135
- saturation: 160,
18136
- aberrationIntensity: .5,
18137
- borderRadius: 8,
18138
- mode: "shader"
18139
- }, glassProps = !0 === glass ? defaultGlassProps : {
18056
+ const defaultGlassProps = GLASS_DEFAULTS.RATING, glassProps = !0 === glass ? defaultGlassProps : {
18140
18057
  ...defaultGlassProps,
18141
18058
  ...glass
18142
18059
  };
@@ -18292,12 +18209,7 @@ const Progress = React.memo( React.forwardRef((({value: value, variant: variant
18292
18209
  })
18293
18210
  });
18294
18211
  if (glass) {
18295
- const defaultGlassProps = {
18296
- displacementScale: 30,
18297
- blurAmount: .5,
18298
- borderRadius: 8,
18299
- mode: "shader"
18300
- }, glassProps = !0 === glass ? defaultGlassProps : {
18212
+ const defaultGlassProps = GLASS_DEFAULTS.PROGRESS, glassProps = !0 === glass ? defaultGlassProps : {
18301
18213
  ...defaultGlassProps,
18302
18214
  ...glass
18303
18215
  };
@@ -18946,15 +18858,7 @@ const Steps = ({items: items, activeIndex: activeIndex = 0, vertical: vertical =
18946
18858
  children: content
18947
18859
  });
18948
18860
  if (glass) {
18949
- // Default glass settings for steps
18950
- const defaultGlassProps = {
18951
- displacementScale: 60,
18952
- blurAmount: 1,
18953
- saturation: 160,
18954
- aberrationIntensity: .5,
18955
- borderRadius: 8,
18956
- mode: "shader"
18957
- }, glassProps = !0 === glass ? defaultGlassProps : {
18861
+ const defaultGlassProps = GLASS_DEFAULTS.STEPS, glassProps = !0 === glass ? defaultGlassProps : {
18958
18862
  ...defaultGlassProps,
18959
18863
  ...glass
18960
18864
  };
@@ -19158,14 +19062,7 @@ const TabsComponentBase = ({items: items, activeIndex: activeIndex = TAB.DEFAULT
19158
19062
  });
19159
19063
  if (glass) {
19160
19064
  // Default glass settings for tabs
19161
- const defaultGlassProps = {
19162
- displacementScale: 60,
19163
- blurAmount: 1,
19164
- saturation: 160,
19165
- aberrationIntensity: .5,
19166
- borderRadius: 8,
19167
- mode: "shader"
19168
- }, glassProps = !0 === glass ? defaultGlassProps : {
19065
+ const defaultGlassProps = GLASS_DEFAULTS.TABS, glassProps = !0 === glass ? defaultGlassProps : {
19169
19066
  ...defaultGlassProps,
19170
19067
  ...glass
19171
19068
  };
@@ -19401,15 +19298,7 @@ const Toggle = ({checked: controlledChecked, defaultChecked: defaultChecked = !1
19401
19298
  })
19402
19299
  });
19403
19300
  if (glass) {
19404
- // Default glass settings for toggles
19405
- const defaultGlassProps = {
19406
- displacementScale: 60,
19407
- blurAmount: 1,
19408
- saturation: 160,
19409
- aberrationIntensity: .5,
19410
- borderRadius: 8,
19411
- mode: "shader"
19412
- }, glassProps = !0 === glass ? defaultGlassProps : {
19301
+ const defaultGlassProps = GLASS_DEFAULTS.TOGGLE, glassProps = !0 === glass ? defaultGlassProps : {
19413
19302
  ...defaultGlassProps,
19414
19303
  ...glass
19415
19304
  };
@@ -19597,10 +19486,7 @@ const Tooltip = React.memo((({content: content, children: children, position: p
19597
19486
  }), content ]
19598
19487
  });
19599
19488
  if (glass) {
19600
- const defaultGlassProps = {
19601
- displacementScale: 100,
19602
- blurAmount: 3
19603
- }, glassProps = !0 === glass ? defaultGlassProps : {
19489
+ const defaultGlassProps = GLASS_DEFAULTS.TOOLTIP, glassProps = !0 === glass ? defaultGlassProps : {
19604
19490
  ...defaultGlassProps,
19605
19491
  ...glass
19606
19492
  };
@@ -20776,6 +20662,8 @@ var components = Object.freeze({
20776
20662
  FOOTER: FOOTER,
20777
20663
  FORM: FORM,
20778
20664
  FORM_GROUP: FORM_GROUP,
20665
+ GLASS_BORDER_GRADIENT: GLASS_BORDER_GRADIENT,
20666
+ GLASS_DEFAULTS: GLASS_DEFAULTS,
20779
20667
  HERO: HERO,
20780
20668
  INPUT: INPUT,
20781
20669
  LIST: LIST,
@@ -28340,6 +28228,7 @@ exports.EDGE_PANEL = EDGE_PANEL, exports.EdgePanel = EdgePanel, exports.Elevatio
28340
28228
  exports.FOOTER = FOOTER, exports.FORM = FORM, exports.FORM_GROUP = FORM_GROUP, exports.Footer = Footer,
28341
28229
  exports.FooterLink = FooterLink, exports.FooterSection = FooterSection, exports.FooterSocialLink = FooterSocialLink,
28342
28230
  exports.Form = Form, exports.FormGroup = FormGroup, exports.FunnelChart = FunnelChart,
28231
+ exports.GLASS_BORDER_GRADIENT = GLASS_BORDER_GRADIENT, exports.GLASS_DEFAULTS = GLASS_DEFAULTS,
28343
28232
  exports.GaugeChart = GaugeChart, exports.Grid = Grid, exports.GridCol = GridCol,
28344
28233
  exports.HERO = HERO, exports.HeatmapChart = HeatmapChart, exports.Hero = Hero, exports.INPUT = INPUT,
28345
28234
  exports.INPUT_CSS_VARS = INPUT_CSS_VARS, exports.Icon = Icon, exports.Input = Input,