@orangecheck/app-design 0.2.0 → 0.3.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
@@ -134,7 +134,26 @@ function Spinner({ className, label }) {
134
134
  );
135
135
  }
136
136
  function Skeleton({ className, ...props }) {
137
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-ui": "skeleton", className: cn("animate-pulse rounded-lg bg-muted", className), ...props });
137
+ return /* @__PURE__ */ jsxRuntime.jsx(
138
+ "div",
139
+ {
140
+ "data-ui": "skeleton",
141
+ className: cn("animate-pulse rounded-lg bg-[var(--oca-skeleton)]", className),
142
+ ...props
143
+ }
144
+ );
145
+ }
146
+ function useTimedOut(active, ms = 12e3) {
147
+ const [timedOut, setTimedOut] = react.useState(false);
148
+ react.useEffect(() => {
149
+ if (!active) {
150
+ setTimedOut(false);
151
+ return;
152
+ }
153
+ const t = setTimeout(() => setTimedOut(true), ms);
154
+ return () => clearTimeout(t);
155
+ }, [active, ms]);
156
+ return timedOut;
138
157
  }
139
158
  var Separator = react.forwardRef(function Separator2({ className, orientation = "horizontal", decorative = true, ...props }, ref) {
140
159
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -207,7 +226,10 @@ var buttonVariants = classVarianceAuthority.cva(
207
226
  "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-full font-medium",
208
227
  "transition-[background-color,border-color,color,box-shadow,transform] duration-150",
209
228
  "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
210
- "active:translate-y-px disabled:pointer-events-none disabled:opacity-45",
229
+ "active:translate-y-px disabled:pointer-events-none",
230
+ // Disabled is one look for every variant: a quiet grey pill, never a
231
+ // faded black or orange slab that reads as broken.
232
+ "disabled:border disabled:border-border disabled:bg-muted disabled:text-oca-subtle disabled:shadow-none",
211
233
  "[&_svg]:pointer-events-none [&_svg]:size-[1.1em] [&_svg]:shrink-0"
212
234
  ],
213
235
  {
@@ -253,6 +275,7 @@ var Button = react.forwardRef(function Button2({ className, variant, size, asChi
253
275
  }
254
276
  );
255
277
  });
278
+ var GOOD_DOT = "border-border bg-card text-foreground before:size-1.5 before:shrink-0 before:rounded-full before:bg-success before:content-['']";
256
279
  var badgeVariants = classVarianceAuthority.cva(
257
280
  "inline-flex h-6 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full border px-2.5 text-[0.8125rem] font-medium leading-none",
258
281
  {
@@ -261,10 +284,13 @@ var badgeVariants = classVarianceAuthority.cva(
261
284
  neutral: "border-border bg-card text-foreground",
262
285
  muted: "border-transparent bg-muted text-muted-foreground",
263
286
  brand: "border-transparent bg-[color-mix(in_oklab,var(--oca-orange)_12%,var(--card))] text-oca-accent-ink",
264
- success: "border-transparent bg-[color-mix(in_oklab,var(--success)_12%,var(--card))] text-success",
265
- warning: "border-transparent bg-[color-mix(in_oklab,var(--warning)_14%,var(--card))] text-warning",
266
287
  danger: "border-transparent bg-[color-mix(in_oklab,var(--destructive)_12%,var(--card))] text-destructive",
267
- info: "border-transparent bg-[color-mix(in_oklab,var(--info)_12%,var(--card))] text-info",
288
+ // A good state is a neutral pill with a green dot, not a green pill.
289
+ success: GOOD_DOT,
290
+ // Aliases kept so older call sites keep compiling: attention is
291
+ // the brand accent, information is neutral.
292
+ warning: "border-transparent bg-[color-mix(in_oklab,var(--oca-orange)_12%,var(--card))] text-oca-accent-ink",
293
+ info: "border-border bg-card text-foreground",
268
294
  ink: "border-transparent bg-oca-ink text-oca-ink-foreground",
269
295
  onInk: "border-white/15 bg-white/5 text-white/90"
270
296
  }
@@ -375,7 +401,7 @@ var Label = react.forwardRef(function Label2({ className, ...props }, ref) {
375
401
  }
376
402
  );
377
403
  });
378
- function Field({ label, hint, error, children, className, hideLabel }) {
404
+ function Field({ label, hint, optional, error, children, className, hideLabel }) {
379
405
  const id = react.useId();
380
406
  const hintId = hint ? `${id}-hint` : void 0;
381
407
  const errorId = error ? `${id}-error` : void 0;
@@ -386,9 +412,12 @@ function Field({ label, hint, error, children, className, hideLabel }) {
386
412
  "aria-invalid": error ? true : void 0
387
413
  }) : children;
388
414
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-2", className), children: [
389
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: id, className: hideLabel ? "sr-only" : void 0, children: label }),
390
- hint ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: hintId, className: "text-[0.8125rem] leading-snug text-muted-foreground", children: hint }) : null,
415
+ /* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: id, className: hideLabel ? "sr-only" : "flex items-baseline gap-2", children: [
416
+ label,
417
+ optional ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.8125rem] font-normal text-muted-foreground", children: "Optional" }) : null
418
+ ] }),
391
419
  control,
420
+ hint ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: hintId, className: "text-[0.8125rem] leading-snug text-muted-foreground", children: hint }) : null,
392
421
  error ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, role: "alert", className: "text-[0.8125rem] leading-snug text-destructive", children: error }) : null
393
422
  ] });
394
423
  }
@@ -665,23 +694,68 @@ var DropdownMenuLabel = react.forwardRef(function DropdownMenuLabel2({ className
665
694
  var DropdownMenuSeparator = react.forwardRef(function DropdownMenuSeparator2({ className, ...props }, ref) {
666
695
  return /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive__namespace.Separator, { ref, className: cn("my-1.5 h-px bg-border", className), ...props });
667
696
  });
697
+ function ScrollFade({ fade = "var(--card)", className, innerClassName, children, ...props }) {
698
+ const ref = react.useRef(null);
699
+ const [edges, setEdges] = react.useState({ left: false, right: false });
700
+ const measure = react.useCallback(() => {
701
+ const el = ref.current;
702
+ if (!el) return;
703
+ const left = el.scrollLeft > 1;
704
+ const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
705
+ setEdges((e) => e.left === left && e.right === right ? e : { left, right });
706
+ }, []);
707
+ react.useEffect(() => {
708
+ const el = ref.current;
709
+ if (!el) return;
710
+ measure();
711
+ const ro = new ResizeObserver(measure);
712
+ ro.observe(el);
713
+ if (el.firstElementChild) ro.observe(el.firstElementChild);
714
+ return () => ro.disconnect();
715
+ }, [measure]);
716
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative min-w-0", className), ...props, children: [
717
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref, onScroll: measure, className: cn("overflow-x-auto overscroll-x-contain", innerClassName), children }),
718
+ /* @__PURE__ */ jsxRuntime.jsx(
719
+ "span",
720
+ {
721
+ "aria-hidden": true,
722
+ className: cn(
723
+ "pointer-events-none absolute inset-y-0 left-0 w-8 transition-opacity duration-150",
724
+ edges.left ? "opacity-100" : "opacity-0"
725
+ ),
726
+ style: { background: `linear-gradient(to right, ${fade}, transparent)` }
727
+ }
728
+ ),
729
+ /* @__PURE__ */ jsxRuntime.jsx(
730
+ "span",
731
+ {
732
+ "aria-hidden": true,
733
+ className: cn(
734
+ "pointer-events-none absolute inset-y-0 right-0 w-10 transition-opacity duration-150",
735
+ edges.right ? "opacity-100" : "opacity-0"
736
+ ),
737
+ style: { background: `linear-gradient(to left, ${fade}, transparent)` }
738
+ }
739
+ )
740
+ ] });
741
+ }
668
742
  var LookContext = react.createContext("pill");
669
743
  var Tabs = react.forwardRef(function Tabs2({ look = "pill", ...props }, ref) {
670
744
  return /* @__PURE__ */ jsxRuntime.jsx(LookContext.Provider, { value: look, children: /* @__PURE__ */ jsxRuntime.jsx(TabsPrimitive__namespace.Root, { ref, ...props }) });
671
745
  });
