@orangecheck/app-design 0.2.1 → 0.3.1

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.
@@ -1,9 +1,12 @@
1
- import { forwardRef, createElement, useState, useRef, useEffect, useCallback } from 'react';
1
+ import { forwardRef, createContext, createElement, useContext, Children, isValidElement, cloneElement, useState, useRef, useEffect, useCallback } from 'react';
2
2
  import { clsx } from 'clsx';
3
3
  import { extendTailwindMerge } from 'tailwind-merge';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { cva } from 'class-variance-authority';
6
6
  import { Check, Copy } from 'lucide-react';
7
+ import { Slot } from '@radix-ui/react-slot';
8
+ import * as SeparatorPrimitive from '@radix-ui/react-separator';
9
+ import 'sonner';
7
10
 
8
11
  var merge = extendTailwindMerge({
9
12
  extend: {
@@ -47,7 +50,7 @@ function Prose({ className, ...props }) {
47
50
  {
48
51
  className: cn(
49
52
  "max-w-[42rem] text-[1.0625rem] leading-[1.7] text-muted-foreground",
50
- "[&_h2]:oca-display-3 [&_h2]:mb-4 [&_h2]:mt-14 [&_h2]:text-foreground",
53
+ "[&_h2]:oca-display-3 [&_h2]:mb-4 [&_h2]:mt-14 [&_h2]:scroll-mt-24 [&_h2]:text-foreground",
51
54
  "[&_h3]:oca-title [&_h3]:mb-3 [&_h3]:mt-10 [&_h3]:text-foreground",
52
55
  "[&_p]:my-4 [&_strong]:font-medium [&_strong]:text-foreground",
53
56
  "[&_a]:text-foreground [&_a]:underline [&_a]:decoration-border [&_a]:underline-offset-4 hover:[&_a]:decoration-foreground",
@@ -63,7 +66,9 @@ var sectionVariants = cva("relative w-full overflow-x-clip", {
63
66
  tone: {
64
67
  canvas: "bg-background text-foreground",
65
68
  card: "bg-card text-card-foreground",
66
- ink: "bg-oca-ink text-oca-ink-foreground",
69
+ // The band line is transparent in light; in dark it keeps the ink
70
+ // band from melting into the canvas.
71
+ ink: "border-y border-[var(--oca-band-line)] bg-oca-ink text-oca-ink-foreground",
67
72
  brand: "bg-brand text-brand-foreground"
68
73
  },
69
74
  space: {
@@ -98,6 +103,121 @@ function SectionIntro({
98
103
  }
99
104
  );
100
105
  }
106
+ function PageHero({
107
+ eyebrow,
108
+ title,
109
+ lead,
110
+ actions,
111
+ art,
112
+ meta,
113
+ children,
114
+ variant = "page",
115
+ size = "lg",
116
+ className
117
+ }) {
118
+ if (variant === "document") {
119
+ return /* @__PURE__ */ jsx(Section, { space: "none", className: cn("pb-12 pt-12 md:pb-16 md:pt-20", className), children: /* @__PURE__ */ jsx(DocHeader, { className: "mx-auto max-w-[42rem]", ...{ eyebrow, title, lead, meta, actions }, children }) });
120
+ }
121
+ return /* @__PURE__ */ jsx(Section, { space: "none", className: cn("pb-14 pt-12 md:pb-20 md:pt-20", className), children: /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-10", art && "items-center md:grid-cols-12"), children: [
122
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-start gap-6", art ? "md:col-span-7" : "max-w-[52rem]"), children: [
123
+ eyebrow ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow", children: eyebrow }) : null,
124
+ /* @__PURE__ */ jsx("h1", { className: size === "xl" ? "oca-display-1" : "oca-display-2", children: title }),
125
+ lead ? /* @__PURE__ */ jsx("p", { className: "oca-lead max-w-[38rem]", children: lead }) : null,
126
+ meta ? /* @__PURE__ */ jsx("p", { className: "text-[0.875rem] text-muted-foreground", children: meta }) : null,
127
+ actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3", children: actions }) : null,
128
+ children
129
+ ] }),
130
+ art ? /* @__PURE__ */ jsx("div", { className: "md:col-span-5", children: art }) : null
131
+ ] }) });
132
+ }
133
+ function DocHeader({
134
+ eyebrow,
135
+ title,
136
+ lead,
137
+ meta,
138
+ actions,
139
+ children,
140
+ className
141
+ }) {
142
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", className), children: [
143
+ eyebrow ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow", children: eyebrow }) : null,
144
+ /* @__PURE__ */ jsx("h1", { className: "oca-display-2", children: title }),
145
+ lead ? /* @__PURE__ */ jsx("p", { className: "oca-lead", children: lead }) : null,
146
+ meta ? /* @__PURE__ */ jsx("p", { className: "text-[0.875rem] text-muted-foreground", children: meta }) : null,
147
+ actions ? /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap gap-3", children: actions }) : null,
148
+ children
149
+ ] });
150
+ }
151
+ function useActiveHeading(ids) {
152
+ const [active, setActive] = useState(ids[0]);
153
+ const key = ids.join("|");
154
+ useEffect(() => {
155
+ const els = key.split("|").map((id) => document.getElementById(id)).filter((el) => el !== null);
156
+ if (els.length === 0 || typeof IntersectionObserver === "undefined") return;
157
+ const io = new IntersectionObserver(
158
+ (entries) => {
159
+ const hit = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];
160
+ if (hit) setActive(hit.target.id);
161
+ },
162
+ { rootMargin: "-80px 0px -65% 0px" }
163
+ );
164
+ els.forEach((el) => io.observe(el));
165
+ return () => io.disconnect();
166
+ }, [key]);
167
+ return active;
168
+ }
169
+ function DocLayout({
170
+ toc,
171
+ header,
172
+ children,
173
+ className
174
+ }) {
175
+ const active = useActiveHeading(toc?.map((t) => t.id) ?? []);
176
+ const links = () => toc?.map((t) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
177
+ "a",
178
+ {
179
+ href: `#${t.id}`,
180
+ "aria-current": active === t.id ? "location" : void 0,
181
+ className: cn(
182
+ "block border-l py-1.5 pl-4 text-[0.875rem] leading-snug transition-colors",
183
+ active === t.id ? "border-foreground text-foreground" : "border-border text-muted-foreground hover:text-foreground"
184
+ ),
185
+ children: t.label
186
+ }
187
+ ) }, t.id));
188
+ const hasToc = !!toc && toc.length > 0;
189
+ return /* @__PURE__ */ jsx("div", { className: cn("oca-container pb-24", header ? "pt-12 md:pt-20" : null, className), children: /* @__PURE__ */ jsxs(
190
+ "div",
191
+ {
192
+ className: cn(
193
+ "mx-auto grid grid-cols-1 gap-8",
194
+ 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]"
195
+ ),
196
+ children: [
197
+ header ? /* @__PURE__ */ jsx("div", { className: cn(hasToc && "lg:col-start-2", "pb-4"), children: header }) : null,
198
+ hasToc ? /* @__PURE__ */ jsxs(Fragment, { children: [
199
+ /* @__PURE__ */ jsxs(
200
+ "nav",
201
+ {
202
+ "aria-label": "On this page",
203
+ className: cn("sticky top-24 hidden self-start lg:block", header && "lg:row-start-2"),
204
+ children: [
205
+ /* @__PURE__ */ jsx("p", { className: "mb-3 text-[0.875rem] font-medium", children: "On this page" }),
206
+ /* @__PURE__ */ jsx("ul", { children: links() })
207
+ ]
208
+ }
209
+ ),
210
+ /* @__PURE__ */ jsxs("details", { className: "group rounded-2xl border border-border bg-card px-4 py-3 lg:hidden", children: [
211
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer list-none text-[0.9375rem] font-medium marker:hidden", children: "On this page" }),
212
+ /* @__PURE__ */ jsx("ul", { className: "mt-3", children: links() })
213
+ ] })
214
+ ] }) : null,
215
+ /* @__PURE__ */ jsx("div", { className: cn("min-w-0", hasToc && "lg:col-start-2", hasToc && header && "lg:row-start-2"), children })
216
+ ]
217
+ }
218
+ ) });
219
+ }
220
+ var GOOD_DOT = "border-border bg-card text-foreground before:size-1.5 before:shrink-0 before:rounded-full before:bg-success before:content-['']";
101
221
  var badgeVariants = cva(
102
222
  "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",
103
223
  {
@@ -106,10 +226,13 @@ var badgeVariants = cva(
106
226
  neutral: "border-border bg-card text-foreground",
107
227
  muted: "border-transparent bg-muted text-muted-foreground",
108
228
  brand: "border-transparent bg-[color-mix(in_oklab,var(--oca-orange)_12%,var(--card))] text-oca-accent-ink",
109
- success: "border-transparent bg-[color-mix(in_oklab,var(--success)_12%,var(--card))] text-success",
110
- warning: "border-transparent bg-[color-mix(in_oklab,var(--warning)_14%,var(--card))] text-warning",
111
229
  danger: "border-transparent bg-[color-mix(in_oklab,var(--destructive)_12%,var(--card))] text-destructive",
112
- info: "border-transparent bg-[color-mix(in_oklab,var(--info)_12%,var(--card))] text-info",
230
+ // A good state is a neutral pill with a green dot, not a green pill.
231
+ success: GOOD_DOT,
232
+ // Aliases kept so older call sites keep compiling: attention is
233
+ // the brand accent, information is neutral.
234
+ warning: "border-transparent bg-[color-mix(in_oklab,var(--oca-orange)_12%,var(--card))] text-oca-accent-ink",
235
+ info: "border-border bg-card text-foreground",
113
236
  ink: "border-transparent bg-oca-ink text-oca-ink-foreground",
114
237
  onInk: "border-white/15 bg-white/5 text-white/90"
115
238
  }
@@ -177,208 +300,161 @@ function StepGrid({ items, look = "cards", className }) {
177
300
  ] }) }, i)
