@geomak/ui 7.1.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { icons_default as Icon, createIcon } from './chunk-KAFJJO5O.js';
2
2
  import { colors_default } from './chunk-I2P4JJDB.js';
3
3
  export { colors_default as COLORS, PALETTE as palette, semanticTokens, vars } from './chunk-I2P4JJDB.js';
4
- import React28, { createContext, useState, useEffect, useMemo, useId, useCallback, useRef, useContext, useSyncExternalStore, useLayoutEffect } from 'react';
4
+ import React29, { createContext, useState, useEffect, useMemo, useId, useCallback, useRef, useContext, useSyncExternalStore, useLayoutEffect } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
@@ -16,8 +16,8 @@ import * as ToggleGroup from '@radix-ui/react-toggle-group';
16
16
  import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
17
17
  import * as SwitchPrimitive from '@radix-ui/react-switch';
18
18
  import * as NavigationMenu from '@radix-ui/react-navigation-menu';
19
- import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
20
19
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
20
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
21
21
  import * as SliderPrimitive from '@radix-ui/react-slider';
22
22
 
23
23
  // src/utils/cx.ts
@@ -555,7 +555,7 @@ var SIZE_CLASSES = {
555
555
  md: "h-9 px-4 text-sm gap-1.5 rounded-lg",
556
556
  lg: "h-11 px-5 text-sm gap-2 rounded-xl"
557
557
  };