672
746
  var TabsList = react.forwardRef(function TabsList2({ className, ...props }, ref) {
673
747
  const look = react.useContext(LookContext);
674
- return /* @__PURE__ */ jsxRuntime.jsx(
748
+ return /* @__PURE__ */ jsxRuntime.jsx(ScrollFade, { fade: "var(--background)", className: look === "pill" ? "w-fit max-w-full" : "w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
675
749
  TabsPrimitive__namespace.List,
676
750
  {
677
751
  ref,
678
752
  className: cn(
679
- look === "pill" ? "inline-flex max-w-full items-center gap-1 overflow-x-auto rounded-full bg-muted p-1" : "flex max-w-full items-center gap-6 overflow-x-auto border-b border-border",
753
+ look === "pill" ? "inline-flex w-max items-center gap-1 rounded-full bg-muted p-1" : "flex w-max min-w-full items-center gap-6 border-b border-border",
680
754
  className
681
755
  ),
682
756
  ...props
683
757
  }
684
- );
758
+ ) });
685
759
  });
686
760
  var TabsTrigger = react.forwardRef(function TabsTrigger2({ className, ...props }, ref) {
687
761
  const look = react.useContext(LookContext);
@@ -887,7 +961,7 @@ function Prose({ className, ...props }) {
887
961
  {
888
962
  className: cn(
889
963
  "max-w-[42rem] text-[1.0625rem] leading-[1.7] text-muted-foreground",
890
- "[&_h2]:oca-display-3 [&_h2]:mb-4 [&_h2]:mt-14 [&_h2]:text-foreground",
964
+ "[&_h2]:oca-display-3 [&_h2]:mb-4 [&_h2]:mt-14 [&_h2]:scroll-mt-24 [&_h2]:text-foreground",
891
965
  "[&_h3]:oca-title [&_h3]:mb-3 [&_h3]:mt-10 [&_h3]:text-foreground",
892
966
  "[&_p]:my-4 [&_strong]:font-medium [&_strong]:text-foreground",
893
967
  "[&_a]:text-foreground [&_a]:underline [&_a]:decoration-border [&_a]:underline-offset-4 hover:[&_a]:decoration-foreground",
@@ -903,7 +977,9 @@ var sectionVariants = classVarianceAuthority.cva("relative w-full overflow-x-cli
903
977
  tone: {
904
978
  canvas: "bg-background text-foreground",
905
979
  card: "bg-card text-card-foreground",
906
- ink: "bg-oca-ink text-oca-ink-foreground",
980
+ // The band line is transparent in light; in dark it keeps the ink
981
+ // band from melting into the canvas.
982
+ ink: "border-y border-[var(--oca-band-line)] bg-oca-ink text-oca-ink-foreground",
907
983
  brand: "bg-brand text-brand-foreground"
908
984
  },
909
985
  space: {
@@ -938,6 +1014,120 @@ function SectionIntro({
938
1014
  }
939
1015
  );
940
1016
  }
1017
+ function PageHero({
1018
+ eyebrow,
1019
+ title,
1020
+ lead,
1021
+ actions,
1022
+ art,
1023
+ meta,
1024
+ children,
1025
+ variant = "page",
1026
+ size = "lg",
1027
+ className
1028
+ }) {
1029
+ if (variant === "document") {
1030
+ return /* @__PURE__ */ jsxRuntime.jsx(Section, { space: "none", className: cn("pb-12 pt-12 md:pb-16 md:pt-20", className), children: /* @__PURE__ */ jsxRuntime.jsx(DocHeader, { className: "mx-auto max-w-[42rem]", ...{ eyebrow, title, lead, meta, actions }, children }) });
1031
+ }
1032
+ return /* @__PURE__ */ jsxRuntime.jsx(Section, { space: "none", className: cn("pb-14 pt-12 md:pb-20 md:pt-20", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("grid grid-cols-1 gap-10", art && "items-center md:grid-cols-12"), children: [
1033
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col items-start gap-6", art ? "md:col-span-7" : "max-w-[52rem]"), children: [
1034
+ eyebrow ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-eyebrow", children: eyebrow }) : null,
1035
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: size === "xl" ? "oca-display-1" : "oca-display-2", children: title }),
1036
+ lead ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-lead max-w-[38rem]", children: lead }) : null,
1037
+ meta ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.875rem] text-muted-foreground", children: meta }) : null,
1038
+ actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-3", children: actions }) : null,
1039
+ children
1040
+ ] }),
1041
+ art ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "md:col-span-5", children: art }) : null
1042
+ ] }) });
1043
+ }
1044
+ function DocHeader({
1045
+ eyebrow,
1046
+ title,
1047
+ lead,
1048
+ meta,
1049
+ actions,
1050
+ children,
1051
+ className
1052
+ }) {
1053
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-5", className), children: [
1054
+ eyebrow ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-eyebrow", children: eyebrow }) : null,
1055
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "oca-display-2", children: title }),
1056
+ lead ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-lead", children: lead }) : null,
1057
+ meta ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.875rem] text-muted-foreground", children: meta }) : null,
1058
+ actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 flex flex-wrap gap-3", children: actions }) : null,
1059
+ children
1060
+ ] });
1061
+ }
1062
+ function useActiveHeading(ids) {
1063
+ const [active, setActive] = react.useState(ids[0]);
1064
+ const key = ids.join("|");
1065
+ react.useEffect(() => {
1066
+ const els = key.split("|").map((id) => document.getElementById(id)).filter((el) => el !== null);
1067
+ if (els.length === 0) return;
1068
+ const io = new IntersectionObserver(
1069
+ (entries) => {
1070
+ const hit = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];
1071
+ if (hit) setActive(hit.target.id);
1072
+ },
1073
+ { rootMargin: "-80px 0px -65% 0px" }
1074
+ );
1075
+ els.forEach((el) => io.observe(el));
1076
+ return () => io.disconnect();
1077
+ }, [key]);
1078
+ return active;
1079
+ }
1080
+ function DocLayout({
1081
+ toc,
1082
+ header,
1083
+ children,
1084
+ className
1085
+ }) {
1086
+ const active = useActiveHeading(toc?.map((t) => t.id) ?? []);
1087
+ const links = () => toc?.map((t) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
1088
+ "a",
1089
+ {
1090
+ href: `#${t.id}`,
1091
+ "aria-current": active === t.id ? "location" : void 0,
1092
+ className: cn(
1093
+ "block border-l py-1.5 pl-4 text-[0.875rem] leading-snug transition-colors",
1094
+ active === t.id ? "border-foreground text-foreground" : "border-border text-muted-foreground hover:text-foreground"
1095
+ ),
1096
+ children: t.label
1097
+ }
1098
+ ) }, t.id));
1099
+ const hasToc = !!toc && toc.length > 0;
1100
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("oca-container pb-24", header ? "pt-12 md:pt-20" : null, className), children: /* @__PURE__ */ jsxRuntime.jsxs(
1101
+ "div",
1102
+ {
1103
+ className: cn(
1104
+ "mx-auto grid grid-cols-1 gap-8",
1105
+ hasToc ? "max-w-[42rem] lg:max-w-[64rem] lg:grid-cols-[13rem_minmax(0,42rem)] lg:gap-x-16 lg:gap-y-10" : "max-w-[42rem]"
1106
+ ),
1107
+ children: [
1108
+ header ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(hasToc && "lg:col-start-2", "pb-4"), children: header }) : null,
1109
+ hasToc ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1110
+ /* @__PURE__ */ jsxRuntime.jsxs(
1111
+ "nav",
1112
+ {
1113
+ "aria-label": "On this page",
1114
+ className: cn("sticky top-24 hidden self-start lg:block", header && "lg:row-start-2"),
1115
+ children: [
1116
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-3 text-[0.875rem] font-medium", children: "On this page" }),
1117
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { children: links() })
1118
+ ]
1119
+ }
1120
+ ),
1121
+ /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group rounded-2xl border border-border bg-card px-4 py-3 lg:hidden", children: [
1122
+ /* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer list-none text-[0.9375rem] font-medium marker:hidden", children: "On this page" }),
1123
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-3", children: links() })
1124
+ ] })
1125
+ ] }) : null,
1126
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("min-w-0", hasToc && "lg:col-start-2", hasToc && header && "lg:row-start-2"), children })
1127
+ ]
1128
+ }
1129
+ ) });
1130
+ }
941
1131
  var pad = (n) => String(n + 1).padStart(2, "0");
942
1132
  function StepGrid({ items, look = "cards", className }) {
943
1133
  const cols = items.length >= 3 ? "md:grid-cols-3" : items.length === 2 ? "md:grid-cols-2" : "";
@@ -965,183 +1155,9 @@ function StepGrid({ items, look = "cards", className }) {
965
1155
  ] }) }, i)
966
1156
  ) });
967
1157
  }
968
- var KIND = {
969
- package: "text-oca-code-package",
970
- tag: "text-oca-code-tag",
971
- link: "text-oca-code-link"
972
- };
973
- function PackageCard({ items, className }) {
974
- return /* @__PURE__ */ jsxRuntime.jsx(
975
- "div",
976
- {
977
- className: cn(
978
- "grid gap-x-10 gap-y-7 rounded-[1.25rem] border border-oca-ink-line bg-oca-ink-card p-6 text-oca-ink-foreground md:grid-cols-2 md:p-9",
979
- className
980
- ),
981
- children: items.map((it, i) => {
982
- const code = /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[0.9375rem]", KIND[it.kind ?? "package"]), children: it.code });
983
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
984
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[1.125rem] font-semibold tracking-[-0.01em] text-oca-ink-title", children: it.title }),
985
- it.href ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: it.href, className: "w-fit underline decoration-transparent underline-offset-4 hover:decoration-current", children: code }) : code,
986
- it.note ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[0.9375rem] leading-relaxed text-oca-ink-subtle", children: [
987
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "// " }),
988
- it.note
989
- ] }) : null
990
- ] }, i);
991
- })
992
- }
993
- );
994
- }
995
- function CodeBlock({
996
- code,
997
- caption,
998
- className
999
- }) {
1000
- return /* @__PURE__ */ jsxRuntime.jsxs("figure", { className: cn("overflow-hidden rounded-[1.25rem] border border-oca-ink-line bg-oca-ink-card", className), children: [
1001
- caption ? /* @__PURE__ */ jsxRuntime.jsx("figcaption", { className: "border-b border-oca-ink-line px-5 py-3 text-[0.8125rem] text-oca-ink-subtle", children: caption }) : null,
1002
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto p-5 font-mono text-[0.875rem] leading-relaxed text-oca-ink-muted", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) })
1003
- ] });
1004
- }
1005
- function SplitBar({
1006
- segments,
1007
- caption,
1008
- className
1009
- }) {
1010
- return /* @__PURE__ */ jsxRuntime.jsxs("figure", { className: cn("flex flex-col gap-5", className), children: [
1011
- caption ? /* @__PURE__ */ jsxRuntime.jsx("figcaption", { className: "text-[0.9375rem]", children: caption }) : null,
1012
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-16 w-full gap-1.5 overflow-hidden rounded-[1.25rem] md:h-20", children: segments.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
1013
- "div",
1014
- {
1015
- className: cn(
1016
- "flex min-w-0 items-end rounded-[1rem] px-4 pb-3",
1017
- s.tone === "card" ? "bg-white text-oca-ink" : "bg-oca-ink text-white"
1018
- ),
1019
- style: { flexBasis: `${s.pct}%` },
1020
- children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "oca-tabular text-[1.5rem] leading-none tracking-[-0.02em] md:text-[1.875rem]", children: [
1021
- s.pct,
1022
- "%"
1023
- ] })
1024
- },
1025
- i
1026
- )) }),
1027
- /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "grid gap-x-8 gap-y-2 sm:grid-cols-2", children: segments.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2.5", children: [
1028
- /* @__PURE__ */ jsxRuntime.jsxs("dt", { className: "sr-only", children: [
1029
- s.pct,
1030
- "%"
1031
- ] }),
1032
- /* @__PURE__ */ jsxRuntime.jsx(
1033
- "span",
1034
- {
1035
- "aria-hidden": true,
1036
- className: cn(
1037
- "inline-block size-2.5 shrink-0 translate-y-[-1px] rounded-full",
1038
- s.tone === "card" ? "bg-white" : "bg-oca-ink"
1039
- )
1040
- }
1041
- ),
1042
- /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "text-[0.9375rem] leading-snug", children: s.label })
1043
- ] }, i)) })
1044
- ] });
1045
- }
1046
- var LABEL = {
1047
- yes: "Yes",
1048
- testing: "In testing",
1049
- "not-yet": "Not yet",
1050
- no: "No"
1051
- };
1052
- var TONE = {
1053
- yes: "text-oca-accent-ink",
1054
- testing: "text-foreground",
1055
- "not-yet": "text-muted-foreground",
1056
- no: "text-muted-foreground"
1057
- };
1058
- function TruthTable({
1059
- rows,
1060
- statusHeading = "Status",
1061
- whatHeading = "What",
1062
- className
1063
- }) {
1064
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("overflow-hidden rounded-xl border border-oca-hairline-strong", className), children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem] md:text-[1rem]", children: [
1065
- /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-oca-table-head", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1066
- /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "w-[30%] border-b border-oca-hairline-strong px-4 py-3 font-semibold md:w-[22%]", children: statusHeading }),
1067
- /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "border-b border-l border-oca-hairline-strong px-4 py-3 font-semibold", children: whatHeading })
1068
- ] }) }),
1069
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-card", children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "align-top", children: [
1070
- /* @__PURE__ */ jsxRuntime.jsx(
1071
- "td",
1072
- {
1073
- className: cn(
1074
- "border-t border-oca-hairline-strong px-4 py-3 font-medium",
1075
- i === 0 && "border-t-0",
1076
- TONE[r.status]
1077
- ),
1078
- children: LABEL[r.status]
1079
- }
1080
- ),
1081
- /* @__PURE__ */ jsxRuntime.jsx(
1082
- "td",
1083
- {
1084
- className: cn(
1085
- "border-l border-t border-oca-hairline-strong px-4 py-3 leading-relaxed",
1086
- i === 0 && "border-t-0"
1087
- ),
1088
- children: r.what
1089
- }
1090
- )
1091
- ] }, i)) })
1092
- ] }) });
1093
- }
1094
- var INT = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
1095
- var COMPACT = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
1096
- function satsText(value, opts = {}) {
1097
- if (value == null || !Number.isFinite(value)) return null;
1098
- const whole = Math.trunc(value);
1099
- const body = (opts.compact ? COMPACT : INT).format(Math.abs(whole));
1100
- const sign = whole < 0 ? "\u2212" : opts.signed && whole > 0 ? "+" : "";
1101
- const unit = opts.unit === "none" ? "" : whole === 1 || whole === -1 ? " sat" : " sats";
1102
- return `${sign}${body}${unit}`;
1103
- }
1104
- function Sats({ value, unit, compact, signed, unknown = "\u2014", className, ...props }) {
1105
- const text = satsText(value, { unit, compact, signed });
1106
- if (text === null) {
1107
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), ...props, children: [
1108
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: unknown }),
1109
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "unknown" })
1110
- ] });
1111
- }
1112
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("oca-tabular whitespace-nowrap", className), ...props, children: text });
1113
- }
1114
- function UsdApprox({
1115
- sats,
1116
- usdPerBtc,
1117
- className
1118
- }) {
1119
- if (sats == null || usdPerBtc == null || !Number.isFinite(sats) || !Number.isFinite(usdPerBtc)) return null;
1120
- const usd = sats / 1e8 * usdPerBtc;
1121
- const text = new Intl.NumberFormat("en-US", {
1122
- style: "currency",
1123
- currency: "USD",
1124
- maximumFractionDigits: usd < 1 ? 4 : 2
1125
- }).format(usd);
1126
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), children: [
1127
- "\u2248 ",
1128
- text
1129
- ] });
1130
- }
1131
- function SatsFigure({
1132
- label,
1133
- value,
1134
- hint,
1135
- className
1136
- }) {
1137
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
1138
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.9375rem] text-muted-foreground", children: label }),
1139
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "oca-display-3 oca-tabular", children: [
1140
- /* @__PURE__ */ jsxRuntime.jsx(Sats, { value, unit: "none" }),
1141
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 text-[0.5em] tracking-normal text-muted-foreground", children: "sats" })
1142
- ] }),
1143
- hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.875rem] text-muted-foreground", children: hint }) : null
1144
- ] });
1158
+ var ShellContext = react.createContext({});
1159
+ function useShellPlace() {
1160
+ return react.useContext(ShellContext);
1145
1161
  }