178
301
  ) });
179
302
  }
180
- var KIND = {
181
- package: "text-oca-code-package",
182
- tag: "text-oca-code-tag",
183
- link: "text-oca-code-link"
184
- };
185
- function PackageCard({ items, className }) {
186
- return /* @__PURE__ */ jsx(
187
- "div",
188
- {
189
- className: cn(
190
- "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",
191
- className
192
- ),
193
- children: items.map((it, i) => {
194
- const code = /* @__PURE__ */ jsx("span", { className: cn("text-[0.9375rem]", KIND[it.kind ?? "package"]), children: it.code });
195
- return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
196
- /* @__PURE__ */ jsx("h3", { className: "text-[1.125rem] font-semibold tracking-[-0.01em] text-oca-ink-title", children: it.title }),
197
- it.href ? /* @__PURE__ */ jsx("a", { href: it.href, className: "w-fit underline decoration-transparent underline-offset-4 hover:decoration-current", children: code }) : code,
198
- it.note ? /* @__PURE__ */ jsxs("p", { className: "text-[0.9375rem] leading-relaxed text-oca-ink-subtle", children: [
199
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "// " }),
200
- it.note
201
- ] }) : null
202
- ] }, i);
203
- })
204
- }
205
- );
206
- }
207
- function CodeBlock({
208
- code,
209
- caption,
210
- className
211
- }) {
212
- return /* @__PURE__ */ jsxs("figure", { className: cn("overflow-hidden rounded-[1.25rem] border border-oca-ink-line bg-oca-ink-card", className), children: [
213
- caption ? /* @__PURE__ */ jsx("figcaption", { className: "border-b border-oca-ink-line px-5 py-3 text-[0.8125rem] text-oca-ink-subtle", children: caption }) : null,
214
- /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto p-5 font-mono text-[0.875rem] leading-relaxed text-oca-ink-muted", children: /* @__PURE__ */ jsx("code", { children: code }) })
215
- ] });
216
- }
217
- function SplitBar({
218
- segments,
219
- caption,
220
- className
221
- }) {
222
- return /* @__PURE__ */ jsxs("figure", { className: cn("flex flex-col gap-5", className), children: [
223
- caption ? /* @__PURE__ */ jsx("figcaption", { className: "text-[0.9375rem]", children: caption }) : null,
224
- /* @__PURE__ */ 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__ */ jsx(
225
- "div",
303
+ function ScrollFade({ fade = "var(--card)", className, innerClassName, children, ...props }) {
304
+ const ref = useRef(null);
305
+ const [edges, setEdges] = useState({ left: false, right: false });
306
+ const measure = useCallback(() => {
307
+ const el = ref.current;
308
+ if (!el) return;
309
+ const left = el.scrollLeft > 1;
310
+ const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
311
+ setEdges((e) => e.left === left && e.right === right ? e : { left, right });
312
+ }, []);
313
+ useEffect(() => {
314
+ const el = ref.current;
315
+ if (!el) return;
316
+ measure();
317
+ if (typeof ResizeObserver === "undefined") return;
318
+ const ro = new ResizeObserver(measure);
319
+ ro.observe(el);
320
+ if (el.firstElementChild) ro.observe(el.firstElementChild);
321
+ return () => ro.disconnect();
322
+ }, [measure]);
323
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative min-w-0", className), ...props, children: [
324
+ /* @__PURE__ */ jsx("div", { ref, onScroll: measure, className: cn("overflow-x-auto overscroll-x-contain", innerClassName), children }),
325
+ /* @__PURE__ */ jsx(
326
+ "span",
226
327
  {
328
+ "aria-hidden": true,
227
329
  className: cn(
228
- "flex min-w-[4.25rem] items-end rounded-[1rem] px-3 pb-2.5 md:px-4 md:pb-3",
229
- s.tone === "card" ? "bg-white text-oca-ink" : "bg-oca-ink text-white"
330
+ "pointer-events-none absolute inset-y-0 left-0 w-8 transition-opacity duration-150",
331
+ edges.left ? "opacity-100" : "opacity-0"
230
332
  ),
231
- style: { flexBasis: `${s.pct}%` },
232
- children: /* @__PURE__ */ jsxs("span", { className: "oca-tabular text-[1.25rem] leading-none tracking-[-0.02em] md:text-[1.875rem]", children: [
233
- s.pct,
234
- "%"
235
- ] })
236
- },
237
- i
238
- )) }),
239
- /* @__PURE__ */ jsx("dl", { className: "grid gap-x-8 gap-y-2 sm:grid-cols-2", children: segments.map((s, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2.5", children: [
240
- /* @__PURE__ */ jsxs("dt", { className: "sr-only", children: [
241
- s.pct,
242
- "%"
243
- ] }),
244
- /* @__PURE__ */ jsx(
245
- "span",
246
- {
247
- "aria-hidden": true,
248
- className: cn(
249
- "inline-block size-2.5 shrink-0 translate-y-[-1px] rounded-full",
250
- s.tone === "card" ? "bg-white" : "bg-oca-ink"
251
- )
252
- }
253
- ),
254
- /* @__PURE__ */ jsx("dd", { className: "text-[0.9375rem] leading-snug", children: s.label })
255
- ] }, i)) })
256
- ] });
257
- }
258
- var LABEL = {
259
- yes: "Yes",
260
- testing: "In testing",
261
- "not-yet": "Not yet",
262
- no: "No"
263
- };
264
- var TONE = {
265
- yes: "text-oca-accent-ink",
266
- testing: "text-foreground",
267
- "not-yet": "text-muted-foreground",
268
- no: "text-muted-foreground"
269
- };
270
- function TruthTable({
271
- rows,
272
- statusHeading = "Status",
273
- whatHeading = "What",
274
- className
275
- }) {
276
- return /* @__PURE__ */ jsx("div", { className: cn("overflow-hidden rounded-xl border border-oca-hairline-strong", className), children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem] md:text-[1rem]", children: [
277
- /* @__PURE__ */ jsx("thead", { className: "bg-oca-table-head", children: /* @__PURE__ */ jsxs("tr", { children: [
278
- /* @__PURE__ */ jsx("th", { scope: "col", className: "w-[30%] border-b border-oca-hairline-strong px-4 py-3 font-semibold md:w-[22%]", children: statusHeading }),
279
- /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-l border-oca-hairline-strong px-4 py-3 font-semibold", children: whatHeading })
280
- ] }) }),
281
- /* @__PURE__ */ jsx("tbody", { className: "bg-card", children: rows.map((r, i) => /* @__PURE__ */ jsxs("tr", { className: "align-top", children: [
282
- /* @__PURE__ */ jsx(
283
- "td",
284
- {
285
- className: cn(
286
- "border-t border-oca-hairline-strong px-4 py-3 font-medium",
287
- i === 0 && "border-t-0",
288
- TONE[r.status]
289
- ),
290
- children: LABEL[r.status]
291
- }
292
- ),
293
- /* @__PURE__ */ jsx(
294
- "td",
295
- {
296
- className: cn(
297
- "border-l border-t border-oca-hairline-strong px-4 py-3 leading-relaxed",
298
- i === 0 && "border-t-0"
299
- ),
300
- children: r.what
301
- }
302
- )
303
- ] }, i)) })
304
- ] }) });
305
- }
306
- var INT = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
307
- var COMPACT = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
308
- function satsText(value, opts = {}) {
309
- if (value == null || !Number.isFinite(value)) return null;
310
- const whole = Math.trunc(value);
311
- const body = (opts.compact ? COMPACT : INT).format(Math.abs(whole));
312
- const sign = whole < 0 ? "\u2212" : opts.signed && whole > 0 ? "+" : "";
313
- const unit = opts.unit === "none" ? "" : whole === 1 || whole === -1 ? " sat" : " sats";
314
- return `${sign}${body}${unit}`;
315
- }
316
- function Sats({ value, unit, compact, signed, unknown = "\u2014", className, ...props }) {
317
- const text = satsText(value, { unit, compact, signed });
318
- if (text === null) {
319
- return /* @__PURE__ */ jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), ...props, children: [
320
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: unknown }),
321
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "unknown" })
322
- ] });
323
- }
324
- return /* @__PURE__ */ jsx("span", { className: cn("oca-tabular whitespace-nowrap", className), ...props, children: text });
325
- }
326
- function UsdApprox({
327
- sats,
328
- usdPerBtc,
329
- className
330
- }) {
331
- if (sats == null || usdPerBtc == null || !Number.isFinite(sats) || !Number.isFinite(usdPerBtc)) return null;
332
- const usd = sats / 1e8 * usdPerBtc;
333
- const text = new Intl.NumberFormat("en-US", {
334
- style: "currency",
335
- currency: "USD",
336
- maximumFractionDigits: usd < 1 ? 4 : 2
337
- }).format(usd);
338
- return /* @__PURE__ */ jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), children: [
339
- "\u2248 ",
340
- text
333
+ style: { background: `linear-gradient(to right, ${fade}, transparent)` }
334
+ }
335
+ ),
336
+ /* @__PURE__ */ jsx(
337
+ "span",
338
+ {
339
+ "aria-hidden": true,
340
+ className: cn(
341
+ "pointer-events-none absolute inset-y-0 right-0 w-10 transition-opacity duration-150",
342
+ edges.right ? "opacity-100" : "opacity-0"
343
+ ),
344
+ style: { background: `linear-gradient(to left, ${fade}, transparent)` }
345
+ }
346
+ )
341
347
  ] });