558
- var Button = React28.forwardRef(function Button2({
558
+ var Button = React29.forwardRef(function Button2({
559
559
  content,
560
560
  variant = "primary",
561
561
  size = "md",
@@ -663,7 +663,7 @@ function MenuButton({
663
663
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
664
664
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95"
665
665
  ].join(" "),
666
- children: items.map((item) => /* @__PURE__ */ jsxs(React28.Fragment, { children: [
666
+ children: items.map((item) => /* @__PURE__ */ jsxs(React29.Fragment, { children: [
667
667
  item.separatorBefore && /* @__PURE__ */ jsx(DropdownMenu.Separator, { className: "my-1 h-px bg-border" }),
668
668
  /* @__PURE__ */ jsxs(
669
669
  DropdownMenu.Item,
@@ -1762,7 +1762,7 @@ function Kbd({
1762
1762
  style
1763
1763
  }) {
1764
1764
  if (keys && keys.length > 0) {
1765
- return /* @__PURE__ */ jsx("span", { className: cx("inline-flex items-center gap-1", className), style, children: keys.map((k, i) => /* @__PURE__ */ jsxs(React28.Fragment, { children: [
1765
+ return /* @__PURE__ */ jsx("span", { className: cx("inline-flex items-center gap-1", className), style, children: keys.map((k, i) => /* @__PURE__ */ jsxs(React29.Fragment, { children: [
1766
1766
  i > 0 && /* @__PURE__ */ jsx("span", { className: "text-foreground-muted text-xs select-none", children: separator }),
1767
1767
  /* @__PURE__ */ jsx("kbd", { className: [cap, SIZE3[size]].join(" "), children: k })
1768
1768
  ] }, `${k}-${i}`)) });
@@ -1854,7 +1854,7 @@ function FlatCarousel({
1854
1854
  style
1855
1855
  }) {
1856
1856
  const scrollerRef = useRef(null);
1857
- const slides = React28.Children.toArray(children);
1857
+ const slides = React29.Children.toArray(children);
1858
1858
  const [active, setActive] = useState(0);
1859
1859
  const [atStart, setAtStart] = useState(true);
1860
1860
  const [atEnd, setAtEnd] = useState(false);
@@ -1909,7 +1909,7 @@ function RotatingCarousel({
1909
1909
  className = "",
1910
1910
  style
1911
1911
  }) {
1912
- const slides = React28.Children.toArray(children);
1912
+ const slides = React29.Children.toArray(children);
1913
1913
  const count = slides.length;
1914
1914
  const [active, setActive] = useState(0);
1915
1915
  const reduced = useReducedMotion();
@@ -5131,7 +5131,7 @@ function Wizard({
5131
5131
  ] });
5132
5132
  }
5133
5133
  var SearchIcon = /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) });
5134
- var SearchInput = React28.forwardRef(function SearchInput2({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout = "vertical", size = "md", icon, helperText, className }, ref) {
5134
+ var SearchInput = React29.forwardRef(function SearchInput2({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout = "vertical", size = "md", icon, helperText, className }, ref) {
5135
5135
  return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, layout, helperText, children: /* @__PURE__ */ jsxs(
5136
5136
  "div",
5137
5137
  {
@@ -5428,7 +5428,7 @@ function TableBody({
5428
5428
  return /* @__PURE__ */ jsx("tbody", { children: rows.map((row, i) => {
5429
5429
  const rowKey = getRowKey(row, i);
5430
5430
  const isExpanded = expanded.has(rowKey);
5431
- return /* @__PURE__ */ jsxs(React28.Fragment, { children: [
5431
+ return /* @__PURE__ */ jsxs(React29.Fragment, { children: [
5432
5432
  /* @__PURE__ */ jsxs(
5433
5433
  "tr",
5434
5434
  {
@@ -5715,6 +5715,84 @@ function TableSkeletonBody({
5715
5715
  i
5716
5716
  )) });
5717
5717
  }
5718
+ var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
5719
+ function VirtualList({
5720
+ items,
5721
+ rowHeight,
5722
+ renderItem: renderItem2,
5723
+ height = 400,
5724
+ getKey,
5725
+ overscan = 6,
5726
+ searchable = false,
5727
+ searchKeys,
5728
+ filter,
5729
+ searchPlaceholder = "Search\u2026",
5730
+ emptyState = "No results",
5731
+ "aria-label": ariaLabel = "List",
5732
+ className = "",
5733
+ style
5734
+ }) {
5735
+ const scrollRef = useRef(null);
5736
+ const [scrollTop, setScrollTop] = useState(0);
5737
+ const [viewport, setViewport] = useState(typeof height === "number" ? height : 400);
5738
+ const [term, setTerm] = useState("");
5739
+ useIsoLayoutEffect(() => {
5740
+ const el = scrollRef.current;
5741
+ if (!el) return;
5742
+ const measure = () => setViewport(el.clientHeight);
5743
+ measure();
5744
+ if (typeof ResizeObserver === "undefined") return;
5745
+ const ro = new ResizeObserver(measure);
5746
+ ro.observe(el);
5747
+ return () => ro.disconnect();
5748
+ }, []);
5749
+ const filtered = useMemo(() => {
5750
+ if (!searchable || !term) return items;
5751
+ if (filter) return items.filter((it) => filter(it, term));
5752
+ const needle = term.toLowerCase();
5753
+ return items.filter((it) => {
5754
+ if (searchKeys) return searchKeys.some((k) => String(it[k] ?? "").toLowerCase().includes(needle));
5755
+ return String(it).toLowerCase().includes(needle);
5756
+ });
5757
+ }, [items, searchable, term, filter, searchKeys]);
5758
+ const total = filtered.length * rowHeight;
5759
+ const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
5760
+ const endIndex = Math.min(filtered.length, Math.ceil((scrollTop + viewport) / rowHeight) + overscan);
5761
+ const offsetY = startIndex * rowHeight;
5762
+ const visible = filtered.slice(startIndex, endIndex);
5763
+ const onSearch = (e) => {
5764
+ setTerm(e.target.value);
5765
+ setScrollTop(0);
5766
+ if (scrollRef.current) scrollRef.current.scrollTop = 0;
5767
+ };
5768
+ return /* @__PURE__ */ jsxs("div", { className: cx("flex w-full flex-col gap-2", className), style, children: [
5769
+ searchable && /* @__PURE__ */ jsx(SearchInput_default, { value: term, onChange: onSearch, placeholder: searchPlaceholder }),
5770
+ /* @__PURE__ */ jsx(
5771
+ "div",
5772
+ {
5773
+ ref: scrollRef,
5774
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
5775
+ role: "list",
5776
+ "aria-label": ariaLabel,
5777
+ className: "relative overflow-y-auto rounded-lg border border-border bg-surface",
5778
+ style: { height },
5779
+ children: filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center p-6 text-sm text-foreground-muted", children: emptyState }) : /* @__PURE__ */ jsx("div", { style: { height: total, position: "relative" }, children: /* @__PURE__ */ jsx("div", { style: { transform: `translateY(${offsetY}px)` }, children: visible.map((item, i) => {
5780
+ const index = startIndex + i;
5781
+ return /* @__PURE__ */ jsx(
5782
+ "div",
5783
+ {
5784
+ role: "listitem",
5785
+ style: { height: rowHeight },
5786
+ className: "box-border",
5787
+ children: renderItem2(item, index)
5788
+ },
5789
+ getKey ? getKey(item, index) : index
5790
+ );
5791
+ }) }) })
5792
+ }
5793
+ )
5794
+ ] });
5795
+ }
5718
5796
  function ThemeSwitch({ checked, onChange, label = "Toggle dark mode", className = "" }) {
5719
5797
  const id = useId();
5720
5798
  return /* @__PURE__ */ jsx("label", { htmlFor: id, className: `flex items-center gap-2 cursor-pointer select-none ${className}`.trim(), children: /* @__PURE__ */ jsx(
@@ -6016,8 +6094,8 @@ function MegaMenuLink({ href, icon, description, active, onClick, children, clas
6016
6094
  function MegaMenuFeatured({ children, className = "" }) {
6017
6095
  return /* @__PURE__ */ jsx("div", { className: cx("min-w-0 rounded-lg bg-surface-raised border border-border p-4 flex flex-col", className), children });
6018
6096
  }
6019
- var elementsOfType = (children, type) => React28.Children.toArray(children).filter(
6020
- (c) => React28.isValidElement(c) && c.type === type
6097
+ var elementsOfType = (children, type) => React29.Children.toArray(children).filter(
6098
+ (c) => React29.isValidElement(c) && c.type === type
6021
6099
  );
6022
6100
  var MOBILE_CHEVRON = /* @__PURE__ */ jsx(
6023
6101
  "svg",
@@ -6054,9 +6132,9 @@ function MobileLinkRow({ link, onNavigate }) {
6054
6132
  );
6055
6133
  }
6056
6134
  function MobilePanel({ panel, onNavigate }) {
6057
- const nodes = React28.Children.toArray(panel.props.children);
6135
+ const nodes = React29.Children.toArray(panel.props.children);
6058
6136
  return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 px-2 pb-3 pt-1", children: nodes.map((node, i) => {
6059
- if (!React28.isValidElement(node)) return null;
6137
+ if (!React29.isValidElement(node)) return null;
6060
6138
  const el = node;
6061
6139
  if (el.type === MegaMenuSection) {
6062
6140
  const { title, children } = el.props;
@@ -6465,7 +6543,7 @@ function ThemeProvider({
6465
6543
  className = "",
6466
6544
  style
6467
6545
  }) {
6468
- const id = React28.useId().replace(/:/g, "");
6546
+ const id = React29.useId().replace(/:/g, "");
6469
6547
  const scopeClass = `geo-th-${id}`;
6470
6548
  const divRef = useRef(null);
6471
6549
  useEffect(() => {
@@ -6712,6 +6790,161 @@ function Password({
6712
6790
  }
6713
6791
  );
6714
6792
  }
6793
+ var defaultPasswordRules = [
6794
+ { label: "At least 8 characters", test: (p) => p.length >= 8 },
6795
+ { label: "A lowercase letter", test: (p) => /[a-z]/.test(p) },
6796
+ { label: "An uppercase letter", test: (p) => /[A-Z]/.test(p) },
6797
+ { label: "A number", test: (p) => /\d/.test(p) },
6798
+ { label: "A symbol", test: (p) => /[^A-Za-z0-9]/.test(p) }
6799
+ ];
6800
+ var LABELS = ["", "Weak", "Fair", "Good", "Strong"];
6801
+ function scorePassword(password) {
6802
+ if (!password) return { score: 0, label: "" };
6803
+ let s = 0;
6804
+ if (password.length >= 8) s++;
6805
+ if (/[a-z]/.test(password) && /[A-Z]/.test(password)) s++;
6806
+ if (/\d/.test(password)) s++;
6807
+ if (/[^A-Za-z0-9]/.test(password)) s++;
6808
+ if (password.length >= 12) s++;
6809
+ const lowEntropy = /^(.)\1+$/.test(password) || /^(?:0123456789|abcdefghijklmnopqrstuvwxyz|qwertyuiop)/i.test(password);
6810
+ if (lowEntropy) s = Math.min(s, 1);
6811
+ const score = Math.max(1, Math.min(4, s));
6812
+ return { score, label: LABELS[score] };
6813
+ }
6814
+ var BAR_COLOR = {
6815
+ 0: "bg-border",
6816
+ 1: "bg-status-error",
6817
+ 2: "bg-status-warning",
6818
+ 3: "bg-accent",
6819
+ 4: "bg-status-success"
6820
+ };
6821
+ var TEXT_COLOR = {
6822
+ 0: "text-foreground-muted",
6823
+ 1: "text-status-error",
6824
+ 2: "text-status-warning",
6825
+ 3: "text-accent",
6826
+ 4: "text-status-success"
6827
+ };
6828
+ var Tick = ({ ok }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", "aria-hidden": "true", className: cx("h-3.5 w-3.5 shrink-0", ok ? "text-status-success" : "text-foreground-muted"), children: ok ? /* @__PURE__ */ jsx("path", { fill: "currentColor", fillRule: "evenodd", clipRule: "evenodd", d: "M10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm3.7 6.3a1 1 0 0 0-1.4-1.4L9 10.18 7.7 8.88a1 1 0 0 0-1.4 1.42l2 2a1 1 0 0 0 1.4 0l4-4Z" }) : /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "2.5", fill: "currentColor" }) });
6829
+ function PasswordStrength({
6830
+ value,
6831
+ scorer = scorePassword,
6832
+ showRequirements = false,
6833
+ rules = defaultPasswordRules,
6834
+ confirmValue,
6835
+ hideMeter = false,
6836
+ className = "",
6837
+ style
6838
+ }) {
6839
+ const { score, label } = useMemo(() => scorer(value), [scorer, value]);
6840
+ const showMatch = confirmValue != null && (value.length > 0 || confirmValue.length > 0);
6841
+ const matches = value.length > 0 && value === confirmValue;
6842
+ return /* @__PURE__ */ jsxs("div", { className: cx("flex flex-col gap-2", className), style, "aria-live": "polite", children: [
6843
+ !hideMeter && /* @__PURE__ */ jsxs(Fragment, { children: [
6844
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1", role: "meter", "aria-valuemin": 0, "aria-valuemax": 4, "aria-valuenow": score, "aria-label": "Password strength", children: [1, 2, 3, 4].map((seg) => /* @__PURE__ */ jsx(
6845
+ "span",
6846
+ {
6847
+ className: cx("h-1.5 flex-1 rounded-full transition-colors duration-200", seg <= score ? BAR_COLOR[score] : "bg-border")
6848
+ },
6849
+ seg
6850
+ )) }),
6851
+ label && /* @__PURE__ */ jsxs("p", { className: "text-xs", children: [
6852
+ /* @__PURE__ */ jsx("span", { className: "text-foreground-muted", children: "Strength: " }),
6853
+ /* @__PURE__ */ jsx("span", { className: cx("font-medium", TEXT_COLOR[score]), children: label })
6854
+ ] })
6855
+ ] }),
6856
+ showRequirements && /* @__PURE__ */ jsx("ul", { className: "mt-0.5 flex flex-col gap-1", children: rules.map((rule, i) => {
6857
+ const ok = rule.test(value);
6858
+ return /* @__PURE__ */ jsxs("li", { className: cx("flex items-center gap-1.5 text-xs", ok ? "text-foreground-secondary" : "text-foreground-muted"), children: [
6859
+ /* @__PURE__ */ jsx(Tick, { ok }),
6860
+ rule.label
6861
+ ] }, i);
6862
+ }) }),
6863
+ showMatch && /* @__PURE__ */ jsxs("p", { className: cx("flex items-center gap-1.5 text-xs font-medium", matches ? "text-status-success" : "text-status-error"), children: [
6864
+ /* @__PURE__ */ jsx(Tick, { ok: matches }),
6865
+ matches ? "Passwords match" : "Passwords don\u2019t match"
6866
+ ] })
6867
+ ] });
6868
+ }
6869
+ var COLS = {
6870
+ 1: "grid-cols-1",
6871
+ 2: "grid-cols-1 sm:grid-cols-2",
6872
+ 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
6873
+ };
6874
+ var PAD2 = { sm: "p-3", md: "p-4", lg: "p-5" };
6875
+ var TITLE = { sm: "text-sm", md: "text-sm", lg: "text-base" };
6876
+ var CheckMark = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", className: "h-5 w-5 text-accent", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M12 2.25a9.75 9.75 0 1 0 0 19.5 9.75 9.75 0 0 0 0-19.5Zm4.28 7.53a.75.75 0 0 0-1.06-1.06l-4.97 4.97-1.97-1.97a.75.75 0 1 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l5.5-5.5Z" }) });
6877
+ function RadioTile({
6878
+ options,
6879
+ value,
6880
+ defaultValue,
6881
+ onChange,
6882
+ name,
6883
+ label,
6884
+ columns = 2,
6885
+ size = "md",
6886
+ disabled,
6887
+ required,
6888
+ helperText,
6889
+ errorMessage,
6890
+ className
6891
+ }) {
6892
+ const groupId = useId();
6893
+ const errorId = useId();
6894
+ const hasError = errorMessage != null;
6895
+ return /* @__PURE__ */ jsx(
6896
+ Field,
6897
+ {
6898
+ className,
6899
+ label,
6900
+ htmlFor: groupId,
6901
+ errorId,
6902
+ errorMessage,
6903
+ required,
6904
+ helperText,
6905
+ children: /* @__PURE__ */ jsx(
6906
+ RadioGroupPrimitive.Root,
6907
+ {
6908
+ id: groupId,
6909
+ name,
6910
+ value,
6911
+ defaultValue,
6912
+ onValueChange: onChange,
6913
+ disabled,
6914
+ required,
6915
+ "aria-invalid": hasError || void 0,
6916
+ "aria-describedby": hasError ? errorId : void 0,
6917
+ className: cx("grid gap-3", COLS[columns]),
6918
+ children: options.map((opt) => /* @__PURE__ */ jsxs(
6919
+ RadioGroupPrimitive.Item,
6920
+ {
6921
+ value: opt.value,
6922
+ disabled: opt.disabled,
6923
+ className: cx(
6924
+ "group relative flex flex-col gap-1 rounded-xl border bg-surface text-left transition-all duration-150",
6925
+ PAD2[size],
6926
+ "border-border hover:border-border-strong",
6927
+ "data-[state=checked]:border-accent data-[state=checked]:ring-1 data-[state=checked]:ring-accent",
6928
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
6929
+ "disabled:cursor-not-allowed disabled:opacity-50"
6930
+ ),
6931
+ children: [
6932
+ /* @__PURE__ */ jsx(RadioGroupPrimitive.Indicator, { className: "absolute right-3 top-3", children: CheckMark }),
6933
+ opt.icon != null && /* @__PURE__ */ jsx("span", { className: "mb-1 text-accent [&>svg]:h-6 [&>svg]:w-6", children: opt.icon }),
6934
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 pr-6", children: [
6935
+ /* @__PURE__ */ jsx("span", { className: cx("font-semibold text-foreground", TITLE[size]), children: opt.label }),
6936
+ opt.badge != null && /* @__PURE__ */ jsx("span", { className: "rounded-full border border-border bg-surface-raised px-2 py-0.5 text-[11px] font-medium text-foreground-secondary", children: opt.badge })
6937
+ ] }),
6938
+ opt.description != null && /* @__PURE__ */ jsx("span", { className: "text-xs leading-relaxed text-foreground-secondary", children: opt.description })
6939
+ ]
6940
+ },
6941
+ opt.value
6942
+ ))
6943
+ }
6944
+ )
6945
+ }
6946
+ );
6947
+ }
6715
6948
  function Checkbox({
6716
6949
  checked,
6717
6950
  value,
@@ -8607,7 +8840,7 @@ function OtpInput({
8607
8840
  emit(valid.join(""));
8608
8841
  focusBox(valid.length);
8609
8842
  };
8610
- return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, errorId, errorMessage, required, layout, helperText, children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", role: "group", "aria-label": typeof label === "string" ? label : "One-time code", children: chars.map((char, idx) => /* @__PURE__ */ jsxs(React28.Fragment, { children: [
8843
+ return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, errorId, errorMessage, required, layout, helperText, children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", role: "group", "aria-label": typeof label === "string" ? label : "One-time code", children: chars.map((char, idx) => /* @__PURE__ */ jsxs(React29.Fragment, { children: [
8611
8844
  /* @__PURE__ */ jsx(
8612
8845
  "input",
8613
8846
  {
@@ -9331,7 +9564,7 @@ function Jumbotron({
9331
9564
  }
9332
9565
  );
9333
9566
  }
9334
- var COLS = {
9567
+ var COLS2 = {
9335
9568
  2: "sm:grid-cols-2",
9336
9569
  3: "sm:grid-cols-2 lg:grid-cols-3",
9337
9570
  4: "sm:grid-cols-2 lg:grid-cols-4"
@@ -9353,7 +9586,7 @@ function FeatureGrid({
9353
9586
  title != null && /* @__PURE__ */ jsx("h2", { className: "text-3xl font-bold tracking-tight text-foreground", children: title }),
9354
9587
  description != null && /* @__PURE__ */ jsx("p", { className: "max-w-2xl text-base leading-relaxed text-foreground-secondary", children: description })
9355
9588
  ] }),
9356
- /* @__PURE__ */ jsx("div", { className: ["grid grid-cols-1 gap-6", COLS[columns]].join(" "), children: features.map((f, i) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 rounded-xl border border-border bg-surface p-5", children: [
9589
+ /* @__PURE__ */ jsx("div", { className: ["grid grid-cols-1 gap-6", COLS2[columns]].join(" "), children: features.map((f, i) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 rounded-xl border border-border bg-surface p-5", children: [
9357
9590
  f.icon != null && /* @__PURE__ */ jsx(
9358
9591
  "span",
9359
9592
  {
@@ -9410,7 +9643,7 @@ function PricingPlans({ plans, eyebrow, title, description, className = "", styl
9410
9643
  )) })
9411
9644
  ] });
9412
9645
  }
9413
- var COLS2 = {
9646
+ var COLS3 = {
9414
9647
  1: "mx-auto max-w-2xl",
9415
9648
  2: "sm:grid-cols-2",
9416
9649
  3: "sm:grid-cols-2 lg:grid-cols-3"
@@ -9427,7 +9660,7 @@ function Testimonials({ testimonials, eyebrow, title, description, columns = 3,
9427
9660
  title != null && /* @__PURE__ */ jsx("h2", { className: "text-3xl font-bold tracking-tight text-foreground", children: title }),
9428
9661
  description != null && /* @__PURE__ */ jsx("p", { className: "max-w-2xl text-base leading-relaxed text-foreground-secondary", children: description })
9429
9662
  ] }),
9430
- /* @__PURE__ */ jsx("div", { className: ["grid grid-cols-1 gap-6", COLS2[columns]].join(" "), children: testimonials.map((tm, i) => /* @__PURE__ */ jsxs("figure", { className: "flex flex-col gap-4 rounded-xl border border-border bg-surface p-6", children: [
9663
+ /* @__PURE__ */ jsx("div", { className: ["grid grid-cols-1 gap-6", COLS3[columns]].join(" "), children: testimonials.map((tm, i) => /* @__PURE__ */ jsxs("figure", { className: "flex flex-col gap-4 rounded-xl border border-border bg-surface p-6", children: [
9431
9664
  tm.rating != null && /* @__PURE__ */ jsx(Stars, { value: tm.rating }),
9432
9665
  /* @__PURE__ */ jsxs("blockquote", { className: "flex-1 text-sm leading-relaxed text-foreground", children: [
9433
9666
  "\u201C",
@@ -9631,7 +9864,7 @@ function Parallax({
9631
9864
  }
9632
9865
  );
9633
9866
  }
9634
- var COLS3 = {
9867
+ var COLS4 = {
9635
9868
  2: "sm:grid-cols-2",
9636
9869
  3: "sm:grid-cols-2 lg:grid-cols-3"
9637
9870
  };
@@ -9652,7 +9885,7 @@ function Blog({
9652
9885
  title != null && /* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold tracking-tight text-foreground sm:text-3xl", children: title }),
9653
9886
  description != null && /* @__PURE__ */ jsx("p", { className: "max-w-2xl text-base leading-relaxed text-foreground-secondary", children: description })
9654
9887
  ] }),
9655
- /* @__PURE__ */ jsx("div", { className: ["grid grid-cols-1 gap-6", COLS3[columns]].join(" "), children: posts.map((post, i) => {
9888
+ /* @__PURE__ */ jsx("div", { className: ["grid grid-cols-1 gap-6", COLS4[columns]].join(" "), children: posts.map((post, i) => {
9656
9889
  const meta = [post.author, post.date, post.readTime].filter((m) => m != null);
9657
9890
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
9658
9891
  post.image && /* @__PURE__ */ jsxs("div", { className: "relative aspect-video overflow-hidden bg-backdrop", children: [
@@ -9663,7 +9896,7 @@ function Blog({
9663
9896
  post.tag != null && !post.image && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { tone: "accent", variant: "soft", size: "sm", children: post.tag }) }),
9664
9897
  /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold leading-snug text-foreground transition-colors group-hover:text-accent", children: post.title }),
9665
9898
  post.excerpt != null && /* @__PURE__ */ jsx("p", { className: "line-clamp-3 text-sm leading-relaxed text-foreground-secondary", children: post.excerpt }),
9666
- meta.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-auto flex flex-wrap items-center gap-x-2 gap-y-1 pt-3 text-xs text-foreground-muted", children: meta.map((m, j) => /* @__PURE__ */ jsxs(React28.Fragment, { children: [
9899
+ meta.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-auto flex flex-wrap items-center gap-x-2 gap-y-1 pt-3 text-xs text-foreground-muted", children: meta.map((m, j) => /* @__PURE__ */ jsxs(React29.Fragment, { children: [
9667
9900
  j > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
9668
9901
  /* @__PURE__ */ jsx("span", { children: m })
9669
9902
  ] }, j)) })
@@ -9836,6 +10069,6 @@ function LeadCapture({
9836
10069
  );
9837
10070
  }
9838
10071
 
9839
- export { Accordion_default as Accordion, AppShell, AutoComplete, Avatar, Badge, Blog, Box, Breadcrumbs, Button_default as Button, CARD_BRANDS, Card_default as Card, CardCarousel, Cart, CartButton, CartProvider, Catalog, CatalogCarousel, CatalogGrid, Chat, Checkbox, Checkout, ColorPicker, ContextMenu, CookieConsent, CreditCardForm, DateRangePicker, Drawer, Dropdown, EmptyCart, FAB, FadingBase, FeatureGrid, Field, FieldHelpIcon, FieldLabel, FileInput, Flex, Form, FormContext, FormField, FormStore, Grid, GridCard, IconButton, Jumbotron, Kbd, LeadCapture, List2 as List, LoadingSpinner, LogoutTimer, MegaMenu_default as MegaMenu, MenuButton, Modal, NotificationProvider, NumberInput, OpaqueGridCard, OtpInput, Parallax, Password, PopConfirm, Portal, PricingPlans, RadioGroup, Rating, ScalableContainer, Scheduler, SearchInput_default as SearchInput, SecureLayout, SegmentedControl, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, SlideShow, Slider, Socials, Statistic, Stepper, Switch, Table, Tabs_default as Tabs, TagsInput, DatePicker as Temporal, Testimonials, TextArea, TextInput, ThemeProvider, ThemeSwitch, TimePicker, Timeline, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Video, Wizard, cardNumberError, cvvError, cx, detectBrand, expiryError, fieldShell, formatCardNumber, formatExpiry, isRequired, luhnValid, onlyDigits, patterns, runFieldRules, useBreakpoint, useCart, useFieldArray, useForm, useFormField, useFormStore, useJwt, useLocalStorage, useMediaQuery, useNotification };
10072
+ export { Accordion_default as Accordion, AppShell, AutoComplete, Avatar, Badge, Blog, Box, Breadcrumbs, Button_default as Button, CARD_BRANDS, Card_default as Card, CardCarousel, Cart, CartButton, CartProvider, Catalog, CatalogCarousel, CatalogGrid, Chat, Checkbox, Checkout, ColorPicker, ContextMenu, CookieConsent, CreditCardForm, DateRangePicker, Drawer, Dropdown, EmptyCart, FAB, FadingBase, FeatureGrid, Field, FieldHelpIcon, FieldLabel, FileInput, Flex, Form, FormContext, FormField, FormStore, Grid, GridCard, IconButton, Jumbotron, Kbd, LeadCapture, List2 as List, LoadingSpinner, LogoutTimer, MegaMenu_default as MegaMenu, MenuButton, Modal, NotificationProvider, NumberInput, OpaqueGridCard, OtpInput, Parallax, Password, PasswordStrength, PopConfirm, Portal, PricingPlans, RadioGroup, RadioTile, Rating, ScalableContainer, Scheduler, SearchInput_default as SearchInput, SecureLayout, SegmentedControl, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, SlideShow, Slider, Socials, Statistic, Stepper, Switch, Table, Tabs_default as Tabs, TagsInput, DatePicker as Temporal, Testimonials, TextArea, TextInput, ThemeProvider, ThemeSwitch, TimePicker, Timeline, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Video, VirtualList, Wizard, cardNumberError, cvvError, cx, defaultPasswordRules, detectBrand, expiryError, fieldShell, formatCardNumber, formatExpiry, isRequired, luhnValid, onlyDigits, patterns, runFieldRules, scorePassword, useBreakpoint, useCart, useFieldArray, useForm, useFormField, useFormStore, useJwt, useLocalStorage, useMediaQuery, useNotification };
9840
10073
  //# sourceMappingURL=index.js.map
9841
10074
  //# sourceMappingURL=index.js.map