1146
1162
  function PageHeader({
1147
1163
  eyebrow,
@@ -1150,29 +1166,117 @@ function PageHeader({
1150
1166
  actions,
1151
1167
  className
1152
1168
  }) {
1169
+ const place = useShellPlace();
1170
+ const eb = eyebrow === void 0 ? place.section : eyebrow;
1153
1171
  return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: cn("flex flex-col gap-5 md:flex-row md:items-end md:justify-between", className), children: [
1154
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-w-[44rem] flex-col gap-3", children: [
1155
- eyebrow ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-eyebrow", children: eyebrow }) : null,
1172
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 max-w-[44rem] flex-col gap-2.5", children: [
1173
+ eb ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-eyebrow", children: eb }) : null,
1156
1174
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "oca-display-3", children: title }),
1157
- description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-body text-[1.0625rem]", children: description }) : null
1175
+ description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-body max-w-[40rem] text-[1.0625rem]", children: description }) : null
1158
1176
  ] }),
1159
- actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 flex-wrap items-center gap-3", children: actions }) : null
1177
+ actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2.5 md:shrink-0 md:justify-end", children: actions }) : null
1160
1178
  ] });
1161
1179
  }
1162
- function StatCard({
1163
- label,
1164
- value,
1165
- hint,
1166
- className
1167
- }) {
1168
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-2 rounded-[1.25rem] border border-border bg-card p-5 md:p-6", className), children: [
1169
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.875rem] text-muted-foreground", children: label }),
1170
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oca-tabular text-[1.75rem] leading-tight tracking-[-0.02em]", children: value }),
1171
- hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.8125rem] leading-snug text-muted-foreground", children: hint }) : null
1172
- ] });
1180
+ var InStatGrid = react.createContext(false);
1181
+ function StatCard({ label, value, hint, className }) {
1182
+ const inGrid = react.useContext(InStatGrid);
1183
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1184
+ "div",
1185
+ {
1186
+ "data-ui": "stat",
1187
+ className: cn(
1188
+ "rounded-[1.25rem] border border-border bg-card p-5 md:p-6",
1189
+ // In a grid, label, value and hint each share a row with their
1190
+ // neighbours, so values line up whatever wraps above them.
1191
+ inGrid ? "row-span-3 grid grid-rows-subgrid gap-1.5" : "flex flex-col gap-2",
1192
+ className
1193
+ ),
1194
+ children: [
1195
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.875rem] leading-snug text-muted-foreground", children: label }),
1196
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "self-end break-words oca-tabular text-[1.5rem] leading-tight tracking-[-0.02em] md:text-[1.75rem]", children: value }),
1197
+ hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.8125rem] leading-snug text-muted-foreground", children: hint }) : inGrid ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true }) : null
1198
+ ]
1199
+ }
1200
+ );
1201
+ }
1202
+ var MD_COLS = {
1203
+ 1: "md:grid-cols-1",
1204
+ 2: "md:grid-cols-2",
1205
+ 3: "md:grid-cols-3",
1206
+ 4: "md:grid-cols-4"
1207
+ };
1208
+ var LG_COLS = {
1209
+ 1: "lg:grid-cols-1",
1210
+ 2: "lg:grid-cols-2",
1211
+ 3: "lg:grid-cols-3",
1212
+ 4: "lg:grid-cols-4",
1213
+ 5: "lg:grid-cols-5"
1214
+ };
1215
+ var MD_SPAN = {
1216
+ 1: "md:col-span-1",
1217
+ 2: "md:col-span-2",
1218
+ 3: "md:col-span-3",
1219
+ 4: "md:col-span-4"
1220
+ };
1221
+ var LG_SPAN = {
1222
+ 1: "lg:col-span-1",
1223
+ 2: "lg:col-span-2",
1224
+ 3: "lg:col-span-3",
1225
+ 4: "lg:col-span-4"
1226
+ };
1227
+ function statColumns(n) {
1228
+ if (n <= 5) return Math.max(1, n);
1229
+ if (n % 4 === 0) return 4;
1230
+ if (n % 3 === 0) return 3;
1231
+ return 4;
1232
+ }
1233
+ function lastSpan(n, cols) {
1234
+ const rest = n % cols;
1235
+ return rest === 0 ? 1 : cols - rest + 1;
1236
+ }
1237
+ function EmptyLine({ children, className }) {
1238
+ return /* @__PURE__ */ jsxRuntime.jsx(
1239
+ "p",
1240
+ {
1241
+ className: cn(
1242
+ "rounded-[1.25rem] border border-dashed border-oca-hairline-strong px-5 py-4 text-[0.9375rem] text-muted-foreground",
1243
+ className
1244
+ ),
1245
+ children
1246
+ }
1247
+ );
1173
1248
  }