342
348
  }
343
- function SatsFigure({
344
- label,
345
- value,
346
- hint,
347
- className
348
- }) {
349
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
350
- /* @__PURE__ */ jsx("span", { className: "text-[0.9375rem] text-muted-foreground", children: label }),
351
- /* @__PURE__ */ jsxs("span", { className: "oca-display-3 oca-tabular", children: [
352
- /* @__PURE__ */ jsx(Sats, { value, unit: "none" }),
353
- /* @__PURE__ */ jsx("span", { className: "ml-2 text-[0.5em] tracking-normal text-muted-foreground", children: "sats" })
354
- ] }),
355
- hint ? /* @__PURE__ */ jsx("span", { className: "text-[0.875rem] text-muted-foreground", children: hint }) : null
356
- ] });
349
+ var ShellContext = createContext({});
350
+ function useShellPlace() {
351
+ return useContext(ShellContext);
357
352
  }
358
- var TOP = "0,3 2,2 6,4 14,0 16,1 6,6";
359
- var LEFT = "0,3 6,6 6,8 0,5";
360
- var RIGHT = "6,6 16,1 16,3 6,8";
361
- var MARK_FACES = { top: "#FED8AB", left: "#FB933C", right: "#EA580B" };
362
- function Mark({ title, size, className, ...rest }) {
353
+ function Spinner({ className, label }) {
363
354
  return /* @__PURE__ */ jsxs(
364
355
  "svg",
365
356
  {
366
- viewBox: "0 0 16 8",
367
- width: size,
368
- height: size ? size / 2 : void 0,
369
- role: title ? "img" : void 0,
370
- "aria-hidden": title ? void 0 : true,
371
- className: cn("shrink-0", className),
372
- ...rest,
357
+ viewBox: "0 0 24 24",
358
+ fill: "none",
359
+ role: label ? "status" : void 0,
360
+ "aria-label": label,
361
+ "aria-hidden": label ? void 0 : true,
362
+ className: cn("size-4 animate-spin", className),
373
363
  children: [
374
- title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
375
- /* @__PURE__ */ jsx("polygon", { points: LEFT, fill: MARK_FACES.left }),
376
- /* @__PURE__ */ jsx("polygon", { points: RIGHT, fill: MARK_FACES.right }),
377
- /* @__PURE__ */ jsx("polygon", { points: TOP, fill: MARK_FACES.top })
364
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeOpacity: "0.2", strokeWidth: "2.5" }),
365
+ /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 0-9-9", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" })
378
366
  ]
