@orangecheck/app-design 0.2.1 → 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.
@@ -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) 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,160 @@ 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
+ const ro = new ResizeObserver(measure);
318
+ ro.observe(el);
319
+ if (el.firstElementChild) ro.observe(el.firstElementChild);
320
+ return () => ro.disconnect();
321
+ }, [measure]);
322
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative min-w-0", className), ...props, children: [
323
+ /* @__PURE__ */ jsx("div", { ref, onScroll: measure, className: cn("overflow-x-auto overscroll-x-contain", innerClassName), children }),
324
+ /* @__PURE__ */ jsx(
325
+ "span",
226
326
  {
327
+ "aria-hidden": true,
227
328
  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"
329
+ "pointer-events-none absolute inset-y-0 left-0 w-8 transition-opacity duration-150",
330
+ edges.left ? "opacity-100" : "opacity-0"
230
331
  ),
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
332
+ style: { background: `linear-gradient(to right, ${fade}, transparent)` }
333
+ }
334
+ ),
335
+ /* @__PURE__ */ jsx(
336
+ "span",
337
+ {
338
+ "aria-hidden": true,
339
+ className: cn(
340
+ "pointer-events-none absolute inset-y-0 right-0 w-10 transition-opacity duration-150",
341
+ edges.right ? "opacity-100" : "opacity-0"
342
+ ),
343
+ style: { background: `linear-gradient(to left, ${fade}, transparent)` }
344
+ }
345
+ )
341
346
  ] });
342
347
  }
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
- ] });
348
+ var ShellContext = createContext({});
349
+ function useShellPlace() {
350
+ return useContext(ShellContext);
357
351
  }
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 }) {
352
+ function Spinner({ className, label }) {
363
353
  return /* @__PURE__ */ jsxs(
364
354
  "svg",
365
355
  {
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,
356
+ viewBox: "0 0 24 24",
357
+ fill: "none",
358
+ role: label ? "status" : void 0,
359
+ "aria-label": label,
360
+ "aria-hidden": label ? void 0 : true,
361
+ className: cn("size-4 animate-spin", className),
373
362
  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 })
363
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeOpacity: "0.2", strokeWidth: "2.5" }),
364
+ /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 0-9-9", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" })
378
365
  ]
379
366
  }
380
367
  );
381
368
  }