1174
- function StatGrid({ className, ...props }) {
1175
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("grid grid-cols-2 gap-3 md:grid-cols-4 md:gap-4", className), ...props });
1249
+ function StatGrid({ columns, empty, isEmpty, className, children, ...props }) {
1250
+ const items = react.Children.toArray(children).filter(react.isValidElement);
1251
+ const n = items.length;
1252
+ const allZero = n > 0 && items.every((el) => "raw" in el.props && (el.props.raw === 0 || el.props.raw === null));
1253
+ if (isEmpty ?? allZero) return /* @__PURE__ */ jsxRuntime.jsx(EmptyLine, { className, children: empty ?? "Nothing here yet." });
1254
+ const lg = columns ?? statColumns(n);
1255
+ const md = lg > 4 ? 3 : lg;
1256
+ const mdSpan = lastSpan(n, md);
1257
+ const lgSpan = lastSpan(n, lg);
1258
+ return /* @__PURE__ */ jsxRuntime.jsx(InStatGrid.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1259
+ "div",
1260
+ {
1261
+ className: cn(
1262
+ "grid gap-3 md:gap-4",
1263
+ n === 1 ? "grid-cols-1" : "grid-cols-2",
1264
+ MD_COLS[md],
1265
+ LG_COLS[lg],
1266
+ className
1267
+ ),
1268
+ ...props,
1269
+ children: items.map((el, i) => {
1270
+ if (i !== n - 1) return el;
1271
+ const span = cn(
1272
+ n % 2 === 1 && n > 1 && "max-md:col-span-2",
1273
+ (mdSpan > 1 || lgSpan > 1) && MD_SPAN[mdSpan],
1274
+ (mdSpan > 1 || lgSpan > 1) && LG_SPAN[lgSpan]
1275
+ );
1276
+ return span ? react.cloneElement(el, { className: cn(el.props.className, span) }) : el;
1277
+ })
1278
+ }
1279
+ ) });
1176
1280
  }
1177
1281
  function KeyValueList({ items, className }) {
1178
1282
  return /* @__PURE__ */ jsxRuntime.jsx("dl", { className: cn("divide-y divide-border rounded-[1.25rem] border border-border bg-card", className), children: items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 px-5 py-3.5 sm:flex-row sm:items-baseline sm:gap-6", children: [
@@ -1186,75 +1290,103 @@ function DataTable({
1186
1290
  rowKey,
1187
1291
  empty,
1188
1292
  caption,
1293
+ phone = "cards",
1189
1294
  className
1190
1295
  }) {
1191
1296
  if (rows.length === 0 && empty) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: empty });
1192
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("overflow-x-auto rounded-[1.25rem] border border-border bg-card", className), children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem]", children: [
1193
- caption ? /* @__PURE__ */ jsxRuntime.jsx("caption", { className: "sr-only", children: caption }) : null,
1194
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-border", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
1195
- "th",
1196
- {
1197
- scope: "col",
1198
- className: cn(
1199
- "whitespace-nowrap px-5 py-3 text-[0.8125rem] font-medium text-muted-foreground",
1200
- c.align === "end" && "text-right",
1201
- c.wide && "hidden md:table-cell"
1202
- ),
1203
- children: c.header
1204
- },
1205
- c.key
1206
- )) }) }),
1207
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border", children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "transition-colors hover:bg-accent/60", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
1208
- "td",
1297
+ const primary = columns.find((c) => c.primary) ?? columns[0];
1298
+ const right = (c) => c.numeric || c.align === "end";
1299
+ const table = /* @__PURE__ */ jsxRuntime.jsx(
1300
+ "div",
1301
+ {
1302
+ className: cn(
1303
+ "overflow-hidden rounded-[1.25rem] border border-border bg-card",
1304
+ phone === "cards" && "max-sm:hidden",
1305
+ className
1306
+ ),
1307
+ children: /* @__PURE__ */ jsxRuntime.jsx(ScrollFade, { fade: "var(--card)", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem]", children: [
1308
+ caption ? /* @__PURE__ */ jsxRuntime.jsx("caption", { className: "sr-only", children: caption }) : null,
1309
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-border", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
1310
+ "th",
1311
+ {
1312
+ scope: "col",
1313
+ style: { width: c.width, minWidth: c.minWidth },
1314
+ className: cn(
1315
+ "whitespace-nowrap px-5 py-3 text-[0.8125rem] font-medium text-muted-foreground",
1316
+ right(c) && "text-right",
1317
+ c.wide && "hidden md:table-cell"
1318
+ ),
1319
+ children: c.header
1320
+ },
1321
+ c.key
1322
+ )) }) }),
1323
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border", children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "transition-colors hover:bg-accent/60", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
1324
+ "td",
1325
+ {
1326
+ style: { minWidth: c.minWidth },
1327
+ className: cn(
1328
+ "px-5 py-3.5 align-middle",
1329
+ right(c) && "text-right",
1330
+ c.numeric && "whitespace-nowrap oca-tabular",
1331
+ c.wide && "hidden md:table-cell"
1332
+ ),
1333
+ children: c.truncate ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate", style: { maxWidth: c.width ?? "16rem" }, children: c.cell(r) }) : c.cell(r)
1334
+ },
1335
+ c.key
1336
+ )) }, rowKey(r, i))) })
1337
+ ] }) })
1338
+ }
1339
+ );
1340
+ if (phone !== "cards") return table;
1341
+ const rest = columns.filter((c) => c !== primary && !c.hideOnPhone);
1342
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1343
+ table,
1344
+ /* @__PURE__ */ jsxRuntime.jsx(
1345
+ "ul",
1209
1346
  {
1210
- className: cn(
1211
- "px-5 py-3.5 align-middle",
1212
- c.align === "end" && "text-right",
1213
- c.wide && "hidden md:table-cell"
1214
- ),
1215
- children: c.cell(r)
1216
- },
1217
- c.key
1218
- )) }, rowKey(r, i))) })
1219
- ] }) });
1347
+ "aria-label": caption,
1348
+ className: cn("divide-y divide-border rounded-[1.25rem] border border-border bg-card sm:hidden", className),
1349
+ children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-col gap-2.5 px-4 py-4", children: [
1350
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 break-words text-[0.9375rem] font-medium", children: primary?.cell(r) }),
1351
+ rest.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "grid grid-cols-[minmax(5.5rem,auto)_minmax(0,1fr)] gap-x-4 gap-y-1.5 text-[0.875rem]", children: rest.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "contents", children: [
1352
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: c.header }),
1353
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { className: cn("min-w-0 break-words", c.numeric && "oca-tabular"), children: c.cell(r) })
1354
+ ] }, c.key)) }) : null
1355
+ ] }, rowKey(r, i)))
1356
+ }
1357
+ )
1358
+ ] });
1220
1359
  }