379
367
  }
380
368
  );
381
369
  }
370
+ function Skeleton({ className, ...props }) {
371
+ return /* @__PURE__ */ jsx(
372
+ "div",
373
+ {
374
+ "data-ui": "skeleton",
375
+ className: cn("animate-pulse rounded-lg bg-[var(--oca-skeleton)]", className),
376
+ ...props
377
+ }
378
+ );
379
+ }
380
+ function useTimedOut(active, ms = 12e3) {
381
+ const [timedOut, setTimedOut] = useState(false);
382
+ useEffect(() => {
383
+ const t = setTimeout(() => setTimedOut(true), ms);
384
+ return () => clearTimeout(t);
385
+ }, [active, ms]);
386
+ return timedOut;
387
+ }
388
+ forwardRef(function Separator2({ className, orientation = "horizontal", decorative = true, ...props }, ref) {
389
+ return /* @__PURE__ */ jsx(
390
+ SeparatorPrimitive.Root,
391
+ {
392
+ ref,
393
+ decorative,
394
+ orientation,
395
+ className: cn(
396
+ "shrink-0 bg-border",
397
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
398
+ className
399
+ ),
400
+ ...props
401
+ }
402
+ );
403
+ });
404
+ var buttonVariants = cva(
405
+ [
406
+ "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-full font-medium",
407
+ "transition-[background-color,border-color,color,box-shadow,transform] duration-150",
408
+ "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
409
+ "active:translate-y-px disabled:pointer-events-none",
410
+ // Disabled is one look for every variant: a quiet grey pill, never a
411
+ // faded black or orange slab that reads as broken.
412
+ "disabled:border disabled:border-border disabled:bg-muted disabled:text-oca-subtle disabled:shadow-none",
413
+ "[&_svg]:pointer-events-none [&_svg]:size-[1.1em] [&_svg]:shrink-0"
414
+ ],
415
+ {
416
+ variants: {
417
+ variant: {
418
+ primary: "bg-primary text-primary-foreground hover:bg-primary/85",
419
+ secondary: "border border-border bg-secondary text-secondary-foreground hover:border-oca-hairline-strong hover:bg-accent",
420
+ brand: "bg-brand text-brand-foreground hover:bg-oca-orange-deep",
421
+ ghost: "text-foreground hover:bg-accent",
422
+ danger: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
423
+ link: "h-auto rounded-none px-0 text-foreground underline decoration-border underline-offset-4 hover:decoration-foreground",
424
+ onInk: "bg-white text-oca-ink hover:bg-white/90",
425
+ onInkOutline: "border border-white/20 text-white hover:bg-white/10"
426
+ },
427
+ size: {
428
+ sm: "h-9 px-4 text-sm",
429
+ md: "h-11 px-5 text-[0.9375rem]",
430
+ lg: "h-14 px-7 text-[1.0625rem]",
431
+ icon: "size-10",
432
+ "icon-sm": "size-8"
433
+ }
434
+ },
435
+ compoundVariants: [{ variant: "link", className: "h-auto px-0" }],
436
+ defaultVariants: { variant: "primary", size: "md" }
437
+ }
438
+ );
439
+ var Button = forwardRef(function Button2({ className, variant, size, asChild = false, loading = false, disabled, children, type, ...props }, ref) {
440
+ const Comp = asChild ? Slot : "button";
441
+ return /* @__PURE__ */ jsx(
442
+ Comp,
443
+ {
444
+ ref,
445
+ "data-ui": "button",
446
+ className: cn(buttonVariants({ variant, size }), className),
447
+ disabled: asChild ? void 0 : disabled || loading,
448
+ "aria-busy": loading || void 0,
449
+ type: asChild ? void 0 : type ?? "button",
450
+ ...props,
451
+ children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
452
+ loading ? /* @__PURE__ */ jsx(Spinner, { className: "size-[1.1em]" }) : null,
453
+ children
454
+ ] })
455
+ }
456
+ );
457
+ });
382
458
  function PageHeader({
383
459
  eyebrow,
384
460
  title,
@@ -386,29 +462,117 @@ function PageHeader({
386
462
  actions,
387
463
  className
388
464
  }) {
465
+ const place = useShellPlace();
466
+ const eb = eyebrow === void 0 ? place.section : eyebrow;
389
467
  return /* @__PURE__ */ jsxs("header", { className: cn("flex flex-col gap-5 md:flex-row md:items-end md:justify-between", className), children: [
390
- /* @__PURE__ */ jsxs("div", { className: "flex max-w-[44rem] flex-col gap-3", children: [
391
- eyebrow ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow", children: eyebrow }) : null,
468
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 max-w-[44rem] flex-col gap-2.5", children: [
469
+ eb ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow", children: eb }) : null,
392
470
  /* @__PURE__ */ jsx("h1", { className: "oca-display-3", children: title }),
393
- description ? /* @__PURE__ */ jsx("p", { className: "oca-body text-[1.0625rem]", children: description }) : null
471
+ description ? /* @__PURE__ */ jsx("p", { className: "oca-body max-w-[40rem] text-[1.0625rem]", children: description }) : null
394
472
  ] }),
395
- actions ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 flex-wrap items-center gap-3", children: actions }) : null
473
+ actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2.5 md:shrink-0 md:justify-end", children: actions }) : null
396
474
  ] });
397
475
  }
398
- function StatCard({
399
- label,
400
- value,
401
- hint,
402
- className
403
- }) {
404
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-2 rounded-[1.25rem] border border-border bg-card p-5 md:p-6", className), children: [
405
- /* @__PURE__ */ jsx("span", { className: "text-[0.875rem] text-muted-foreground", children: label }),
406
- /* @__PURE__ */ jsx("span", { className: "oca-tabular text-[1.75rem] leading-tight tracking-[-0.02em]", children: value }),
407
- hint ? /* @__PURE__ */ jsx("span", { className: "text-[0.8125rem] leading-snug text-muted-foreground", children: hint }) : null
408
- ] });
476
+ var InStatGrid = createContext(false);
477
+ function StatCard({ label, value, hint, className }) {
478
+ const inGrid = useContext(InStatGrid);
479
+ return /* @__PURE__ */ jsxs(
480
+ "div",
481
+ {
482
+ "data-ui": "stat",
483
+ className: cn(
484
+ "rounded-[1.25rem] border border-border bg-card p-5 md:p-6",
485
+ // In a grid, label, value and hint each share a row with their
486
+ // neighbours, so values line up whatever wraps above them.
487
+ inGrid ? "row-span-3 grid grid-rows-subgrid gap-1.5" : "flex flex-col gap-2",
488
+ className
489
+ ),
490
+ children: [
491
+ /* @__PURE__ */ jsx("span", { className: "text-[0.875rem] leading-snug text-muted-foreground", children: label }),
492
+ /* @__PURE__ */ jsx("span", { className: "self-end break-words oca-tabular text-[1.5rem] leading-tight tracking-[-0.02em] md:text-[1.75rem]", children: value }),
493
+ hint ? /* @__PURE__ */ jsx("span", { className: "text-[0.8125rem] leading-snug text-muted-foreground", children: hint }) : inGrid ? /* @__PURE__ */ jsx("span", { "aria-hidden": true }) : null
494
+ ]
495
+ }
496
+ );
497
+ }
498
+ var MD_COLS = {
499
+ 1: "md:grid-cols-1",
500
+ 2: "md:grid-cols-2",
501
+ 3: "md:grid-cols-3",
502
+ 4: "md:grid-cols-4"
503
+ };
504
+ var LG_COLS = {
505
+ 1: "lg:grid-cols-1",
506
+ 2: "lg:grid-cols-2",
507
+ 3: "lg:grid-cols-3",
508
+ 4: "lg:grid-cols-4",
509
+ 5: "lg:grid-cols-5"
510
+ };
511
+ var MD_SPAN = {
512
+ 1: "md:col-span-1",
513
+ 2: "md:col-span-2",
514
+ 3: "md:col-span-3",
515
+ 4: "md:col-span-4"
516
+ };
517
+ var LG_SPAN = {
518
+ 1: "lg:col-span-1",
519
+ 2: "lg:col-span-2",
520
+ 3: "lg:col-span-3",
521
+ 4: "lg:col-span-4"
522
+ };
523
+ function statColumns(n) {
524
+ if (n <= 5) return Math.max(1, n);
525
+ if (n % 4 === 0) return 4;
526
+ if (n % 3 === 0) return 3;
527
+ return 4;
409
528
  }