369
+ function Skeleton({ className, ...props }) {
370
+ return /* @__PURE__ */ jsx(
371
+ "div",
372
+ {
373
+ "data-ui": "skeleton",
374
+ className: cn("animate-pulse rounded-lg bg-[var(--oca-skeleton)]", className),
375
+ ...props
376
+ }
377
+ );
378
+ }
379
+ function useTimedOut(active, ms = 12e3) {
380
+ const [timedOut, setTimedOut] = useState(false);
381
+ useEffect(() => {
382
+ const t = setTimeout(() => setTimedOut(true), ms);
383
+ return () => clearTimeout(t);
384
+ }, [active, ms]);
385
+ return timedOut;
386
+ }
387
+ forwardRef(function Separator2({ className, orientation = "horizontal", decorative = true, ...props }, ref) {
388
+ return /* @__PURE__ */ jsx(
389
+ SeparatorPrimitive.Root,
390
+ {
391
+ ref,
392
+ decorative,
393
+ orientation,
394
+ className: cn(
395
+ "shrink-0 bg-border",
396
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
397
+ className
398
+ ),
399
+ ...props
400
+ }
401
+ );
402
+ });
403
+ var buttonVariants = cva(
404
+ [
405
+ "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-full font-medium",
406
+ "transition-[background-color,border-color,color,box-shadow,transform] duration-150",
407
+ "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
408
+ "active:translate-y-px disabled:pointer-events-none",
409
+ // Disabled is one look for every variant: a quiet grey pill, never a
410
+ // faded black or orange slab that reads as broken.
411
+ "disabled:border disabled:border-border disabled:bg-muted disabled:text-oca-subtle disabled:shadow-none",
412
+ "[&_svg]:pointer-events-none [&_svg]:size-[1.1em] [&_svg]:shrink-0"
413
+ ],
414
+ {
415
+ variants: {
416
+ variant: {
417
+ primary: "bg-primary text-primary-foreground hover:bg-primary/85",
418
+ secondary: "border border-border bg-secondary text-secondary-foreground hover:border-oca-hairline-strong hover:bg-accent",
419
+ brand: "bg-brand text-brand-foreground hover:bg-oca-orange-deep",
420
+ ghost: "text-foreground hover:bg-accent",
421
+ danger: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
422
+ link: "h-auto rounded-none px-0 text-foreground underline decoration-border underline-offset-4 hover:decoration-foreground",
423
+ onInk: "bg-white text-oca-ink hover:bg-white/90",
424
+ onInkOutline: "border border-white/20 text-white hover:bg-white/10"
425
+ },
426
+ size: {
427
+ sm: "h-9 px-4 text-sm",
428
+ md: "h-11 px-5 text-[0.9375rem]",
429
+ lg: "h-14 px-7 text-[1.0625rem]",
430
+ icon: "size-10",
431
+ "icon-sm": "size-8"
432
+ }
433
+ },
434
+ compoundVariants: [{ variant: "link", className: "h-auto px-0" }],
435
+ defaultVariants: { variant: "primary", size: "md" }
436
+ }
437
+ );
438
+ var Button = forwardRef(function Button2({ className, variant, size, asChild = false, loading = false, disabled, children, type, ...props }, ref) {
439
+ const Comp = asChild ? Slot : "button";
440
+ return /* @__PURE__ */ jsx(
441
+ Comp,
442
+ {
443
+ ref,
444
+ "data-ui": "button",
445
+ className: cn(buttonVariants({ variant, size }), className),
446
+ disabled: asChild ? void 0 : disabled || loading,
447
+ "aria-busy": loading || void 0,
448
+ type: asChild ? void 0 : type ?? "button",
449
+ ...props,
450
+ children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
451
+ loading ? /* @__PURE__ */ jsx(Spinner, { className: "size-[1.1em]" }) : null,
452
+ children
453
+ ] })
454
+ }
455
+ );
456
+ });
382
457
  function PageHeader({
383
458
  eyebrow,
384
459
  title,
@@ -386,29 +461,117 @@ function PageHeader({
386
461
  actions,
387
462
  className
388
463
  }) {
464
+ const place = useShellPlace();
465
+ const eb = eyebrow === void 0 ? place.section : eyebrow;
389
466
  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,
467
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 max-w-[44rem] flex-col gap-2.5", children: [
468
+ eb ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow", children: eb }) : null,
392
469
  /* @__PURE__ */ jsx("h1", { className: "oca-display-3", children: title }),
393
- description ? /* @__PURE__ */ jsx("p", { className: "oca-body text-[1.0625rem]", children: description }) : null
470
+ description ? /* @__PURE__ */ jsx("p", { className: "oca-body max-w-[40rem] text-[1.0625rem]", children: description }) : null
394
471
  ] }),
395
- actions ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 flex-wrap items-center gap-3", children: actions }) : null
472
+ actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2.5 md:shrink-0 md:justify-end", children: actions }) : null
396
473
  ] });