1221
1360
  function EmptyState({
1222
1361
  title,
1223
1362
  children,
1224
1363
  action,
1364
+ icon,
1225
1365
  className
1226
1366
  }) {
1227
1367
  return /* @__PURE__ */ jsxRuntime.jsxs(
1228
1368
  "div",
1229
1369
  {
1230
1370
  className: cn(
1231
- "flex flex-col items-center gap-4 rounded-[1.25rem] border border-dashed border-oca-hairline-strong px-6 py-12 text-center",
1371
+ "flex flex-col items-center gap-3 rounded-[1.25rem] border border-dashed border-oca-hairline-strong px-6 py-10 text-center",
1232
1372
  className
1233
1373
  ),
1234
1374
  children: [
1235
- /* @__PURE__ */ jsxRuntime.jsx(Mark, { size: 56 }),
1375
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 flex size-11 items-center justify-center rounded-full bg-muted text-muted-foreground [&_svg]:size-5", children: icon }) : null,
1236
1376
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "oca-title-sm", children: title }),
1237
1377
  children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oca-body max-w-md text-[0.9375rem]", children }) : null,
1238
- action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 flex flex-wrap justify-center gap-3", children: action }) : null
1378
+ action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex flex-wrap justify-center gap-3", children: action }) : null
1239
1379
  ]
1240
1380
  }
1241
1381
  );
1242
1382
  }
1243
- var NOTICE = {
1244
- neutral: "border-border bg-card",
1245
- info: "border-[color-mix(in_oklab,var(--info)_30%,var(--border))] bg-[color-mix(in_oklab,var(--info)_6%,var(--card))]",
1246
- success: "border-[color-mix(in_oklab,var(--success)_30%,var(--border))] bg-[color-mix(in_oklab,var(--success)_6%,var(--card))]",
1247
- warning: "border-[color-mix(in_oklab,var(--warning)_35%,var(--border))] bg-[color-mix(in_oklab,var(--warning)_7%,var(--card))]",
1248
- danger: "border-[color-mix(in_oklab,var(--destructive)_32%,var(--border))] bg-[color-mix(in_oklab,var(--destructive)_6%,var(--card))]",
1249
- brand: "border-[color-mix(in_oklab,var(--oca-orange)_35%,var(--border))] bg-[color-mix(in_oklab,var(--oca-orange)_7%,var(--card))]"
1250
- };
1251
- var NOTICE_TITLE = {
1252
- neutral: "text-foreground",
1253
- info: "text-info",
1254
- success: "text-success",
1255
- warning: "text-warning",
1256
- danger: "text-destructive",
1257
- brand: "text-oca-accent-ink"
1383
+ var NOTICE_KIND = {
1384
+ neutral: "neutral",
1385
+ info: "neutral",
1386
+ success: "neutral",
1387
+ brand: "brand",
1388
+ warning: "brand",
1389
+ danger: "danger"
1258
1390
  };
1259
1391
  function Notice({
1260
1392
  tone = "neutral",
@@ -1263,25 +1395,60 @@ function Notice({
1263
1395
  action,
1264
1396
  className
1265
1397
  }) {
1398
+ const kind = NOTICE_KIND[tone];
1266
1399
  return /* @__PURE__ */ jsxRuntime.jsxs(
1267
1400
  "div",
1268
1401
  {
1269
1402
  role: tone === "danger" || tone === "warning" ? "alert" : "note",
1403
+ "data-tone": kind,
1270
1404
  className: cn(
1271
- "flex flex-col gap-3 rounded-2xl border px-5 py-4 sm:flex-row sm:items-start sm:justify-between",
1272
- NOTICE[tone],
1405
+ "flex flex-col gap-3 rounded-2xl border border-border bg-card px-5 py-4 sm:flex-row sm:items-start sm:justify-between",
1406
+ kind === "danger" && "border-[color-mix(in_oklab,var(--destructive)_40%,var(--border))]",
1273
1407
  className
1274
1408
  ),
1275
1409
  children: [
1276
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
1277
- title ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("text-[0.9375rem] font-medium", NOTICE_TITLE[tone]), children: title }) : null,
1278
- children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[0.9375rem] leading-relaxed text-foreground/85", children }) : null
1410
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 gap-3", children: [
1411
+ kind !== "neutral" ? /* @__PURE__ */ jsxRuntime.jsx(
1412
+ "span",
1413
+ {
1414
+ "aria-hidden": true,
1415
+ className: cn(
1416
+ "mt-[0.45rem] size-2 shrink-0 rounded-full",
1417
+ kind === "brand" ? "bg-oca-orange" : "bg-destructive"
1418
+ )
1419
+ }
1420
+ ) : null,
1421
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
1422
+ title ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("text-[0.9375rem] font-medium", kind === "danger" && "text-destructive"), children: title }) : null,
1423
+ children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[0.9375rem] leading-relaxed text-muted-foreground", children }) : null
1424
+ ] })
1279
1425
  ] }),
1280
1426
  action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: action }) : null
1281
1427
  ]
1282
1428
  }
1283
1429
  );
1284
1430
  }
1431
+ function LoadingBlock({
1432
+ rows = 3,
1433
+ timeoutMs = 12e3,
1434
+ onRetry,
1435
+ label = "Loading",
1436
+ className
1437
+ }) {
1438
+ const timedOut = useTimedOut(true, timeoutMs);
1439
+ if (timedOut) {
1440
+ return /* @__PURE__ */ jsxRuntime.jsx(
1441
+ Notice,
1442
+ {
1443
+ className,
1444
+ title: "This is taking longer than it should.",
1445
+ action: /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "secondary", onClick: onRetry ?? (() => window.location.reload()), children: "Try again" }),
1446
+ children: "Check your connection, then try again."
1447
+ }
1448
+ );
1449
+ }
1450
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-label": label, className: cn("flex flex-col gap-3", className), children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-16 rounded-2xl" }, i)) });
1451
+ }
1285
1452
  function middle(v, head, tail) {
1286
1453
  return v.length <= head + tail + 1 ? v : `${v.slice(0, head)}\u2026${v.slice(-tail)}`;
1287
1454
  }
@@ -1387,6 +1554,252 @@ function ActivityList({ items, empty, className }) {
1387
1554
  return /* @__PURE__ */ jsxRuntime.jsx("li", { children: it.href ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: it.href, className: "block transition-colors hover:bg-accent/60", children: body }) : body }, it.id);
1388
1555
  }) });
1389
1556
  }