410
- function StatGrid({ className, ...props }) {
411
- return /* @__PURE__ */ jsx("div", { className: cn("grid grid-cols-2 gap-3 md:grid-cols-4 md:gap-4", className), ...props });
529
+ function lastSpan(n, cols) {
530
+ const rest = n % cols;
531
+ return rest === 0 ? 1 : cols - rest + 1;
532
+ }
533
+ function EmptyLine({ children, className }) {
534
+ return /* @__PURE__ */ jsx(
535
+ "p",
536
+ {
537
+ className: cn(
538
+ "rounded-[1.25rem] border border-dashed border-oca-hairline-strong px-5 py-4 text-[0.9375rem] text-muted-foreground",
539
+ className
540
+ ),
541
+ children
542
+ }
543
+ );
544
+ }
545
+ function StatGrid({ columns, empty, isEmpty, className, children, ...props }) {
546
+ const items = Children.toArray(children).filter(isValidElement);
547
+ const n = items.length;
548
+ const allZero = n > 0 && items.every((el) => "raw" in el.props && (el.props.raw === 0 || el.props.raw === null));
549
+ if (isEmpty ?? allZero) return /* @__PURE__ */ jsx(EmptyLine, { className, children: empty ?? "Nothing here yet." });
550
+ const lg = columns ?? statColumns(n);
551
+ const md = lg > 4 ? 3 : lg;
552
+ const mdSpan = lastSpan(n, md);
553
+ const lgSpan = lastSpan(n, lg);
554
+ return /* @__PURE__ */ jsx(InStatGrid.Provider, { value: true, children: /* @__PURE__ */ jsx(
555
+ "div",
556
+ {
557
+ className: cn(
558
+ "grid gap-3 md:gap-4",
559
+ n === 1 ? "grid-cols-1" : "grid-cols-2",
560
+ MD_COLS[md],
561
+ LG_COLS[lg],
562
+ className
563
+ ),
564
+ ...props,
565
+ children: items.map((el, i) => {
566
+ if (i !== n - 1) return el;
567
+ const span = cn(
568
+ n % 2 === 1 && n > 1 && "max-md:col-span-2",
569
+ (mdSpan > 1 || lgSpan > 1) && MD_SPAN[mdSpan],
570
+ (mdSpan > 1 || lgSpan > 1) && LG_SPAN[lgSpan]
571
+ );
572
+ return span ? cloneElement(el, { className: cn(el.props.className, span) }) : el;
573
+ })
574
+ }
575
+ ) });
412
576
  }
413
577
  function KeyValueList({ items, className }) {
414
578
  return /* @__PURE__ */ jsx("dl", { className: cn("divide-y divide-border rounded-[1.25rem] border border-border bg-card", className), children: items.map((it, i) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 px-5 py-3.5 sm:flex-row sm:items-baseline sm:gap-6", children: [
@@ -422,75 +586,103 @@ function DataTable({
422
586
  rowKey,
423
587
  empty,
424
588
  caption,
589
+ phone = "cards",
425
590
  className
426
591
  }) {
427
592
  if (rows.length === 0 && empty) return /* @__PURE__ */ jsx(Fragment, { children: empty });
428
- return /* @__PURE__ */ jsx("div", { className: cn("overflow-x-auto rounded-[1.25rem] border border-border bg-card", className), children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem]", children: [
429
- caption ? /* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }) : null,
430
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border", children: columns.map((c) => /* @__PURE__ */ jsx(
431
- "th",
432
- {
433
- scope: "col",
434
- className: cn(
435
- "whitespace-nowrap px-5 py-3 text-[0.8125rem] font-medium text-muted-foreground",
436
- c.align === "end" && "text-right",
437
- c.wide && "hidden md:table-cell"
438
- ),
439
- children: c.header
440
- },
441
- c.key
442
- )) }) }),
443
- /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-border", children: rows.map((r, i) => /* @__PURE__ */ jsx("tr", { className: "transition-colors hover:bg-accent/60", children: columns.map((c) => /* @__PURE__ */ jsx(
444
- "td",
593
+ const primary = columns.find((c) => c.primary) ?? columns[0];
594
+ const right = (c) => c.numeric || c.align === "end";
595
+ const table = /* @__PURE__ */ jsx(
596
+ "div",
597
+ {
598
+ className: cn(
599
+ "overflow-hidden rounded-[1.25rem] border border-border bg-card",
600
+ phone === "cards" && "max-sm:hidden",
601
+ className
602
+ ),
603
+ children: /* @__PURE__ */ jsx(ScrollFade, { fade: "var(--card)", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem]", children: [
604
+ caption ? /* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }) : null,
605
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border", children: columns.map((c) => /* @__PURE__ */ jsx(
606
+ "th",
607
+ {
608
+ scope: "col",
609
+ style: { width: c.width, minWidth: c.minWidth },
610
+ className: cn(
611
+ "whitespace-nowrap px-5 py-3 text-[0.8125rem] font-medium text-muted-foreground",
612
+ right(c) && "text-right",
613
+ c.wide && "hidden md:table-cell"
614
+ ),
615
+ children: c.header
616
+ },
617
+ c.key
618
+ )) }) }),
619
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-border", children: rows.map((r, i) => /* @__PURE__ */ jsx("tr", { className: "transition-colors hover:bg-accent/60", children: columns.map((c) => /* @__PURE__ */ jsx(
620
+ "td",
621
+ {
622
+ style: { minWidth: c.minWidth },
623
+ className: cn(
624
+ "px-5 py-3.5 align-middle",
625
+ right(c) && "text-right",
626
+ c.numeric && "whitespace-nowrap oca-tabular",
627
+ c.wide && "hidden md:table-cell"
628
+ ),
629
+ children: c.truncate ? /* @__PURE__ */ jsx("div", { className: "truncate", style: { maxWidth: c.width ?? "16rem" }, children: c.cell(r) }) : c.cell(r)
630
+ },
631
+ c.key
632
+ )) }, rowKey(r, i))) })
633
+ ] }) })
634
+ }
635
+ );
636
+ if (phone !== "cards") return table;
637
+ const rest = columns.filter((c) => c !== primary && !c.hideOnPhone);
638
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
639
+ table,
640
+ /* @__PURE__ */ jsx(
641
+ "ul",
445
642
  {
446
- className: cn(
447
- "px-5 py-3.5 align-middle",
448
- c.align === "end" && "text-right",
449
- c.wide && "hidden md:table-cell"
450
- ),
451
- children: c.cell(r)
452
- },
453
- c.key
454
- )) }, rowKey(r, i))) })
455
- ] }) });
643
+ "aria-label": caption,
644
+ className: cn("divide-y divide-border rounded-[1.25rem] border border-border bg-card sm:hidden", className),
645
+ children: rows.map((r, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-2.5 px-4 py-4", children: [
646
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 break-words text-[0.9375rem] font-medium", children: primary?.cell(r) }),
647
+ rest.length > 0 ? /* @__PURE__ */ 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__ */ jsxs("div", { className: "contents", children: [
648
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: c.header }),
649
+ /* @__PURE__ */ jsx("dd", { className: cn("min-w-0 break-words", c.numeric && "oca-tabular"), children: c.cell(r) })
650
+ ] }, c.key)) }) : null
651
+ ] }, rowKey(r, i)))
652
+ }
653
+ )
654
+ ] });
456
655
  }