397
474
  }
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
- ] });
475
+ var InStatGrid = createContext(false);
476
+ function StatCard({ label, value, hint, className }) {
477
+ const inGrid = useContext(InStatGrid);
478
+ return /* @__PURE__ */ jsxs(
479
+ "div",
480
+ {
481
+ "data-ui": "stat",
482
+ className: cn(
483
+ "rounded-[1.25rem] border border-border bg-card p-5 md:p-6",
484
+ // In a grid, label, value and hint each share a row with their
485
+ // neighbours, so values line up whatever wraps above them.
486
+ inGrid ? "row-span-3 grid grid-rows-subgrid gap-1.5" : "flex flex-col gap-2",
487
+ className
488
+ ),
489
+ children: [
490
+ /* @__PURE__ */ jsx("span", { className: "text-[0.875rem] leading-snug text-muted-foreground", children: label }),
491
+ /* @__PURE__ */ jsx("span", { className: "self-end break-words oca-tabular text-[1.5rem] leading-tight tracking-[-0.02em] md:text-[1.75rem]", children: value }),
492
+ hint ? /* @__PURE__ */ jsx("span", { className: "text-[0.8125rem] leading-snug text-muted-foreground", children: hint }) : inGrid ? /* @__PURE__ */ jsx("span", { "aria-hidden": true }) : null
493
+ ]
494
+ }
495
+ );
496
+ }
497
+ var MD_COLS = {
498
+ 1: "md:grid-cols-1",
499
+ 2: "md:grid-cols-2",
500
+ 3: "md:grid-cols-3",
501
+ 4: "md:grid-cols-4"
502
+ };
503
+ var LG_COLS = {
504
+ 1: "lg:grid-cols-1",
505
+ 2: "lg:grid-cols-2",
506
+ 3: "lg:grid-cols-3",
507
+ 4: "lg:grid-cols-4",
508
+ 5: "lg:grid-cols-5"
509
+ };
510
+ var MD_SPAN = {
511
+ 1: "md:col-span-1",
512
+ 2: "md:col-span-2",
513
+ 3: "md:col-span-3",
514
+ 4: "md:col-span-4"
515
+ };
516
+ var LG_SPAN = {
517
+ 1: "lg:col-span-1",
518
+ 2: "lg:col-span-2",
519
+ 3: "lg:col-span-3",
520
+ 4: "lg:col-span-4"
521
+ };
522
+ function statColumns(n) {
523
+ if (n <= 5) return Math.max(1, n);
524
+ if (n % 4 === 0) return 4;
525
+ if (n % 3 === 0) return 3;
526
+ return 4;
409
527
  }
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 });
528
+ function lastSpan(n, cols) {
529
+ const rest = n % cols;
530
+ return rest === 0 ? 1 : cols - rest + 1;
531
+ }
532
+ function EmptyLine({ children, className }) {
533
+ return /* @__PURE__ */ jsx(
534
+ "p",
535
+ {
536
+ className: cn(
537
+ "rounded-[1.25rem] border border-dashed border-oca-hairline-strong px-5 py-4 text-[0.9375rem] text-muted-foreground",
538
+ className
539
+ ),
540
+ children
541
+ }
542
+ );
543
+ }
544
+ function StatGrid({ columns, empty, isEmpty, className, children, ...props }) {
545
+ const items = Children.toArray(children).filter(isValidElement);
546
+ const n = items.length;
547
+ const allZero = n > 0 && items.every((el) => "raw" in el.props && (el.props.raw === 0 || el.props.raw === null));
548
+ if (isEmpty ?? allZero) return /* @__PURE__ */ jsx(EmptyLine, { className, children: empty ?? "Nothing here yet." });
549
+ const lg = columns ?? statColumns(n);
550
+ const md = lg > 4 ? 3 : lg;
551
+ const mdSpan = lastSpan(n, md);
552
+ const lgSpan = lastSpan(n, lg);
553
+ return /* @__PURE__ */ jsx(InStatGrid.Provider, { value: true, children: /* @__PURE__ */ jsx(
554
+ "div",
555
+ {
556
+ className: cn(
557
+ "grid gap-3 md:gap-4",
558
+ n === 1 ? "grid-cols-1" : "grid-cols-2",
559
+ MD_COLS[md],
560
+ LG_COLS[lg],
561
+ className
562
+ ),
563
+ ...props,
564
+ children: items.map((el, i) => {
565
+ if (i !== n - 1) return el;
566
+ const span = cn(
567
+ n % 2 === 1 && n > 1 && "max-md:col-span-2",
568
+ (mdSpan > 1 || lgSpan > 1) && MD_SPAN[mdSpan],
569
+ (mdSpan > 1 || lgSpan > 1) && LG_SPAN[lgSpan]
570
+ );
571
+ return span ? cloneElement(el, { className: cn(el.props.className, span) }) : el;
572
+ })
573
+ }
574
+ ) });
412
575
  }