1557
+ var KIND = {
1558
+ package: "text-oca-code-package",
1559
+ tag: "text-oca-code-tag",
1560
+ link: "text-oca-code-link"
1561
+ };
1562
+ function PackageCard({ items, className }) {
1563
+ return /* @__PURE__ */ jsxRuntime.jsx(
1564
+ "div",
1565
+ {
1566
+ className: cn(
1567
+ "grid gap-x-10 gap-y-7 rounded-[1.25rem] border border-oca-ink-line bg-oca-ink-card p-6 text-oca-ink-foreground md:grid-cols-2 md:p-9",
1568
+ className
1569
+ ),
1570
+ children: items.map((it, i) => {
1571
+ const code = /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[0.9375rem]", KIND[it.kind ?? "package"]), children: it.code });
1572
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
1573
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[1.125rem] font-semibold tracking-[-0.01em] text-oca-ink-title", children: it.title }),
1574
+ it.href ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: it.href, className: "w-fit underline decoration-transparent underline-offset-4 hover:decoration-current", children: code }) : code,
1575
+ it.note ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[0.9375rem] leading-relaxed text-oca-ink-subtle", children: [
1576
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "// " }),
1577
+ it.note
1578
+ ] }) : null
1579
+ ] }, i);
1580
+ })
1581
+ }
1582
+ );
1583
+ }
1584
+ function CodeBlock({
1585
+ code,
1586
+ caption,
1587
+ copy = true,
1588
+ maxLines,
1589
+ className
1590
+ }) {
1591
+ const lines = code.split("\n").length;
1592
+ const foldable = maxLines !== void 0 && lines > maxLines + 2;
1593
+ const [open, setOpen] = react.useState(false);
1594
+ const folded = foldable && !open;
1595
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1596
+ "figure",
1597
+ {
1598
+ className: cn(
1599
+ "overflow-hidden rounded-[1.25rem] border border-[var(--oca-code-line)] bg-[var(--oca-code-surface)]",
1600
+ className
1601
+ ),
1602
+ children: [
1603
+ /* @__PURE__ */ jsxRuntime.jsxs("figcaption", { className: "flex h-11 items-center justify-between gap-3 border-b border-[var(--oca-code-line)] pl-5 pr-2", children: [
1604
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[0.8125rem] text-oca-ink-subtle", children: caption ?? "Code" }),
1605
+ copy ? /* @__PURE__ */ jsxRuntime.jsx(
1606
+ CopyButton,
1607
+ {
1608
+ value: code,
1609
+ label: "Copy code",
1610
+ className: "text-oca-ink-subtle hover:bg-white/10 hover:text-white"
1611
+ }
1612
+ ) : null
1613
+ ] }),
1614
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1615
+ /* @__PURE__ */ jsxRuntime.jsx(
1616
+ "div",
1617
+ {
1618
+ className: cn(folded && "overflow-hidden"),
1619
+ style: folded ? { maxHeight: `calc(${maxLines} * 1.625em + 2.5rem)` } : void 0,
1620
+ children: /* @__PURE__ */ jsxRuntime.jsx(ScrollFade, { fade: "var(--oca-code-surface)", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "w-max min-w-full p-5 font-mono text-[0.875rem] leading-[1.625] text-oca-ink-muted", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) }) })
1621
+ }
1622
+ ),
1623
+ foldable ? /* @__PURE__ */ jsxRuntime.jsx(
1624
+ "div",
1625
+ {
1626
+ className: cn(
1627
+ "flex justify-center px-5 pb-4",
1628
+ folded && "absolute inset-x-0 bottom-0 pt-16"
1629
+ ),
1630
+ style: folded ? { background: "linear-gradient(to top, var(--oca-code-surface) 40%, transparent)" } : void 0,
1631
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1632
+ "button",
1633
+ {
1634
+ type: "button",
1635
+ onClick: () => setOpen((v) => !v),
1636
+ className: "rounded-full border border-white/20 px-4 py-1.5 text-[0.8125rem] text-white transition-colors hover:bg-white/10",
1637
+ children: open ? "Show less" : `Show all ${lines} lines`
1638
+ }
1639
+ )
1640
+ }
1641
+ ) : null
1642
+ ] })
1643
+ ]
1644
+ }
1645
+ );
1646
+ }
1647
+ function SplitBar({
1648
+ segments,
1649
+ caption,
1650
+ className
1651
+ }) {
1652
+ return /* @__PURE__ */ jsxRuntime.jsxs("figure", { className: cn("flex flex-col gap-5", className), children: [
1653
+ caption ? /* @__PURE__ */ jsxRuntime.jsx("figcaption", { className: "text-[0.9375rem]", children: caption }) : null,
1654
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-16 w-full gap-1.5 overflow-hidden rounded-[1.25rem] md:h-20", children: segments.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
1655
+ "div",
1656
+ {
1657
+ className: cn(
1658
+ "flex min-w-[4.25rem] items-end rounded-[1rem] px-3 pb-2.5 md:px-4 md:pb-3",
1659
+ s.tone === "card" ? "bg-white text-oca-ink" : "bg-oca-ink text-white"
1660
+ ),
1661
+ style: { flexBasis: `${s.pct}%` },
1662
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "oca-tabular text-[1.25rem] leading-none tracking-[-0.02em] md:text-[1.875rem]", children: [
1663
+ s.pct,
1664
+ "%"
1665
+ ] })
1666
+ },
1667
+ i
1668
+ )) }),
1669
+ /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "grid gap-x-8 gap-y-2 sm:grid-cols-2", children: segments.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2.5", children: [
1670
+ /* @__PURE__ */ jsxRuntime.jsxs("dt", { className: "sr-only", children: [
1671
+ s.pct,
1672
+ "%"
1673
+ ] }),
1674
+ /* @__PURE__ */ jsxRuntime.jsx(
1675
+ "span",
1676
+ {
1677
+ "aria-hidden": true,
1678
+ className: cn(
1679
+ "inline-block size-2.5 shrink-0 translate-y-[-1px] rounded-full",
1680
+ s.tone === "card" ? "bg-white" : "bg-oca-ink"
1681
+ )
1682
+ }
1683
+ ),
1684
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "text-[0.9375rem] leading-snug", children: s.label })
1685
+ ] }, i)) })
1686
+ ] });
1687
+ }
1688
+ var LABEL = {
1689
+ yes: "Yes",
1690
+ testing: "In testing",
1691
+ "not-yet": "Not yet",
1692
+ no: "No"
1693
+ };
1694
+ var TONE = {
1695
+ yes: "text-oca-accent-ink",
1696
+ testing: "text-foreground",
1697
+ "not-yet": "text-muted-foreground",
1698
+ no: "text-muted-foreground"
1699
+ };
1700
+ function TruthTable({
1701
+ rows,
1702
+ statusHeading = "Status",
1703
+ whatHeading = "What",
1704
+ className
1705
+ }) {
1706
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1707
+ /* @__PURE__ */ jsxRuntime.jsx(
1708
+ "ul",
1709
+ {
1710
+ className: cn(
1711
+ "divide-y divide-oca-hairline-strong overflow-hidden rounded-xl border border-oca-hairline-strong bg-card sm:hidden",
1712
+ className
1713
+ ),
1714
+ children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-col gap-1.5 px-4 py-3.5", children: [
1715
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[0.875rem] font-medium", TONE[r.status]), children: LABEL[r.status] }),
1716
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.9375rem] leading-relaxed", children: r.what })
1717
+ ] }, i))
1718
+ }
1719
+ ),
1720
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("overflow-hidden rounded-xl border border-oca-hairline-strong max-sm:hidden", className), children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem] md:text-[1rem]", children: [
1721
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-oca-table-head", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1722
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "w-[30%] border-b border-oca-hairline-strong px-4 py-3 font-semibold md:w-[22%]", children: statusHeading }),
1723
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "border-b border-l border-oca-hairline-strong px-4 py-3 font-semibold", children: whatHeading })
1724
+ ] }) }),
1725
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-card", children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "align-top", children: [
1726
+ /* @__PURE__ */ jsxRuntime.jsx(
1727
+ "td",
1728
+ {
1729
+ className: cn(
1730
+ "border-t border-oca-hairline-strong px-4 py-3 font-medium",
1731
+ i === 0 && "border-t-0",
1732
+ TONE[r.status]
1733
+ ),
1734
+ children: LABEL[r.status]
1735
+ }
1736
+ ),
1737
+ /* @__PURE__ */ jsxRuntime.jsx(
1738
+ "td",
1739
+ {
1740
+ className: cn(
1741
+ "border-l border-t border-oca-hairline-strong px-4 py-3 leading-relaxed",
1742
+ i === 0 && "border-t-0"
1743
+ ),
1744
+ children: r.what
1745
+ }
1746
+ )
1747
+ ] }, i)) })
1748
+ ] }) })
1749
+ ] });
1750
+ }
1751
+ var INT = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
1752
+ var COMPACT = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
1753
+ function satsText(value, opts = {}) {
1754
+ if (value == null || !Number.isFinite(value)) return null;
1755
+ const whole = Math.trunc(value);
1756
+ const body = (opts.compact ? COMPACT : INT).format(Math.abs(whole));
1757
+ const sign = whole < 0 ? "\u2212" : opts.signed && whole > 0 ? "+" : "";
1758
+ const unit = opts.unit === "none" ? "" : whole === 1 || whole === -1 ? " sat" : " sats";
1759
+ return `${sign}${body}${unit}`;
1760
+ }
1761
+ function Sats({ value, unit, compact, signed, unknown = "\u2014", className, ...props }) {
1762
+ const text = satsText(value, { unit, compact, signed });
1763
+ if (text === null) {
1764
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), ...props, children: [
1765
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: unknown }),
1766
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "unknown" })
1767
+ ] });
1768
+ }
1769
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("oca-tabular whitespace-nowrap", className), ...props, children: text });
1770
+ }
1771
+ function UsdApprox({
1772
+ sats,
1773
+ usdPerBtc,
1774
+ className
1775
+ }) {
1776
+ if (sats == null || usdPerBtc == null || !Number.isFinite(sats) || !Number.isFinite(usdPerBtc)) return null;
1777
+ const usd = sats / 1e8 * usdPerBtc;
1778
+ const text = new Intl.NumberFormat("en-US", {
1779
+ style: "currency",
1780
+ currency: "USD",
1781
+ maximumFractionDigits: usd < 1 ? 4 : 2
1782
+ }).format(usd);
1783
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), children: [
1784
+ "\u2248 ",
1785
+ text
1786
+ ] });
1787
+ }
1788
+ function SatsFigure({
1789
+ label,
1790
+ value,
1791
+ hint,
1792
+ className
1793
+ }) {
1794
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
1795
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.9375rem] text-muted-foreground", children: label }),
1796
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "oca-display-3 oca-tabular", children: [
1797
+ /* @__PURE__ */ jsxRuntime.jsx(Sats, { value, unit: "none" }),
1798
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 text-[0.5em] tracking-normal text-muted-foreground", children: "sats" })
1799
+ ] }),
1800
+ hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.875rem] text-muted-foreground", children: hint }) : null
1801
+ ] });
1802
+ }
1390
1803
  function NavLink({ item, className, onNavigate }) {
1391
1804
  const cls = cn(
1392
1805
  "rounded-full px-3 py-2 text-[0.9375rem] transition-colors",
@@ -1452,12 +1865,12 @@ function SiteFooter({
1452
1865
  className
1453
1866
  }) {
1454
1867
  return /* @__PURE__ */ jsxRuntime.jsxs("footer", { className: cn("border-t border-border bg-background", className), children: [
1455
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oca-container grid gap-10 py-14 md:grid-cols-[1.3fr_repeat(3,1fr)] md:py-16", children: [
1456
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
1868
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oca-container grid grid-cols-2 gap-x-6 gap-y-10 py-14 md:grid-cols-[1.3fr_repeat(3,1fr)] md:py-16", children: [
1869
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-2 flex flex-col gap-4 md:col-span-1", children: [
1457
1870
  /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 26 }),
1458
1871
  fineprint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-xs text-[0.9375rem] leading-relaxed text-muted-foreground", children: fineprint }) : null
1459
1872
  ] }),
1460
- columns.map((col) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
1873
+ columns.map((col) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
1461
1874
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[0.875rem] font-medium", children: col.heading }),
1462
1875
  col.links.map((l) => /* @__PURE__ */ jsxRuntime.jsx(
1463
1876
  NavLink,
@@ -1473,8 +1886,8 @@ function SiteFooter({
1473
1886
  ] });
1474
1887
  }
1475
1888
  function SideNav({ groups, onNavigate }) {
1476
- return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Sections", className: "flex flex-col gap-6", children: groups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
1477
- g.label ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-1.5 text-[0.8125rem] text-muted-foreground", children: g.label }) : null,
1889
+ return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Pages", className: "flex flex-col gap-5", children: groups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
1890
+ g.label ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-1 text-[0.8125rem] text-muted-foreground", children: g.label }) : null,
1478
1891
  g.items.map((it) => /* @__PURE__ */ jsxRuntime.jsxs(
1479
1892
  Link2__default.default,
1480
1893
  {
@@ -1482,8 +1895,8 @@ function SideNav({ groups, onNavigate }) {
1482
1895
  onClick: onNavigate,
1483
1896
  "aria-current": it.active ? "page" : void 0,
1484
1897
  className: cn(
1485
- "flex h-10 items-center gap-3 rounded-xl px-3 text-[0.9375rem] transition-colors",
1486
- "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
1898
+ "flex h-9 items-center gap-3 rounded-xl px-3 text-[0.9375rem] transition-colors",
1899
+ "[&_svg]:size-[1.0625rem] [&_svg]:shrink-0",
1487
1900
  it.active ? "bg-card text-foreground shadow-sm ring-1 ring-border [&_svg]:text-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground"
1488
1901
  ),
1489
1902
  children: [
@@ -1496,8 +1909,56 @@ function SideNav({ groups, onNavigate }) {
1496
1909
  ))
1497
1910
  ] }, g.label ?? gi)) });
1498
1911
  }
1912
+ function SectionSwitch({
1913
+ sections,
1914
+ current,
1915
+ onNavigate
1916
+ }) {
1917
+ const cols = sections.length <= 3 ? sections.length : 2;
1918
+ return /* @__PURE__ */ jsxRuntime.jsx(
1919
+ "nav",
1920
+ {
1921
+ "aria-label": "Sections",
1922
+ className: cn("grid gap-1 bg-muted p-1", cols === sections.length ? "rounded-full" : "rounded-[1.25rem]"),
1923
+ style: { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` },
1924
+ children: sections.map((sec) => {
1925
+ const on = sec.id === current.id;
1926
+ return /* @__PURE__ */ jsxRuntime.jsx(
1927
+ Link2__default.default,
1928
+ {
1929
+ href: sec.href,
1930
+ onClick: onNavigate,
1931
+ "aria-current": on ? "true" : void 0,
1932
+ className: cn(
1933
+ "flex h-8 min-w-0 items-center justify-center rounded-full px-1.5 text-[0.8125rem] font-medium transition-colors",
1934
+ on ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
1935
+ ),
1936
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: sec.label })
1937
+ },
1938
+ sec.id
1939
+ );
1940
+ })
1941
+ }
1942
+ );
1943
+ }
1944
+ var activeItem = (groups) => groups.flatMap((g) => g.items).find((it) => it.active);
1945
+ var warned = /* @__PURE__ */ new Set();
1946
+ function warnDuplicateLabels(groups) {
1947
+ if (typeof process === "undefined" || process.env.NODE_ENV === "production") return;
1948
+ const seen = /* @__PURE__ */ new Set();
1949
+ for (const it of groups.flatMap((g) => g.items)) {
1950
+ if (seen.has(it.label) && !warned.has(it.label)) {
1951
+ warned.add(it.label);
1952
+ console.warn(`AppShell: two pages are labelled "${it.label}"; give each its own name.`);
1953
+ }
1954
+ seen.add(it.label);
1955
+ }
1956
+ }
1499
1957
  function AppShell({
1500
1958
  nav,
1959
+ sections,
1960
+ activeSection,
1961
+ persistent = [],
1501
1962
  home = "/",
1502
1963
  account,
1503
1964
  banner,
@@ -1506,38 +1967,59 @@ function AppShell({
1506
1967
  className
1507
1968
  }) {
1508
1969
  const [open, setOpen] = react.useState(false);
1509
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("min-h-dvh bg-background", className), children: [
1510
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sticky top-0 z-40 flex h-14 items-center justify-between border-b border-border bg-background/85 px-4 backdrop-blur-md lg:hidden", children: [
1511
- /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open, onOpenChange: setOpen, children: [
1512
- /* @__PURE__ */ jsxRuntime.jsx(
1513
- SheetTrigger,
1514
- {
1515
- className: "inline-flex size-10 items-center justify-center rounded-full hover:bg-accent",
1516
- "aria-label": "Open navigation",
1517
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "size-5" })
1518
- }
1519
- ),
1520
- /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "left", className: "p-4 pt-16", children: [
1521
- /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: "Navigation" }),
1522
- /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups: nav, onNavigate: () => setOpen(false) })
1523
- ] })
1970
+ const current = sections && sections.length > 0 ? sections.find((sec) => sec.id === activeSection) ?? sections.find((sec) => activeItem(sec.groups)) ?? sections[0] : void 0;
1971
+ const groups = current ? [...current.groups, ...persistent] : [...nav ?? [], ...persistent];
1972
+ warnDuplicateLabels(groups);
1973
+ const page = activeItem(groups);
1974
+ const place = react.useMemo(
1975
+ () => ({ section: current ? current.title ?? current.label : void 0, page: page?.label }),
1976
+ [current, page?.label]
1977
+ );
1978
+ const switcher = sections && current && sections.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(SectionSwitch, { sections, current, onNavigate: () => setOpen(false) }) : null;
1979
+ return /* @__PURE__ */ jsxRuntime.jsx(ShellContext.Provider, { value: place, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("min-h-dvh bg-background", className), children: [
1980
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sticky top-0 z-40 border-b border-border bg-background/90 backdrop-blur-md lg:hidden", children: [
1981
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-14 items-center gap-2 px-3", children: [
1982
+ /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open, onOpenChange: setOpen, children: [
1983
+ /* @__PURE__ */ jsxRuntime.jsx(
1984
+ SheetTrigger,
1985
+ {
1986
+ className: "inline-flex size-10 items-center justify-center rounded-full hover:bg-accent",
1987
+ "aria-label": "Open navigation",
1988
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "size-5" })
1989
+ }
1990
+ ),
1991
+ /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "left", className: "flex flex-col gap-6 overflow-y-auto p-4 pt-16", children: [
1992
+ /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: "Navigation" }),
1993
+ switcher,
1994
+ /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups, onNavigate: () => setOpen(false) }),
1995
+ footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer }) : null
1996
+ ] })
1997
+ ] }),
1998
+ /* @__PURE__ */ jsxRuntime.jsx(Link2__default.default, { href: home, "aria-label": "Home", className: "rounded-lg p-1", children: /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 22 }) }),
1999
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex items-center", children: account })
1524
2000
  ] }),
1525
- /* @__PURE__ */ jsxRuntime.jsx(Link2__default.default, { href: home, "aria-label": "Home", children: /* @__PURE__ */ jsxRuntime.jsx(AppIcon, { size: 28 }) }),
1526
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: account })
2001
+ place.page ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-9 items-center gap-1.5 border-t border-border px-4 text-[0.875rem]", children: [
2002
+ place.section ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2003
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: place.section }),
2004
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": true, className: "size-3.5 shrink-0 text-muted-foreground" })
2005
+ ] }) : null,
2006
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", children: place.page })
2007
+ ] }) : null
1527
2008
  ] }),
1528
2009
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-[90rem]", children: [
1529
- /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "sticky top-0 hidden h-dvh w-64 shrink-0 flex-col gap-8 overflow-y-auto px-4 py-6 lg:flex", children: [
2010
+ /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "sticky top-0 hidden h-dvh w-[17rem] shrink-0 flex-col gap-6 overflow-y-auto px-4 py-6 lg:flex", children: [
1530
2011
  /* @__PURE__ */ jsxRuntime.jsx(Link2__default.default, { href: home, className: "px-2", "aria-label": "Home", children: /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 26 }) }),
1531
- /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups: nav }),
2012
+ switcher,
2013
+ /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups }),
1532
2014
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer })
1533
2015
  ] }),
1534
2016
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
1535
2017
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden h-[4.5rem] items-center justify-end gap-3 px-8 lg:flex", children: account }),
1536
2018
  banner ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pb-2 pt-4 md:px-8 lg:pt-0", children: banner }) : null,
1537
- /* @__PURE__ */ jsxRuntime.jsx("main", { id: "main-content", className: "flex-1 px-4 pb-16 pt-6 md:px-8 lg:pt-2", children })
2019
+ /* @__PURE__ */ jsxRuntime.jsx("main", { id: "main-content", className: "flex-1 px-4 pb-16 pt-6 md:px-8 lg:pt-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full max-w-[72rem]", children }) })
1538
2020
  ] })
1539
2021
  ] })
1540
- ] });
2022
+ ] }) });
1541
2023
  }
1542
2024
  function AccountMenu({
1543
2025
  name,
@@ -1549,11 +2031,11 @@ function AccountMenu({
1549
2031
  /* @__PURE__ */ jsxRuntime.jsxs(
1550
2032
  DropdownMenuTrigger,
1551
2033
  {
1552
- className: "inline-flex h-10 items-center gap-2.5 rounded-full border border-border bg-card pl-1 pr-3.5 text-[0.9375rem] outline-none transition-colors hover:border-oca-hairline-strong focus-visible:ring-2 focus-visible:ring-ring",
2034
+ className: "inline-flex h-10 items-center gap-2.5 rounded-full border border-border bg-card pl-1 pr-1 text-[0.9375rem] outline-none transition-colors hover:border-oca-hairline-strong focus-visible:ring-2 focus-visible:ring-ring sm:pr-3.5",
1553
2035
  "aria-label": `Account: ${name}`,
1554
2036
  children: [
1555
2037
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-8 items-center justify-center rounded-full bg-foreground text-[0.8125rem] font-medium text-background", children: initial }),
1556
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[10rem] truncate", children: name })
2038
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[10rem] truncate max-sm:hidden", children: name })
1557
2039
  ]
1558
2040
  }
1559
2041
  ),
@@ -1633,6 +2115,8 @@ exports.DialogHeader = DialogHeader;
1633
2115
  exports.DialogTitle = DialogTitle;
1634
2116
  exports.DialogTrigger = DialogTrigger;
1635
2117
  exports.Display = Display;
2118
+ exports.DocHeader = DocHeader;
2119
+ exports.DocLayout = DocLayout;
1636
2120
  exports.DropdownMenu = DropdownMenu;
1637
2121
  exports.DropdownMenuContent = DropdownMenuContent;
1638
2122
  exports.DropdownMenuGroup = DropdownMenuGroup;
@@ -1640,6 +2124,7 @@ exports.DropdownMenuItem = DropdownMenuItem;
1640
2124
  exports.DropdownMenuLabel = DropdownMenuLabel;
1641
2125
  exports.DropdownMenuSeparator = DropdownMenuSeparator;
1642
2126
  exports.DropdownMenuTrigger = DropdownMenuTrigger;
2127
+ exports.EmptyLine = EmptyLine;
1643
2128
  exports.EmptyState = EmptyState;
1644
2129
  exports.Eyebrow = Eyebrow;
1645
2130
  exports.Field = Field;
@@ -1652,12 +2137,14 @@ exports.Kbd = Kbd;
1652
2137
  exports.KeyValueList = KeyValueList;
1653
2138
  exports.Label = Label;
1654
2139
  exports.Lead = Lead;
2140
+ exports.LoadingBlock = LoadingBlock;
1655
2141
  exports.Lockup = Lockup;
1656
2142
  exports.MARK_FACES = MARK_FACES;
1657
2143
  exports.Mark = Mark;
1658
2144
  exports.Notice = Notice;
1659
2145
  exports.PackageCard = PackageCard;
1660
2146
  exports.PageHeader = PageHeader;
2147
+ exports.PageHero = PageHero;
1661
2148
  exports.Popover = Popover;
1662
2149
  exports.PopoverAnchor = PopoverAnchor;
1663
2150
  exports.PopoverContent = PopoverContent;
@@ -1669,6 +2156,7 @@ exports.RadioGroupItem = RadioGroupItem;
1669
2156
  exports.SPHERE_GRADIENT = SPHERE_GRADIENT;
1670
2157
  exports.Sats = Sats;
1671
2158
  exports.SatsFigure = SatsFigure;
2159
+ exports.ScrollFade = ScrollFade;
1672
2160
  exports.Section = Section;
1673
2161
  exports.SectionIntro = SectionIntro;
1674
2162
  exports.Select = Select;
@@ -1684,6 +2172,7 @@ exports.SheetContent = SheetContent;
1684
2172
  exports.SheetDescription = SheetDescription;
1685
2173
  exports.SheetTitle = SheetTitle;
1686
2174
  exports.SheetTrigger = SheetTrigger;
2175
+ exports.ShellContext = ShellContext;
1687
2176
  exports.SiteFooter = SiteFooter;
1688
2177
  exports.SiteHeader = SiteHeader;
1689
2178
  exports.Skeleton = Skeleton;
@@ -1717,5 +2206,7 @@ exports.cn = cn;
1717
2206
  exports.satsText = satsText;
1718
2207
  exports.sectionVariants = sectionVariants;
1719
2208
  exports.useCopy = useCopy;
2209
+ exports.useShellPlace = useShellPlace;
2210
+ exports.useTimedOut = useTimedOut;
1720
2211
  //# sourceMappingURL=index.js.map
1721
2212
  //# sourceMappingURL=index.js.map