457
656
  function EmptyState({
458
657
  title,
459
658
  children,
460
659
  action,
660
+ icon,
461
661
  className
462
662
  }) {
463
663
  return /* @__PURE__ */ jsxs(
464
664
  "div",
465
665
  {
466
666
  className: cn(
467
- "flex flex-col items-center gap-4 rounded-[1.25rem] border border-dashed border-oca-hairline-strong px-6 py-12 text-center",
667
+ "flex flex-col items-center gap-3 rounded-[1.25rem] border border-dashed border-oca-hairline-strong px-6 py-10 text-center",
468
668
  className
469
669
  ),
470
670
  children: [
471
- /* @__PURE__ */ jsx(Mark, { size: 56 }),
671
+ icon ? /* @__PURE__ */ 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,
472
672
  /* @__PURE__ */ jsx("h3", { className: "oca-title-sm", children: title }),
473
673
  children ? /* @__PURE__ */ jsx("div", { className: "oca-body max-w-md text-[0.9375rem]", children }) : null,
474
- action ? /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap justify-center gap-3", children: action }) : null
674
+ action ? /* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap justify-center gap-3", children: action }) : null
475
675
  ]
476
676
  }
477
677
  );
478
678
  }
479
- var NOTICE = {
480
- neutral: "border-border bg-card",
481
- info: "border-[color-mix(in_oklab,var(--info)_30%,var(--border))] bg-[color-mix(in_oklab,var(--info)_6%,var(--card))]",
482
- success: "border-[color-mix(in_oklab,var(--success)_30%,var(--border))] bg-[color-mix(in_oklab,var(--success)_6%,var(--card))]",
483
- warning: "border-[color-mix(in_oklab,var(--warning)_35%,var(--border))] bg-[color-mix(in_oklab,var(--warning)_7%,var(--card))]",
484
- danger: "border-[color-mix(in_oklab,var(--destructive)_32%,var(--border))] bg-[color-mix(in_oklab,var(--destructive)_6%,var(--card))]",
485
- brand: "border-[color-mix(in_oklab,var(--oca-orange)_35%,var(--border))] bg-[color-mix(in_oklab,var(--oca-orange)_7%,var(--card))]"
486
- };
487
- var NOTICE_TITLE = {
488
- neutral: "text-foreground",
489
- info: "text-info",
490
- success: "text-success",
491
- warning: "text-warning",
492
- danger: "text-destructive",
493
- brand: "text-oca-accent-ink"
679
+ var NOTICE_KIND = {
680
+ neutral: "neutral",
681
+ info: "neutral",
682
+ success: "neutral",
683
+ brand: "brand",
684
+ warning: "brand",
685
+ danger: "danger"
494
686
  };
495
687
  function Notice({
496
688
  tone = "neutral",
@@ -499,25 +691,60 @@ function Notice({
499
691
  action,
500
692
  className
501
693
  }) {
694
+ const kind = NOTICE_KIND[tone];
502
695
  return /* @__PURE__ */ jsxs(
503
696
  "div",
504
697
  {
505
698
  role: tone === "danger" || tone === "warning" ? "alert" : "note",
699
+ "data-tone": kind,
506
700
  className: cn(
507
- "flex flex-col gap-3 rounded-2xl border px-5 py-4 sm:flex-row sm:items-start sm:justify-between",
508
- NOTICE[tone],
701
+ "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",
702
+ kind === "danger" && "border-[color-mix(in_oklab,var(--destructive)_40%,var(--border))]",
509
703
  className
510
704
  ),
511
705
  children: [
512
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
513
- title ? /* @__PURE__ */ jsx("p", { className: cn("text-[0.9375rem] font-medium", NOTICE_TITLE[tone]), children: title }) : null,
514
- children ? /* @__PURE__ */ jsx("div", { className: "text-[0.9375rem] leading-relaxed text-foreground/85", children }) : null
706
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 gap-3", children: [
707
+ kind !== "neutral" ? /* @__PURE__ */ jsx(
708
+ "span",
709
+ {
710
+ "aria-hidden": true,
711
+ className: cn(
712
+ "mt-[0.45rem] size-2 shrink-0 rounded-full",
713
+ kind === "brand" ? "bg-oca-orange" : "bg-destructive"
714
+ )
715
+ }
716
+ ) : null,
717
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
718
+ title ? /* @__PURE__ */ jsx("p", { className: cn("text-[0.9375rem] font-medium", kind === "danger" && "text-destructive"), children: title }) : null,
719
+ children ? /* @__PURE__ */ jsx("div", { className: "text-[0.9375rem] leading-relaxed text-muted-foreground", children }) : null
720
+ ] })
515
721
  ] }),
516
722
  action ? /* @__PURE__ */ jsx("div", { className: "shrink-0", children: action }) : null
517
723
  ]
518
724
  }
519
725
  );
520
726
  }