413
576
  function KeyValueList({ items, className }) {
414
577
  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 +585,103 @@ function DataTable({
422
585
  rowKey,
423
586
  empty,
424
587
  caption,
588
+ phone = "cards",
425
589
  className
426
590
  }) {
427
591
  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",
592
+ const primary = columns.find((c) => c.primary) ?? columns[0];
593
+ const right = (c) => c.numeric || c.align === "end";
594
+ const table = /* @__PURE__ */ jsx(
595
+ "div",
596
+ {
597
+ className: cn(
598
+ "overflow-hidden rounded-[1.25rem] border border-border bg-card",
599
+ phone === "cards" && "max-sm:hidden",
600
+ className
601
+ ),
602
+ children: /* @__PURE__ */ jsx(ScrollFade, { fade: "var(--card)", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left text-[0.9375rem]", children: [
603
+ caption ? /* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }) : null,
604
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border", children: columns.map((c) => /* @__PURE__ */ jsx(
605
+ "th",
606
+ {
607
+ scope: "col",
608
+ style: { width: c.width, minWidth: c.minWidth },
609
+ className: cn(
610
+ "whitespace-nowrap px-5 py-3 text-[0.8125rem] font-medium text-muted-foreground",
611
+ right(c) && "text-right",
612
+ c.wide && "hidden md:table-cell"
613
+ ),
614
+ children: c.header
615
+ },
616
+ c.key
617
+ )) }) }),
618
+ /* @__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(
619
+ "td",
620
+ {
621
+ style: { minWidth: c.minWidth },
622
+ className: cn(
623
+ "px-5 py-3.5 align-middle",
624
+ right(c) && "text-right",
625
+ c.numeric && "whitespace-nowrap oca-tabular",
626
+ c.wide && "hidden md:table-cell"
627
+ ),
628
+ children: c.truncate ? /* @__PURE__ */ jsx("div", { className: "truncate", style: { maxWidth: c.width ?? "16rem" }, children: c.cell(r) }) : c.cell(r)
629
+ },
630
+ c.key
631
+ )) }, rowKey(r, i))) })
632
+ ] }) })
633
+ }
634
+ );
635
+ if (phone !== "cards") return table;
636
+ const rest = columns.filter((c) => c !== primary && !c.hideOnPhone);
637
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
638
+ table,
639
+ /* @__PURE__ */ jsx(
640
+ "ul",
445
641
  {
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
- ] }) });
642
+ "aria-label": caption,
643
+ className: cn("divide-y divide-border rounded-[1.25rem] border border-border bg-card sm:hidden", className),
644
+ children: rows.map((r, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-2.5 px-4 py-4", children: [
645
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 break-words text-[0.9375rem] font-medium", children: primary?.cell(r) }),
646
+ 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: [
647
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: c.header }),
648
+ /* @__PURE__ */ jsx("dd", { className: cn("min-w-0 break-words", c.numeric && "oca-tabular"), children: c.cell(r) })
649
+ ] }, c.key)) }) : null
650
+ ] }, rowKey(r, i)))
651
+ }
652
+ )
653
+ ] });
456
654
  }
457
655
  function EmptyState({
458
656
  title,
459
657
  children,
460
658
  action,
659
+ icon,
461
660
  className
462
661
  }) {
463
662
  return /* @__PURE__ */ jsxs(
464
663
  "div",
465
664
  {
466
665
  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",
666
+ "flex flex-col items-center gap-3 rounded-[1.25rem] border border-dashed border-oca-hairline-strong px-6 py-10 text-center",
468
667
  className
469
668
  ),
470
669
  children: [
471
- /* @__PURE__ */ jsx(Mark, { size: 56 }),
670
+ 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
671
  /* @__PURE__ */ jsx("h3", { className: "oca-title-sm", children: title }),
473
672
  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
673
+ action ? /* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap justify-center gap-3", children: action }) : null
475
674
  ]
476
675
  }
477
676
  );
478
677
  }
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"
678
+ var NOTICE_KIND = {
679
+ neutral: "neutral",
680
+ info: "neutral",
681
+ success: "neutral",
682
+ brand: "brand",
683
+ warning: "brand",
684
+ danger: "danger"
494
685
  };
495
686
  function Notice({
496
687
  tone = "neutral",
@@ -499,25 +690,60 @@ function Notice({
499
690
  action,
500
691
  className
501
692
  }) {
693
+ const kind = NOTICE_KIND[tone];
502
694
  return /* @__PURE__ */ jsxs(
503
695
  "div",
504
696
  {
505
697
  role: tone === "danger" || tone === "warning" ? "alert" : "note",
698
+ "data-tone": kind,
506
699
  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],
700
+ "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",
701
+ kind === "danger" && "border-[color-mix(in_oklab,var(--destructive)_40%,var(--border))]",
509
702
  className
510
703
  ),
511
704
  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
705
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 gap-3", children: [
706
+ kind !== "neutral" ? /* @__PURE__ */ jsx(
707
+ "span",
708
+ {
709
+ "aria-hidden": true,
710
+ className: cn(
711
+ "mt-[0.45rem] size-2 shrink-0 rounded-full",
712
+ kind === "brand" ? "bg-oca-orange" : "bg-destructive"
713
+ )
714
+ }
715
+ ) : null,
716
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
717
+ title ? /* @__PURE__ */ jsx("p", { className: cn("text-[0.9375rem] font-medium", kind === "danger" && "text-destructive"), children: title }) : null,
718
+ children ? /* @__PURE__ */ jsx("div", { className: "text-[0.9375rem] leading-relaxed text-muted-foreground", children }) : null
719
+ ] })
515
720
  ] }),