727
+ function LoadingBlock({
728
+ rows = 3,
729
+ timeoutMs = 12e3,
730
+ onRetry,
731
+ label = "Loading",
732
+ className
733
+ }) {
734
+ const timedOut = useTimedOut(true, timeoutMs);
735
+ if (timedOut) {
736
+ return /* @__PURE__ */ jsx(
737
+ Notice,
738
+ {
739
+ className,
740
+ title: "This is taking longer than it should.",
741
+ action: /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: onRetry ?? (() => window.location.reload()), children: "Try again" }),
742
+ children: "Check your connection, then try again."
743
+ }
744
+ );
745
+ }
746
+ return /* @__PURE__ */ jsx("div", { role: "status", "aria-label": label, className: cn("flex flex-col gap-3", className), children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-16 rounded-2xl" }, i)) });
747
+ }
521
748
  function middle(v, head, tail) {
522
749
  return v.length <= head + tail + 1 ? v : `${v.slice(0, head)}\u2026${v.slice(-tail)}`;
523
750
  }
@@ -623,7 +850,253 @@ function ActivityList({ items, empty, className }) {
623
850
  return /* @__PURE__ */ jsx("li", { children: it.href ? /* @__PURE__ */ jsx("a", { href: it.href, className: "block transition-colors hover:bg-accent/60", children: body }) : body }, it.id);
624
851
  }) });
625
852
  }
853
+ var KIND = {
854
+ package: "text-oca-code-package",
855
+ tag: "text-oca-code-tag",
856
+ link: "text-oca-code-link"
857
+ };
858
+ function PackageCard({ items, className }) {
859
+ return /* @__PURE__ */ jsx(
860
+ "div",
861
+ {
862
+ className: cn(
863
+ "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",
864
+ className
865
+ ),
866
+ children: items.map((it, i) => {
867
+ const code = /* @__PURE__ */ jsx("span", { className: cn("text-[0.9375rem]", KIND[it.kind ?? "package"]), children: it.code });
868
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
869
+ /* @__PURE__ */ jsx("h3", { className: "text-[1.125rem] font-semibold tracking-[-0.01em] text-oca-ink-title", children: it.title }),
870
+ it.href ? /* @__PURE__ */ jsx("a", { href: it.href, className: "w-fit underline decoration-transparent underline-offset-4 hover:decoration-current", children: code }) : code,
871
+ it.note ? /* @__PURE__ */ jsxs("p", { className: "text-[0.9375rem] leading-relaxed text-oca-ink-subtle", children: [
872
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "// " }),
873
+ it.note
874
+ ] }) : null
875
+ ] }, i);
876
+ })
877
+ }
878
+ );
879
+ }
880
+ function CodeBlock({
881
+ code,
882
+ caption,
883
+ copy = true,
884
+ maxLines,
885
+ className
886
+ }) {
887
+ const lines = code.split("\n").length;
888
+ const foldable = maxLines !== void 0 && lines > maxLines + 2;
889
+ const [open, setOpen] = useState(false);
890
+ const folded = foldable && !open;
891
+ return /* @__PURE__ */ jsxs(
892
+ "figure",
893
+ {
894
+ className: cn(
895
+ "overflow-hidden rounded-[1.25rem] border border-[var(--oca-code-line)] bg-[var(--oca-code-surface)]",
896
+ className
897
+ ),
898
+ children: [
899
+ /* @__PURE__ */ jsxs("figcaption", { className: "flex h-11 items-center justify-between gap-3 border-b border-[var(--oca-code-line)] pl-5 pr-2", children: [
900
+ /* @__PURE__ */ jsx("span", { className: "truncate text-[0.8125rem] text-oca-ink-subtle", children: caption ?? "Code" }),
901
+ copy ? /* @__PURE__ */ jsx(
902
+ CopyButton,
903
+ {
904
+ value: code,
905
+ label: "Copy code",
906
+ className: "text-oca-ink-subtle hover:bg-white/10 hover:text-white"
907
+ }
908
+ ) : null
909
+ ] }),
910
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
911
+ /* @__PURE__ */ jsx(
912
+ "div",
913
+ {
914
+ className: cn(folded && "overflow-hidden"),
915
+ style: folded ? { maxHeight: `calc(${maxLines} * 1.625em + 2.5rem)` } : void 0,
916
+ children: /* @__PURE__ */ jsx(ScrollFade, { fade: "var(--oca-code-surface)", children: /* @__PURE__ */ jsx("pre", { className: "w-max min-w-full p-5 font-mono text-[0.875rem] leading-[1.625] text-oca-ink-muted", children: /* @__PURE__ */ jsx("code", { children: code }) }) })
917
+ }
918
+ ),
919
+ foldable ? /* @__PURE__ */ jsx(
920
+ "div",
921
+ {
922
+ className: cn(
923
+ "flex justify-center px-5 pb-4",
924
+ folded && "absolute inset-x-0 bottom-0 pt-16"
925
+ ),
926
+ style: folded ? { background: "linear-gradient(to top, var(--oca-code-surface) 40%, transparent)" } : void 0,
927
+ children: /* @__PURE__ */ jsx(
928
+ "button",
929
+ {
930
+ type: "button",
931
+ onClick: () => setOpen((v) => !v),
932
+ className: "rounded-full border border-white/20 px-4 py-1.5 text-[0.8125rem] text-white transition-colors hover:bg-white/10",
933
+ children: open ? "Show less" : `Show all ${lines} lines`
934
+ }
935
+ )
936
+ }
937
+ ) : null
938
+ ] })
939
+ ]
940
+ }
941
+ );
942
+ }
943
+ function SplitBar({
944
+ segments,
945
+ caption,
946
+ className
947
+ }) {
948
+ return /* @__PURE__ */ jsxs("figure", { className: cn("flex flex-col gap-5", className), children: [
949
+ caption ? /* @__PURE__ */ jsx("figcaption", { className: "text-[0.9375rem]", children: caption }) : null,
950
+ /* @__PURE__ */ 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__ */ jsx(
951
+ "div",
952
+ {
953
+ className: cn(
954
+ "flex min-w-[4.25rem] items-end rounded-[1rem] px-3 pb-2.5 md:px-4 md:pb-3",
955
+ s.tone === "card" ? "bg-white text-oca-ink" : "bg-oca-ink text-white"
956
+ ),
957
+ style: { flexBasis: `${s.pct}%` },
958
+ children: /* @__PURE__ */ jsxs("span", { className: "oca-tabular text-[1.25rem] leading-none tracking-[-0.02em] md:text-[1.875rem]", children: [
959
+ s.pct,
960
+ "%"
961
+ ] })
962
+ },
963
+ i
964
+ )) }),
965
+ /* @__PURE__ */ jsx("dl", { className: "grid gap-x-8 gap-y-2 sm:grid-cols-2", children: segments.map((s, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2.5", children: [
966
+ /* @__PURE__ */ jsxs("dt", { className: "sr-only", children: [
967
+ s.pct,
968
+ "%"
969
+ ] }),
970
+ /* @__PURE__ */ jsx(
971
+ "span",
972
+ {
973
+ "aria-hidden": true,
974
+ className: cn(
975
+ "inline-block size-2.5 shrink-0 translate-y-[-1px] rounded-full",
976
+ s.tone === "card" ? "bg-white" : "bg-oca-ink"
977
+ )
978
+ }
979
+ ),
980
+ /* @__PURE__ */ jsx("dd", { className: "text-[0.9375rem] leading-snug", children: s.label })
981
+ ] }, i)) })
982
+ ] });
983
+ }
984
+ var LABEL = {
985
+ yes: "Yes",
986
+ testing: "In testing",
987
+ "not-yet": "Not yet",
988
+ no: "No"
989
+ };
990
+ var TONE = {
991
+ yes: "text-oca-accent-ink",
992
+ testing: "text-foreground",
993
+ "not-yet": "text-muted-foreground",
994
+ no: "text-muted-foreground"
995
+ };
996
+ function TruthTable({
997
+ rows,
998
+ statusHeading = "Status",
999
+ whatHeading = "What",
1000
+ className
1001
+ }) {
1002
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1003
+ /* @__PURE__ */ jsx(
1004
+ "ul",
1005
+ {
1006
+ className: cn(
1007
+ "divide-y divide-oca-hairline-strong overflow-hidden rounded-xl border border-oca-hairline-strong bg-card sm:hidden",
1008
+ className
1009
+ ),
1010
+ children: rows.map((r, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-1.5 px-4 py-3.5", children: [
1011
+ /* @__PURE__ */ jsx("span", { className: cn("text-[0.875rem] font-medium", TONE[r.status]), children: LABEL[r.status] }),
1012
+ /* @__PURE__ */ jsx("span", { className: "text-[0.9375rem] leading-relaxed", children: r.what })
1013
+ ] }, i))
1014
+ }
1015
+ ),
1016
+ /* @__PURE__ */ jsx("div", { className: cn("overflow-hidden rounded-xl border border-oca-hairline-strong max-sm:hidden", className), children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem] md:text-[1rem]", children: [
1017
+ /* @__PURE__ */ jsx("thead", { className: "bg-oca-table-head", children: /* @__PURE__ */ jsxs("tr", { children: [
1018
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "w-[30%] border-b border-oca-hairline-strong px-4 py-3 font-semibold md:w-[22%]", children: statusHeading }),
1019
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-l border-oca-hairline-strong px-4 py-3 font-semibold", children: whatHeading })
1020
+ ] }) }),
1021
+ /* @__PURE__ */ jsx("tbody", { className: "bg-card", children: rows.map((r, i) => /* @__PURE__ */ jsxs("tr", { className: "align-top", children: [
1022
+ /* @__PURE__ */ jsx(
1023
+ "td",
1024
+ {
1025
+ className: cn(
1026
+ "border-t border-oca-hairline-strong px-4 py-3 font-medium",
1027
+ i === 0 && "border-t-0",
1028
+ TONE[r.status]
1029
+ ),
1030
+ children: LABEL[r.status]
1031
+ }
1032
+ ),
1033
+ /* @__PURE__ */ jsx(
1034
+ "td",
1035
+ {
1036
+ className: cn(
1037
+ "border-l border-t border-oca-hairline-strong px-4 py-3 leading-relaxed",
1038
+ i === 0 && "border-t-0"
1039
+ ),
1040
+ children: r.what
1041
+ }
1042
+ )
1043
+ ] }, i)) })
1044
+ ] }) })
1045
+ ] });
1046
+ }
1047
+ var INT = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
1048
+ var COMPACT = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
1049
+ function satsText(value, opts = {}) {
1050
+ if (value == null || !Number.isFinite(value)) return null;
1051
+ const whole = Math.trunc(value);
1052
+ const body = (opts.compact ? COMPACT : INT).format(Math.abs(whole));
1053
+ const sign = whole < 0 ? "\u2212" : opts.signed && whole > 0 ? "+" : "";
1054
+ const unit = opts.unit === "none" ? "" : whole === 1 || whole === -1 ? " sat" : " sats";
1055
+ return `${sign}${body}${unit}`;
1056
+ }
1057
+ function Sats({ value, unit, compact, signed, unknown = "\u2014", className, ...props }) {
1058
+ const text = satsText(value, { unit, compact, signed });
1059
+ if (text === null) {
1060
+ return /* @__PURE__ */ jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), ...props, children: [
1061
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: unknown }),
1062
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "unknown" })
1063
+ ] });
1064
+ }
1065
+ return /* @__PURE__ */ jsx("span", { className: cn("oca-tabular whitespace-nowrap", className), ...props, children: text });
1066
+ }
1067
+ function UsdApprox({
1068
+ sats,
1069
+ usdPerBtc,
1070
+ className
1071
+ }) {
1072
+ if (sats == null || usdPerBtc == null || !Number.isFinite(sats) || !Number.isFinite(usdPerBtc)) return null;
1073
+ const usd = sats / 1e8 * usdPerBtc;
1074
+ const text = new Intl.NumberFormat("en-US", {
1075
+ style: "currency",
1076
+ currency: "USD",
1077
+ maximumFractionDigits: usd < 1 ? 4 : 2
1078
+ }).format(usd);
1079
+ return /* @__PURE__ */ jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), children: [
1080
+ "\u2248 ",
1081
+ text
1082
+ ] });
1083
+ }
1084
+ function SatsFigure({
1085
+ label,
1086
+ value,
1087
+ hint,
1088
+ className
1089
+ }) {
1090
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
1091
+ /* @__PURE__ */ jsx("span", { className: "text-[0.9375rem] text-muted-foreground", children: label }),
1092
+ /* @__PURE__ */ jsxs("span", { className: "oca-display-3 oca-tabular", children: [
1093
+ /* @__PURE__ */ jsx(Sats, { value, unit: "none" }),
1094
+ /* @__PURE__ */ jsx("span", { className: "ml-2 text-[0.5em] tracking-normal text-muted-foreground", children: "sats" })
1095
+ ] }),
1096
+ hint ? /* @__PURE__ */ jsx("span", { className: "text-[0.875rem] text-muted-foreground", children: hint }) : null
1097
+ ] });
1098
+ }
626
1099
 
627
- export { Accent, ActivityList, CodeBlock, Container, CopyButton, DataTable, Display, EmptyState, Eyebrow, Footnote, HashChip, KeyValueList, Lead, Notice, PackageCard, PageHeader, ProgressSteps, Prose, Sats, SatsFigure, Section, SectionIntro, SplitBar, StatCard, StatGrid, StepGrid, TruthTable, UsdApprox, satsText, sectionVariants, useCopy };
1100
+ export { Accent, ActivityList, CodeBlock, Container, CopyButton, DataTable, Display, DocHeader, DocLayout, EmptyLine, EmptyState, Eyebrow, Footnote, HashChip, KeyValueList, Lead, LoadingBlock, Notice, PackageCard, PageHeader, PageHero, ProgressSteps, Prose, Sats, SatsFigure, Section, SectionIntro, SplitBar, StatCard, StatGrid, StepGrid, TruthTable, UsdApprox, satsText, sectionVariants, useCopy };
628
1101
  //# sourceMappingURL=components.mjs.map
629
1102
  //# sourceMappingURL=components.mjs.map