516
721
  action ? /* @__PURE__ */ jsx("div", { className: "shrink-0", children: action }) : null
517
722
  ]
518
723
  }
519
724
  );
520
725
  }
726
+ function LoadingBlock({
727
+ rows = 3,
728
+ timeoutMs = 12e3,
729
+ onRetry,
730
+ label = "Loading",
731
+ className
732
+ }) {
733
+ const timedOut = useTimedOut(true, timeoutMs);
734
+ if (timedOut) {
735
+ return /* @__PURE__ */ jsx(
736
+ Notice,
737
+ {
738
+ className,
739
+ title: "This is taking longer than it should.",
740
+ action: /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: onRetry ?? (() => window.location.reload()), children: "Try again" }),
741
+ children: "Check your connection, then try again."
742
+ }
743
+ );
744
+ }
745
+ 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)) });
746
+ }
521
747
  function middle(v, head, tail) {
522
748
  return v.length <= head + tail + 1 ? v : `${v.slice(0, head)}\u2026${v.slice(-tail)}`;
523
749
  }
@@ -623,7 +849,253 @@ function ActivityList({ items, empty, className }) {
623
849
  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
850
  }) });
625
851
  }
852
+ var KIND = {
853
+ package: "text-oca-code-package",
854
+ tag: "text-oca-code-tag",
855
+ link: "text-oca-code-link"
856
+ };
857
+ function PackageCard({ items, className }) {
858
+ return /* @__PURE__ */ jsx(
859
+ "div",
860
+ {
861
+ className: cn(
862
+ "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",
863
+ className
864
+ ),
865
+ children: items.map((it, i) => {
866
+ const code = /* @__PURE__ */ jsx("span", { className: cn("text-[0.9375rem]", KIND[it.kind ?? "package"]), children: it.code });
867
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
868
+ /* @__PURE__ */ jsx("h3", { className: "text-[1.125rem] font-semibold tracking-[-0.01em] text-oca-ink-title", children: it.title }),
869
+ it.href ? /* @__PURE__ */ jsx("a", { href: it.href, className: "w-fit underline decoration-transparent underline-offset-4 hover:decoration-current", children: code }) : code,
870
+ it.note ? /* @__PURE__ */ jsxs("p", { className: "text-[0.9375rem] leading-relaxed text-oca-ink-subtle", children: [
871
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "// " }),
872
+ it.note
873
+ ] }) : null
874
+ ] }, i);
875
+ })
876
+ }
877
+ );
878
+ }
879
+ function CodeBlock({
880
+ code,
881
+ caption,
882
+ copy = true,
883
+ maxLines,
884
+ className
885
+ }) {
886
+ const lines = code.split("\n").length;
887
+ const foldable = maxLines !== void 0 && lines > maxLines + 2;
888
+ const [open, setOpen] = useState(false);
889
+ const folded = foldable && !open;
890
+ return /* @__PURE__ */ jsxs(
891
+ "figure",
892
+ {
893
+ className: cn(
894
+ "overflow-hidden rounded-[1.25rem] border border-[var(--oca-code-line)] bg-[var(--oca-code-surface)]",
895
+ className
896
+ ),
897
+ children: [
898
+ /* @__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: [
899
+ /* @__PURE__ */ jsx("span", { className: "truncate text-[0.8125rem] text-oca-ink-subtle", children: caption ?? "Code" }),
900
+ copy ? /* @__PURE__ */ jsx(
901
+ CopyButton,
902
+ {
903
+ value: code,
904
+ label: "Copy code",
905
+ className: "text-oca-ink-subtle hover:bg-white/10 hover:text-white"
906
+ }
907
+ ) : null
908
+ ] }),
909
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
910
+ /* @__PURE__ */ jsx(
911
+ "div",
912
+ {
913
+ className: cn(folded && "overflow-hidden"),
914
+ style: folded ? { maxHeight: `calc(${maxLines} * 1.625em + 2.5rem)` } : void 0,
915
+ 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 }) }) })
916
+ }
917
+ ),
918
+ foldable ? /* @__PURE__ */ jsx(
919
+ "div",
920
+ {
921
+ className: cn(
922
+ "flex justify-center px-5 pb-4",
923
+ folded && "absolute inset-x-0 bottom-0 pt-16"
924
+ ),
925
+ style: folded ? { background: "linear-gradient(to top, var(--oca-code-surface) 40%, transparent)" } : void 0,
926
+ children: /* @__PURE__ */ jsx(
927
+ "button",
928
+ {
929
+ type: "button",
930
+ onClick: () => setOpen((v) => !v),
931
+ className: "rounded-full border border-white/20 px-4 py-1.5 text-[0.8125rem] text-white transition-colors hover:bg-white/10",
932
+ children: open ? "Show less" : `Show all ${lines} lines`
933
+ }
934
+ )
935
+ }
936
+ ) : null
937
+ ] })
938
+ ]
939
+ }
940
+ );
941
+ }
942
+ function SplitBar({
943
+ segments,
944
+ caption,
945
+ className
946
+ }) {
947
+ return /* @__PURE__ */ jsxs("figure", { className: cn("flex flex-col gap-5", className), children: [
948
+ caption ? /* @__PURE__ */ jsx("figcaption", { className: "text-[0.9375rem]", children: caption }) : null,
949
+ /* @__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(
950
+ "div",
951
+ {
952
+ className: cn(
953
+ "flex min-w-[4.25rem] items-end rounded-[1rem] px-3 pb-2.5 md:px-4 md:pb-3",
954
+ s.tone === "card" ? "bg-white text-oca-ink" : "bg-oca-ink text-white"
955
+ ),
956
+ style: { flexBasis: `${s.pct}%` },
957
+ children: /* @__PURE__ */ jsxs("span", { className: "oca-tabular text-[1.25rem] leading-none tracking-[-0.02em] md:text-[1.875rem]", children: [
958
+ s.pct,
959
+ "%"
960
+ ] })
961
+ },
962
+ i
963
+ )) }),
964
+ /* @__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: [
965
+ /* @__PURE__ */ jsxs("dt", { className: "sr-only", children: [
966
+ s.pct,
967
+ "%"
968
+ ] }),
969
+ /* @__PURE__ */ jsx(
970
+ "span",
971
+ {
972
+ "aria-hidden": true,
973
+ className: cn(
974
+ "inline-block size-2.5 shrink-0 translate-y-[-1px] rounded-full",
975
+ s.tone === "card" ? "bg-white" : "bg-oca-ink"
976
+ )
977
+ }
978
+ ),
979
+ /* @__PURE__ */ jsx("dd", { className: "text-[0.9375rem] leading-snug", children: s.label })
980
+ ] }, i)) })
981
+ ] });
982
+ }
983
+ var LABEL = {
984
+ yes: "Yes",
985
+ testing: "In testing",
986
+ "not-yet": "Not yet",
987
+ no: "No"
988
+ };
989
+ var TONE = {
990
+ yes: "text-oca-accent-ink",
991
+ testing: "text-foreground",
992
+ "not-yet": "text-muted-foreground",
993
+ no: "text-muted-foreground"
994
+ };
995
+ function TruthTable({
996
+ rows,
997
+ statusHeading = "Status",
998
+ whatHeading = "What",
999
+ className
1000
+ }) {
1001
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1002
+ /* @__PURE__ */ jsx(
1003
+ "ul",
1004
+ {
1005
+ className: cn(
1006
+ "divide-y divide-oca-hairline-strong overflow-hidden rounded-xl border border-oca-hairline-strong bg-card sm:hidden",
1007
+ className
1008
+ ),
1009
+ children: rows.map((r, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-1.5 px-4 py-3.5", children: [
1010
+ /* @__PURE__ */ jsx("span", { className: cn("text-[0.875rem] font-medium", TONE[r.status]), children: LABEL[r.status] }),
1011
+ /* @__PURE__ */ jsx("span", { className: "text-[0.9375rem] leading-relaxed", children: r.what })
1012
+ ] }, i))
1013
+ }
1014
+ ),
1015
+ /* @__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: [
1016
+ /* @__PURE__ */ jsx("thead", { className: "bg-oca-table-head", children: /* @__PURE__ */ jsxs("tr", { children: [
1017
+ /* @__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 }),
1018
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-l border-oca-hairline-strong px-4 py-3 font-semibold", children: whatHeading })
1019
+ ] }) }),
1020
+ /* @__PURE__ */ jsx("tbody", { className: "bg-card", children: rows.map((r, i) => /* @__PURE__ */ jsxs("tr", { className: "align-top", children: [
1021
+ /* @__PURE__ */ jsx(
1022
+ "td",
1023
+ {
1024
+ className: cn(
1025
+ "border-t border-oca-hairline-strong px-4 py-3 font-medium",
1026
+ i === 0 && "border-t-0",
1027
+ TONE[r.status]
1028
+ ),
1029
+ children: LABEL[r.status]
1030
+ }
1031
+ ),
1032
+ /* @__PURE__ */ jsx(
1033
+ "td",
1034
+ {
1035
+ className: cn(
1036
+ "border-l border-t border-oca-hairline-strong px-4 py-3 leading-relaxed",
1037
+ i === 0 && "border-t-0"
1038
+ ),
1039
+ children: r.what
1040
+ }
1041
+ )
1042
+ ] }, i)) })
1043
+ ] }) })
1044
+ ] });
1045
+ }
1046
+ var INT = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
1047
+ var COMPACT = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
1048
+ function satsText(value, opts = {}) {
1049
+ if (value == null || !Number.isFinite(value)) return null;
1050
+ const whole = Math.trunc(value);
1051
+ const body = (opts.compact ? COMPACT : INT).format(Math.abs(whole));
1052
+ const sign = whole < 0 ? "\u2212" : opts.signed && whole > 0 ? "+" : "";
1053
+ const unit = opts.unit === "none" ? "" : whole === 1 || whole === -1 ? " sat" : " sats";
1054
+ return `${sign}${body}${unit}`;
1055
+ }
1056
+ function Sats({ value, unit, compact, signed, unknown = "\u2014", className, ...props }) {
1057
+ const text = satsText(value, { unit, compact, signed });
1058
+ if (text === null) {
1059
+ return /* @__PURE__ */ jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), ...props, children: [
1060
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: unknown }),
1061
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "unknown" })
1062
+ ] });
1063
+ }
1064
+ return /* @__PURE__ */ jsx("span", { className: cn("oca-tabular whitespace-nowrap", className), ...props, children: text });
1065
+ }
1066
+ function UsdApprox({
1067
+ sats,
1068
+ usdPerBtc,
1069
+ className
1070
+ }) {
1071
+ if (sats == null || usdPerBtc == null || !Number.isFinite(sats) || !Number.isFinite(usdPerBtc)) return null;
1072
+ const usd = sats / 1e8 * usdPerBtc;
1073
+ const text = new Intl.NumberFormat("en-US", {
1074
+ style: "currency",
1075
+ currency: "USD",
1076
+ maximumFractionDigits: usd < 1 ? 4 : 2
1077
+ }).format(usd);
1078
+ return /* @__PURE__ */ jsxs("span", { className: cn("oca-tabular text-muted-foreground", className), children: [
1079
+ "\u2248 ",
1080
+ text
1081
+ ] });
1082
+ }
1083
+ function SatsFigure({
1084
+ label,
1085
+ value,
1086
+ hint,
1087
+ className
1088
+ }) {
1089
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
1090
+ /* @__PURE__ */ jsx("span", { className: "text-[0.9375rem] text-muted-foreground", children: label }),
1091
+ /* @__PURE__ */ jsxs("span", { className: "oca-display-3 oca-tabular", children: [
1092
+ /* @__PURE__ */ jsx(Sats, { value, unit: "none" }),
1093
+ /* @__PURE__ */ jsx("span", { className: "ml-2 text-[0.5em] tracking-normal text-muted-foreground", children: "sats" })
1094
+ ] }),
1095
+ hint ? /* @__PURE__ */ jsx("span", { className: "text-[0.875rem] text-muted-foreground", children: hint }) : null
1096
+ ] });
1097
+ }
626
1098
 
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 };
1099
+ 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
1100
  //# sourceMappingURL=components.mjs.map
629
1101
  //# sourceMappingURL=components.mjs.map