@orangecheck/app-design 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/chrome.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var lucideReact = require('lucide-react');
4
- var Link5 = require('next/link');
4
+ var Link = require('next/link');
5
5
  var react = require('react');
6
6
  var clsx = require('clsx');
7
7
  var tailwindMerge = require('tailwind-merge');
@@ -10,12 +10,12 @@ var DialogPrimitive = require('@radix-ui/react-dialog');
10
10
  var DropdownMenuPrimitive = require('@radix-ui/react-dropdown-menu');
11
11
  var PopoverPrimitive = require('@radix-ui/react-popover');
12
12
  var TooltipPrimitive = require('@radix-ui/react-tooltip');
13
+ var nextThemes = require('next-themes');
13
14
  var classVarianceAuthority = require('class-variance-authority');
14
15
  var SeparatorPrimitive = require('@radix-ui/react-separator');
15
16
  require('sonner');
16
17
  var reactSlot = require('@radix-ui/react-slot');
17
18
  var router = require('next/compat/router');
18
- var nextThemes = require('next-themes');
19
19
 
20
20
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
21
 
@@ -37,7 +37,7 @@ function _interopNamespace(e) {
37
37
  return Object.freeze(n);
38
38
  }
39
39
 
40
- var Link5__default = /*#__PURE__*/_interopDefault(Link5);
40
+ var Link__default = /*#__PURE__*/_interopDefault(Link);
41
41
  var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
42
42
  var DropdownMenuPrimitive__namespace = /*#__PURE__*/_interopNamespace(DropdownMenuPrimitive);
43
43
  var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
@@ -436,6 +436,17 @@ var DropdownMenuLabel = react.forwardRef(function DropdownMenuLabel2({ className
436
436
  var DropdownMenuSeparator = react.forwardRef(function DropdownMenuSeparator2({ className, ...props }, ref) {
437
437
  return /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive__namespace.Separator, { ref, className: cn("my-1.5 h-px bg-border", className), ...props });
438
438
  });
439
+ function sharedContext(name, initial) {
440
+ const key = /* @__PURE__ */ Symbol.for(`@orangecheck/app-design/${name}`);
441
+ const store = globalThis;
442
+ return store[key] ?? (store[key] = react.createContext(initial));
443
+ }
444
+
445
+ // src/lib/shell-context.ts
446
+ var ShellContext = sharedContext("shell-place", {});
447
+ function useShellPlace() {
448
+ return react.useContext(ShellContext);
449
+ }
439
450
  var GOOD_DOT = "border-border bg-card text-foreground before:size-1.5 before:shrink-0 before:rounded-full before:bg-success before:content-['']";
440
451
  var badgeVariants = classVarianceAuthority.cva(
441
452
  "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",
@@ -562,1043 +573,1060 @@ function FamilyMenu({ current, className }) {
562
573
  )
563
574
  ] });
564
575
  }
565
- function NavLink({
566
- item,
567
- className,
568
- onNavigate,
569
- line = false
570
- }) {
571
- const cls = cn(
572
- "rounded-full px-3 py-2 text-[0.9375rem] transition-colors",
573
- item.active ? "text-foreground" : "text-muted-foreground hover:text-foreground",
574
- line && "relative after:absolute after:inset-x-3 after:-bottom-[0.625rem] after:h-0.5 after:rounded-full",
575
- line && item.active && "after:bg-foreground",
576
- className
577
- );
578
- if (item.external) {
579
- return /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.href, className: cls, rel: "noreferrer", onClick: onNavigate, children: item.label });
580
- }
581
- return /* @__PURE__ */ jsxRuntime.jsx(Link5__default.default, { href: item.href, className: cls, "aria-current": item.active ? "page" : void 0, onClick: onNavigate, children: item.label });
582
- }
583
- function NavRow({ item, onNavigate }) {
584
- const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
585
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.9375rem] font-medium leading-6 text-foreground", children: item.label }),
586
- item.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.8125rem] leading-snug text-muted-foreground", children: item.description }) : null
587
- ] });
588
- const cls = cn(
589
- "flex flex-col gap-0.5 rounded-xl px-3 py-2 transition-colors hover:bg-accent",
590
- item.active && "bg-muted hover:bg-muted dark:bg-white/[0.06]"
591
- );
592
- return item.external ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.href, className: cls, rel: "noreferrer", onClick: onNavigate, children: body }) : /* @__PURE__ */ jsxRuntime.jsx(Link5__default.default, { href: item.href, className: cls, "aria-current": item.active ? "page" : void 0, onClick: onNavigate, children: body });
593
- }
594
- var seeAll = (item) => typeof item.label === "string" ? `All ${item.label.toLowerCase()}` : "See all";
595
- function NavDropdown({ item }) {
596
- const [open, setOpen] = react.useState(false);
597
- const active = item.active || item.children?.some((c) => c.active);
598
- return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
599
- /* @__PURE__ */ jsxRuntime.jsxs(
600
- PopoverTrigger,
576
+ function SideNav({ groups, onNavigate }) {
577
+ return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Pages", className: "flex flex-col gap-5", children: groups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
578
+ g.label ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-1 text-[0.8125rem] text-muted-foreground", children: g.label }) : null,
579
+ g.items.map((it) => /* @__PURE__ */ jsxRuntime.jsxs(
580
+ Link__default.default,
601
581
  {
582
+ href: it.href,
583
+ onClick: onNavigate,
584
+ "aria-current": it.active ? "page" : void 0,
602
585
  className: cn(
603
- "group inline-flex items-center gap-1 rounded-full py-2 pl-3 pr-2.5 text-[0.9375rem] transition-colors md:max-lg:pl-2.5 md:max-lg:pr-2",
604
- active ? "text-foreground" : "text-muted-foreground hover:text-foreground data-[state=open]:text-foreground"
586
+ "flex h-9 items-center gap-3 rounded-xl px-3 text-[0.9375rem] transition-colors",
587
+ "[&_svg]:size-[1.0625rem] [&_svg]:shrink-0",
588
+ it.active ? "bg-card text-foreground shadow-sm ring-1 ring-border [&_svg]:text-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground"
605
589
  ),
606
590
  children: [
607
- item.label,
608
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { "aria-hidden": true, className: "size-3.5 transition-transform duration-200 group-data-[state=open]:rotate-180" })
591
+ it.icon,
592
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: it.label }),
593
+ it.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: it.badge }) : null
609
594
  ]
610
- }
611
- ),
612
- /* @__PURE__ */ jsxRuntime.jsxs(PopoverContent, { align: "center", sideOffset: 10, className: "w-[min(calc(100vw-2rem),26rem)] p-2", children: [
613
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col", children: item.children?.map((c) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(NavRow, { item: c, onNavigate: () => setOpen(false) }) }, c.href)) }),
614
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 border-t border-border px-1 pt-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(
615
- NavLink,
616
- {
617
- item: { ...item, label: seeAll(item), children: void 0 },
618
- className: "block rounded-xl px-2 py-2 text-[0.875rem]",
619
- onNavigate: () => setOpen(false)
620
- }
621
- ) })
622
- ] })
623
- ] });
595
+ },
596
+ it.href
597
+ ))
598
+ ] }, g.label ?? gi)) });
624
599
  }
625
- function SiteHeader({
626
- home = "/",
627
- brand,
628
- product,
629
- family,
630
- nav = [],
631
- search,
632
- actions,
633
- variant = "site",
634
- navOnPhone = true,
635
- menu: menuExtra,
636
- className
600
+ function SectionSwitch({
601
+ sections,
602
+ current,
603
+ onNavigate
637
604
  }) {
638
- const [open, setOpen] = react.useState(false);
639
- const tool = variant === "tool";
640
- const size = tool ? 24 : 28;
641
- const current = typeof family === "string" ? family : void 0;
642
- const phoneNav = navOnPhone ? nav : [];
643
- const menu = phoneNav.length > 0 || Boolean(family) || Boolean(menuExtra);
644
- const brandNode = brand ?? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center", children: [
645
- /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size }),
646
- product ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
647
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "mx-3 h-5 w-px bg-oca-hairline-strong" }),
648
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium tracking-[-0.01em]", style: { fontSize: size * 0.56 }, children: product })
649
- ] }) : null
650
- ] });
651
- const navigation = nav.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Main", className: "hidden items-center gap-1 md:flex", children: nav.map(
652
- (n) => n.children?.length ? /* @__PURE__ */ jsxRuntime.jsx(NavDropdown, { item: n }, n.href) : (
653
- // Between md and lg the family menu and the actions share the row: tighter.
654
- /* @__PURE__ */ jsxRuntime.jsx(NavLink, { item: n, line: tool, className: tool ? void 0 : "md:max-lg:px-2.5" }, n.href)
655
- )
656
- ) }) : null;
605
+ const cols = sections.length <= 3 ? sections.length : 2;
657
606
  return /* @__PURE__ */ jsxRuntime.jsx(
658
- "header",
607
+ "nav",
659
608
  {
660
- className: cn(
661
- "sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur-md supports-[backdrop-filter]:bg-background/90",
662
- tool ? "border-border" : "border-transparent",
663
- className
664
- ),
665
- children: /* @__PURE__ */ jsxRuntime.jsxs(
609
+ "aria-label": "Sections",
610
+ className: cn("grid gap-1 bg-muted p-1", cols === sections.length ? "rounded-full" : "rounded-[1.25rem]"),
611
+ style: { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` },
612
+ children: sections.map((sec) => {
613
+ const on = sec.id === current.id;
614
+ return /* @__PURE__ */ jsxRuntime.jsx(
615
+ Link__default.default,
616
+ {
617
+ href: sec.href,
618
+ onClick: onNavigate,
619
+ "aria-current": on ? "true" : void 0,
620
+ className: cn(
621
+ "flex h-8 min-w-0 items-center justify-center rounded-full px-1.5 text-[0.8125rem] font-medium transition-colors",
622
+ on ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
623
+ ),
624
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: sec.label })
625
+ },
626
+ sec.id
627
+ );
628
+ })
629
+ }
630
+ );
631
+ }
632
+ function TabBar({ items }) {
633
+ return /* @__PURE__ */ jsxRuntime.jsx(
634
+ "nav",
635
+ {
636
+ "aria-label": "Pages",
637
+ "data-oca-tabbar": "",
638
+ className: "fixed inset-x-0 bottom-0 z-40 border-t border-border bg-background/90 pb-[env(safe-area-inset-bottom)] backdrop-blur-md md:hidden",
639
+ children: /* @__PURE__ */ jsxRuntime.jsx(
666
640
  "div",
667
641
  {
668
- className: cn(
669
- "flex items-center justify-between gap-4",
670
- tool ? "h-14 px-4 md:px-6" : "oca-container h-16 md:h-[4.5rem] md:max-lg:gap-2",
671
- // Three columns, the outer two each at least their content: the
672
- // navigation stays centred whatever the actions hold, so it does
673
- // not shift when they change (signed out to signed in).
674
- !tool && nav.length > 0 && "md:grid md:grid-cols-[minmax(max-content,1fr)_auto_minmax(max-content,1fr)]"
675
- ),
676
- children: [
677
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex min-w-0 items-center gap-1", tool ? "md:gap-1" : "justify-self-start"), children: [
678
- /* @__PURE__ */ jsxRuntime.jsxs(Link5__default.default, { href: home, className: "-ml-1 min-w-0 rounded-lg p-1", children: [
679
- brandNode,
680
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: " home" })
681
- ] }),
682
- family ? /* @__PURE__ */ jsxRuntime.jsx(FamilyMenu, { current, className: "max-md:hidden" }) : null,
683
- tool && navigation ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-5", children: navigation }) : null
684
- ] }),
685
- !tool ? navigation : null,
686
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 justify-self-end", children: [
687
- search,
688
- actions,
689
- menu ? /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open, onOpenChange: setOpen, children: [
690
- /* @__PURE__ */ jsxRuntime.jsx(
691
- SheetTrigger,
642
+ className: "mx-auto grid h-14 max-w-[32rem] px-1.5",
643
+ style: { gridTemplateColumns: `repeat(${items.length}, minmax(0, 1fr))` },
644
+ children: items.map((it) => /* @__PURE__ */ jsxRuntime.jsxs(
645
+ Link__default.default,
646
+ {
647
+ href: it.href,
648
+ "aria-current": it.active ? "page" : void 0,
649
+ className: cn(
650
+ "flex min-w-0 flex-col items-center justify-center gap-1 rounded-2xl text-[0.6875rem] leading-none outline-none transition-colors [-webkit-tap-highlight-color:transparent] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
651
+ it.active ? "text-foreground" : "text-muted-foreground hover:text-foreground"
652
+ ),
653
+ children: [
654
+ /* @__PURE__ */ jsxRuntime.jsxs(
655
+ "span",
692
656
  {
693
- className: "inline-flex size-10 items-center justify-center rounded-full text-foreground hover:bg-accent md:hidden",
694
- "aria-label": "Open menu",
695
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "size-5" })
657
+ className: cn(
658
+ "relative flex h-7 w-12 items-center justify-center rounded-full transition-[background-color,box-shadow] duration-200",
659
+ "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
660
+ it.active ? "bg-card shadow-sm ring-1 ring-border [&_svg]:stroke-2" : "[&_svg]:stroke-[1.75]"
661
+ ),
662
+ children: [
663
+ it.icon,
664
+ it.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "absolute right-2.5 top-0.5 size-2 rounded-full bg-oca-orange ring-2 ring-background" }) : null
665
+ ]
696
666
  }
697
667
  ),
698
- /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "right", className: "pt-16", children: [
699
- /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: "Menu" }),
700
- menuExtra ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col", (phoneNav.length > 0 || family) && "border-b border-border pb-3"), children: menuExtra }) : null,
701
- phoneNav.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Main", className: "flex flex-col", children: phoneNav.map(
702
- (n) => n.children?.length ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col py-2", children: [
703
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-1 text-[0.8125rem] text-muted-foreground", children: n.label }),
704
- n.children.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
705
- NavLink,
706
- {
707
- item: c,
708
- className: "rounded-xl px-3 py-2.5 text-[1.0625rem]",
709
- onNavigate: () => setOpen(false)
710
- },
711
- c.href
712
- ))
713
- ] }, n.href) : /* @__PURE__ */ jsxRuntime.jsx(
714
- NavLink,
715
- {
716
- item: n,
717
- className: "rounded-xl px-3 py-3 text-[1.0625rem]",
718
- onNavigate: () => setOpen(false)
719
- },
720
- n.href
721
- )
722
- ) }) : null,
723
- family ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: cn("group", phoneNav.length > 0 && "mt-2 border-t border-border pt-4"), children: [
724
- /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2.5 text-[1.0625rem] [&::-webkit-details-marker]:hidden", children: [
725
- "All orangecheck sites",
726
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { "aria-hidden": true, className: "size-4 text-muted-foreground transition-transform group-open:rotate-180" })
727
- ] }),
728
- /* @__PURE__ */ jsxRuntime.jsx(FamilyList, { current, onNavigate: () => setOpen(false), stack: true, className: "mt-2" })
729
- ] }) : null
730
- ] })
731
- ] }) : null
732
- ] })
733
- ]
668
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-full truncate px-0.5", children: it.label }),
669
+ it.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: it.badge }) : null
670
+ ]
671
+ },
672
+ it.href
673
+ ))
734
674
  }
735
675
  )
736
676
  }
737
677
  );
738
678
  }
739
- function SkipLink({ href = "#main-content", children = "Skip to content" }) {
740
- return /* @__PURE__ */ jsxRuntime.jsx(
741
- "a",
742
- {
743
- href,
744
- className: "sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-3 focus:z-50 focus:rounded-full focus:border focus:border-border focus:bg-card focus:px-4 focus:py-2 focus:text-[0.9375rem] focus:font-medium focus:text-foreground focus:shadow-md",
745
- children
746
- }
747
- );
748
- }
749
- var BANNER_TONE = {
750
- neutral: "border-border bg-muted",
751
- brand: "border-[color-mix(in_oklab,var(--oca-orange)_30%,var(--border))] bg-[color-mix(in_oklab,var(--oca-orange)_9%,var(--background))]",
752
- danger: "border-[color-mix(in_oklab,var(--destructive)_35%,var(--border))] bg-[color-mix(in_oklab,var(--destructive)_8%,var(--background))]"
753
- };
754
- function SiteBanner({
755
- tone = "neutral",
679
+ function tabsFor(groups) {
680
+ const items = groups.flatMap((g) => g.items);
681
+ return items.length >= 2 && items.length <= 5 && items.every((it) => it.icon) ? items : null;
682
+ }
683
+ var activeItem = (groups) => groups.flatMap((g) => g.items).find((it) => it.active);
684
+ var warned = /* @__PURE__ */ new Set();
685
+ function warnDuplicateLabels(groups) {
686
+ if (typeof process === "undefined" || process.env.NODE_ENV === "production") return;
687
+ const seen = /* @__PURE__ */ new Set();
688
+ for (const it of groups.flatMap((g) => g.items)) {
689
+ if (seen.has(it.label) && !warned.has(it.label)) {
690
+ warned.add(it.label);
691
+ console.warn(`AppShell: two pages are labelled "${it.label}"; give each its own name.`);
692
+ }
693
+ seen.add(it.label);
694
+ }
695
+ }
696
+ function AppShell({
697
+ nav,
698
+ sections,
699
+ activeSection,
700
+ page: pageOverride,
701
+ persistent = [],
702
+ home = "/",
703
+ family,
704
+ account,
705
+ banner,
706
+ footer,
756
707
  children,
757
- action,
758
708
  className
759
709
  }) {
760
- return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "data-tone": tone, className: cn("border-b text-[0.875rem]", BANNER_TONE[tone], className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oca-container flex flex-wrap items-center justify-between gap-x-4 gap-y-1 py-2.5", children: [
761
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex min-w-0 items-baseline gap-2.5 leading-snug", children: [
762
- tone !== "neutral" ? /* @__PURE__ */ jsxRuntime.jsx(
763
- "span",
764
- {
765
- "aria-hidden": true,
766
- className: cn(
767
- "size-2 shrink-0 translate-y-[-0.05em] rounded-full",
768
- tone === "brand" ? "bg-oca-orange" : "bg-destructive"
769
- )
770
- }
771
- ) : null,
772
- /* @__PURE__ */ jsxRuntime.jsx("span", { children })
773
- ] }),
774
- action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("shrink-0 font-medium", tone !== "neutral" && "max-sm:pl-[1.125rem]"), children: action }) : null
775
- ] }) });
776
- }
777
- var FOOT_LINK = "inline-flex min-h-11 items-center transition-colors hover:text-foreground md:min-h-0";
778
- function LegalRow({ links }) {
779
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-x-6 gap-y-2 sm:flex-row sm:items-center sm:justify-between", children: [
780
- /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Legal", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-wrap gap-x-5", children: links.map((l) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: l.href.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(Link5__default.default, { href: l.href, className: FOOT_LINK, children: l.label }) : /* @__PURE__ */ jsxRuntime.jsx("a", { href: l.href, className: FOOT_LINK, children: l.label }) }, l.href)) }) }),
781
- /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
782
- "\xA9 ",
783
- (/* @__PURE__ */ new Date()).getFullYear(),
784
- " OrangeCheck"
785
- ] })
786
- ] });
710
+ const [open, setOpen] = react.useState(false);
711
+ const current = sections && sections.length > 0 ? sections.find((sec) => sec.id === activeSection) ?? sections.find((sec) => activeItem(sec.groups)) ?? sections[0] : void 0;
712
+ const groups = current ? [...current.groups, ...persistent] : [...nav ?? [], ...persistent];
713
+ warnDuplicateLabels(groups);
714
+ const tabs = current ? null : tabsFor(groups);
715
+ const page = activeItem(groups);
716
+ const place = react.useMemo(
717
+ () => ({
718
+ section: current ? current.title ?? current.label : void 0,
719
+ page: pageOverride ?? page?.label
720
+ }),
721
+ [current, page?.label, pageOverride]
722
+ );
723
+ const familyMenu = family ? /* @__PURE__ */ jsxRuntime.jsx(FamilyMenu, { current: typeof family === "string" ? family : void 0 }) : null;
724
+ const switcher = sections && current && sections.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(SectionSwitch, { sections, current, onNavigate: () => setOpen(false) }) : null;
725
+ return /* @__PURE__ */ jsxRuntime.jsx(ShellContext.Provider, { value: place, children: /* @__PURE__ */ jsxRuntime.jsxs(
726
+ "div",
727
+ {
728
+ className: cn(
729
+ "min-h-dvh bg-background",
730
+ tabs && "max-md:pb-[calc(3.5rem_+_env(safe-area-inset-bottom))]",
731
+ className
732
+ ),
733
+ children: [
734
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sticky top-0 z-40 border-b border-border bg-background/90 backdrop-blur-md lg:hidden", children: [
735
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-14 items-center gap-2 px-3", children: [
736
+ /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open, onOpenChange: setOpen, children: [
737
+ /* @__PURE__ */ jsxRuntime.jsx(
738
+ SheetTrigger,
739
+ {
740
+ className: cn(
741
+ "inline-flex size-10 items-center justify-center rounded-full hover:bg-accent",
742
+ // The tab bar holds every page; the sheet is left for a footer.
743
+ tabs && !footer && "max-md:hidden"
744
+ ),
745
+ "aria-label": "Open navigation",
746
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "size-5" })
747
+ }
748
+ ),
749
+ /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "left", className: "flex flex-col gap-6 overflow-y-auto p-4 pt-16", children: [
750
+ /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: "Navigation" }),
751
+ switcher,
752
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: tabs ? "max-md:hidden" : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups, onNavigate: () => setOpen(false) }) }),
753
+ footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer }) : null
754
+ ] })
755
+ ] }),
756
+ /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: home, "aria-label": "Home", className: "rounded-lg p-1", children: /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 22 }) }),
757
+ familyMenu,
758
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex items-center", children: account })
759
+ ] }),
760
+ place.page ? /* @__PURE__ */ jsxRuntime.jsxs(
761
+ "div",
762
+ {
763
+ className: cn(
764
+ "flex h-9 items-center gap-1.5 border-t border-border px-4 text-[0.875rem]",
765
+ // The active tab already names the page.
766
+ tabs && !pageOverride && "max-md:hidden"
767
+ ),
768
+ children: [
769
+ place.section ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
770
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: place.section }),
771
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": true, className: "size-3.5 shrink-0 text-muted-foreground" })
772
+ ] }) : null,
773
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", children: place.page })
774
+ ]
775
+ }
776
+ ) : null
777
+ ] }),
778
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-[90rem]", children: [
779
+ /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "sticky top-0 hidden h-dvh w-[17rem] shrink-0 flex-col gap-6 overflow-y-auto px-4 py-6 lg:flex", children: [
780
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
781
+ /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: home, className: "px-2", "aria-label": "Home", children: /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 26 }) }),
782
+ familyMenu
783
+ ] }),
784
+ switcher,
785
+ /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups }),
786
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer })
787
+ ] }),
788
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
789
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden h-[4.5rem] items-center justify-end gap-3 px-8 lg:flex", children: account }),
790
+ banner ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pb-2 pt-4 md:px-8 lg:pt-0", children: banner }) : null,
791
+ /* @__PURE__ */ jsxRuntime.jsx("main", { id: "main-content", className: "flex-1 px-4 pb-16 pt-6 md:px-8 lg:pt-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full max-w-[72rem]", children }) })
792
+ ] })
793
+ ] }),
794
+ tabs ? /* @__PURE__ */ jsxRuntime.jsx(TabBar, { items: tabs }) : null
795
+ ]
796
+ }
797
+ ) });
787
798
  }
788
- function SiteFooter({
789
- columns = [],
790
- family = false,
791
- legal = false,
792
- compact = false,
793
- fineprint,
794
- meta,
795
- signature = true,
796
- className
799
+ function AccountMenu({
800
+ name,
801
+ detail,
802
+ items,
803
+ nameFrom = "sm"
797
804
  }) {
798
- const legalLinks = legal === true ? LEGAL_LINKS : legal || null;
799
- if (compact) {
800
- return /* @__PURE__ */ jsxRuntime.jsx("footer", { className: cn("border-t border-border bg-background text-[0.875rem] text-muted-foreground", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oca-container flex flex-col gap-x-8 gap-y-3 py-5 lg:flex-row lg:items-center lg:justify-between", children: [
801
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-1", children: [
802
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: OCHK_IO, className: "rounded-lg text-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 22 }) }),
803
- family ? /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Protocols", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-wrap gap-x-4", children: PROTOCOLS.map((p) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: p.pageHref, className: FOOT_LINK, children: p.label }) }, p.slug)) }) }) : null
804
- ] }),
805
- legalLinks || meta ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-x-6 gap-y-2 sm:flex-row sm:flex-wrap sm:items-center", children: [
806
- legalLinks ? /* @__PURE__ */ jsxRuntime.jsx(LegalRow, { links: legalLinks }) : null,
807
- meta
808
- ] }) : null
809
- ] }) });
810
- }
811
- const cols = [
812
- ...columns,
813
- ...family ? familyByGroup().map((g) => ({
814
- // The lockup above already says orangecheck.
815
- heading: g.id === "orangecheck" ? "Start here" : g.label,
816
- links: g.sites.map((s) => ({ href: s.href, label: s.label, external: true }))
817
- })) : []
818
- ];
819
- const n = cols.length;
820
- const inline = n <= 5;
821
- const grid = {
822
- "--oca-footer-md": `repeat(${Math.max(1, Math.min(n, 4))}, minmax(0, 1fr))`,
823
- "--oca-footer-lg": inline ? `minmax(0, 1.3fr) repeat(${Math.max(1, n)}, minmax(0, 1fr))` : `repeat(${Math.min(n, 6)}, minmax(0, 1fr))`
824
- };
825
- return /* @__PURE__ */ jsxRuntime.jsxs("footer", { className: cn("border-t border-border bg-background", className), children: [
805
+ const initial = name.trim().charAt(0).toUpperCase() || "\xB7";
806
+ return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
826
807
  /* @__PURE__ */ jsxRuntime.jsxs(
827
- "div",
808
+ DropdownMenuTrigger,
828
809
  {
829
- style: grid,
830
- className: "oca-container grid grid-cols-2 gap-x-6 gap-y-10 py-14 md:grid-cols-[var(--oca-footer-md)] md:py-16 lg:grid-cols-[var(--oca-footer-lg)]",
810
+ className: cn(
811
+ "inline-flex h-10 items-center gap-2.5 rounded-full border border-border bg-card pl-1 pr-1 text-[0.9375rem] outline-none transition-colors hover:border-oca-hairline-strong focus-visible:ring-2 focus-visible:ring-ring",
812
+ nameFrom === "lg" ? "lg:pr-3.5" : "sm:pr-3.5"
813
+ ),
814
+ "aria-label": `${name}, account menu`,
831
815
  children: [
832
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("col-span-full flex flex-col gap-4", inline && "lg:col-span-1"), children: [
833
- /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 26 }),
834
- fineprint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-xs text-[0.9375rem] leading-relaxed text-muted-foreground", children: fineprint }) : null
835
- ] }),
836
- cols.map((col, i) => {
837
- const spans = n % 2 === 1 && i === n - 1;
838
- return /* @__PURE__ */ jsxRuntime.jsxs(
839
- "div",
840
- {
841
- className: cn("flex min-w-0 flex-col gap-1", spans && "col-span-2 md:col-span-1"),
842
- children: [
843
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[0.875rem] font-medium", children: col.heading }),
844
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col gap-1", spans && "grid grid-cols-2 gap-x-6 md:flex md:flex-col"), children: col.links.map((l) => /* @__PURE__ */ jsxRuntime.jsx(
845
- NavLink,
846
- {
847
- item: l,
848
- className: "-mx-3 py-2.5 text-[0.9375rem] md:py-1.5"
849
- },
850
- l.href
851
- )) })
852
- ]
853
- },
854
- col.heading
855
- );
856
- })
816
+ /* @__PURE__ */ jsxRuntime.jsx(
817
+ "span",
818
+ {
819
+ "aria-hidden": true,
820
+ "data-initial": initial,
821
+ className: "flex size-8 items-center justify-center rounded-full bg-foreground text-[0.8125rem] font-medium text-background before:content-[attr(data-initial)]"
822
+ }
823
+ ),
824
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("max-w-[10rem] truncate", nameFrom === "lg" ? "max-lg:hidden" : "max-sm:hidden"), children: name })
857
825
  ]
858
826
  }
859
827
  ),
860
- signature ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "oca-container select-none overflow-hidden pb-8 md:pb-12", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-[0.18em] text-[clamp(3rem,11.5vw,10rem)] leading-[0.78] tracking-[-0.065em] [font-family:var(--oc-font-display)]", children: [
861
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-[0.06em] inline-block w-[1.1em] shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(Mark, { className: "block h-auto w-full" }) }),
862
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "orangecheck" })
863
- ] }) }) : null,
864
- legalLinks || meta ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oca-container", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 border-t border-border py-6 text-[0.875rem] text-muted-foreground", children: [
865
- legalLinks ? /* @__PURE__ */ jsxRuntime.jsx(LegalRow, { links: legalLinks }) : null,
866
- meta
867
- ] }) }) : null
828
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuContent, { align: "end", className: "w-64", children: [
829
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuLabel, { className: "flex flex-col gap-0.5", children: [
830
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[0.9375rem] font-medium text-foreground", children: name }),
831
+ detail ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-mono text-[0.75rem]", children: detail }) : null
832
+ ] }),
833
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}),
834
+ items.map(
835
+ (it, i) => it === "separator" ? /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}, `sep-${i}`) : it.href ? /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuItem, { asChild: true, destructive: it.destructive, children: /* @__PURE__ */ jsxRuntime.jsxs(Link__default.default, { href: it.href, children: [
836
+ it.icon,
837
+ it.label
838
+ ] }) }, it.label) : /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onSelect: it.onSelect, destructive: it.destructive, children: [
839
+ it.icon,
840
+ it.label
841
+ ] }, it.label)
842
+ )
843
+ ] })
868
844
  ] });
869
845
  }
870
- function IsoCheck({ size = 360, settle = false, shadow = true, tilt = false, title, className }) {
871
- const ref = react.useRef(null);
872
- react.useEffect(() => {
873
- const el = ref.current;
874
- if (!el || !tilt || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
875
- let raf = 0;
876
- let tx = 0;
877
- let ty = 0;
878
- let x = 0;
879
- let y = 0;
880
- const step = () => {
881
- x += (tx - x) * 0.08;
882
- y += (ty - y) * 0.08;
883
- el.style.setProperty("--rx", `${(-y * 10).toFixed(2)}deg`);
884
- el.style.setProperty("--ry", `${(x * 14).toFixed(2)}deg`);
885
- raf = Math.abs(tx - x) + Math.abs(ty - y) > 1e-3 ? requestAnimationFrame(step) : 0;
886
- };
887
- const move = (e) => {
888
- const b = el.getBoundingClientRect();
889
- tx = Math.max(-1, Math.min(1, (e.clientX - (b.left + b.width / 2)) / (window.innerWidth / 2)));
890
- ty = Math.max(-1, Math.min(1, (e.clientY - (b.top + b.height / 2)) / (window.innerHeight / 2)));
891
- if (!raf) raf = requestAnimationFrame(step);
892
- };
893
- window.addEventListener("pointermove", move, { passive: true });
894
- return () => {
895
- cancelAnimationFrame(raf);
896
- window.removeEventListener("pointermove", move);
897
- };
898
- }, [tilt]);
899
- return /* @__PURE__ */ jsxRuntime.jsxs(
900
- "div",
846
+ function ThemeToggle({ className }) {
847
+ const { theme, setTheme } = nextThemes.useTheme();
848
+ const [mounted, setMounted] = react.useState(false);
849
+ react.useEffect(() => setMounted(true), []);
850
+ const current = mounted ? theme ?? "system" : "system";
851
+ const next = current === "light" ? "dark" : current === "dark" ? "system" : "light";
852
+ const Icon = current === "light" ? lucideReact.Sun : current === "dark" ? lucideReact.Moon : lucideReact.SunMoon;
853
+ return /* @__PURE__ */ jsxRuntime.jsx(
854
+ "button",
901
855
  {
902
- ref,
903
- className: cn("relative inline-block", settle && "oca-animate-settle", className),
904
- style: { width: size, perspective: tilt ? 900 : void 0 },
905
- children: [
906
- tilt ? /* @__PURE__ */ jsxRuntime.jsx(
907
- "div",
908
- {
909
- "aria-hidden": true,
910
- className: "absolute left-1/2 top-1/2 -z-0 aspect-square w-[120%] -translate-x-1/2 -translate-y-1/2 rounded-full opacity-70 blur-3xl",
911
- style: { background: "radial-gradient(closest-side, rgb(255 140 70 / 0.35), transparent)" }
912
- }
913
- ) : null,
914
- /* @__PURE__ */ jsxRuntime.jsx(
915
- "div",
916
- {
917
- className: "relative z-10 transition-transform duration-300 ease-out",
918
- style: tilt ? { transform: "rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg))", transformStyle: "preserve-3d" } : void 0,
919
- children: /* @__PURE__ */ jsxRuntime.jsx(Mark, { size, title, className: "block" })
920
- }
921
- ),
922
- shadow ? /* @__PURE__ */ jsxRuntime.jsx(
923
- "div",
924
- {
925
- "aria-hidden": true,
926
- className: "absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl",
927
- style: { height: size * 0.08, bottom: -size * 0.04, background: "rgb(234 88 11 / 0.18)" }
928
- }
929
- ) : null
930
- ]
856
+ type: "button",
857
+ onClick: () => setTheme(next),
858
+ "aria-label": `Theme: ${current}. Switch to ${next}`,
859
+ title: `Theme: ${current}`,
860
+ className: cn(
861
+ "inline-flex size-10 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
862
+ className
863
+ ),
864
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-[1.125rem]" })
931
865
  }
932
866
  );
933
867
  }
934
- function SignInFrame({
935
- title = "Sign in",
936
- lead,
937
- children,
938
- error,
939
- fineprint,
940
- brand,
941
- home = "/",
942
- back,
943
- art,
944
- caption,
945
- className
868
+ function NavLink({
869
+ item,
870
+ className,
871
+ onNavigate,
872
+ line = false
946
873
  }) {
947
- return /* @__PURE__ */ jsxRuntime.jsxs(
948
- "main",
949
- {
950
- id: "main-content",
951
- className: cn("grid min-h-dvh grid-cols-1 bg-background text-foreground lg:grid-cols-2", className),
952
- children: [
953
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "flex flex-col px-4 pb-8 pt-5 sm:px-8 lg:px-14 lg:pt-8", children: [
954
- /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex items-center justify-between gap-4", children: [
955
- /* @__PURE__ */ jsxRuntime.jsxs(Link5__default.default, { href: home, className: "w-fit rounded-lg", children: [
956
- brand ?? /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 28 }),
957
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: " home" })
958
- ] }),
959
- back === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
960
- Link5__default.default,
961
- {
962
- href: home,
963
- className: "text-[0.9375rem] text-muted-foreground underline-offset-4 hover:text-foreground hover:underline",
964
- children: "Back to home"
965
- }
966
- ) : back
967
- ] }),
968
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-[25rem] flex-1 flex-col justify-center gap-9 py-12", children: [
969
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
970
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "oca-display-2", children: title }),
971
- lead ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-lead", children: lead }) : null
972
- ] }),
973
- /* @__PURE__ */ jsxRuntime.jsx("div", { children }),
974
- error ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-[0.9375rem] text-destructive", children: error }) : null,
975
- fineprint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.8125rem] leading-relaxed text-muted-foreground", children: fineprint }) : null
976
- ] })
977
- ] }),
978
- /* @__PURE__ */ jsxRuntime.jsxs(
979
- "aside",
980
- {
981
- "aria-hidden": true,
982
- className: "relative hidden flex-col justify-between gap-10 overflow-hidden border-l border-[var(--oca-band-line)] bg-oca-ink p-14 text-oca-ink-foreground lg:flex",
983
- children: [
984
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: art ?? /* @__PURE__ */ jsxRuntime.jsx(IsoCheck, { size: 340, settle: true, tilt: true, className: "max-w-full" }) }),
985
- caption ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-w-[24rem] flex-col gap-3", children: caption }) : null
986
- ]
987
- }
988
- )
989
- ]
990
- }
874
+ const cls = cn(
875
+ "rounded-full px-3 py-2 text-[0.9375rem] transition-colors",
876
+ item.active ? "text-foreground" : "text-muted-foreground hover:text-foreground",
877
+ line && "relative after:absolute after:inset-x-3 after:-bottom-[0.625rem] after:h-0.5 after:rounded-full",
878
+ line && item.active && "after:bg-foreground",
879
+ className
991
880
  );
881
+ if (item.external) {
882
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.href, className: cls, rel: "noreferrer", onClick: onNavigate, children: item.label });
883
+ }
884
+ return /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: item.href, className: cls, "aria-current": item.active ? "page" : void 0, onClick: onNavigate, children: item.label });
992
885
  }
993
- function Spinner({ className, label }) {
994
- return /* @__PURE__ */ jsxRuntime.jsxs(
995
- "svg",
996
- {
997
- viewBox: "0 0 24 24",
998
- fill: "none",
999
- role: label ? "status" : void 0,
1000
- "aria-label": label,
1001
- "aria-hidden": label ? void 0 : true,
1002
- className: cn("size-4 animate-spin", className),
1003
- children: [
1004
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeOpacity: "0.2", strokeWidth: "2.5" }),
1005
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 0 0-9-9", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" })
1006
- ]
1007
- }
886
+ function NavRow({ item, onNavigate }) {
887
+ const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
888
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.9375rem] font-medium leading-6 text-foreground", children: item.label }),
889
+ item.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.8125rem] leading-snug text-muted-foreground", children: item.description }) : null
890
+ ] });
891
+ const cls = cn(
892
+ "flex flex-col gap-0.5 rounded-xl px-3 py-2 transition-colors hover:bg-accent",
893
+ item.active && "bg-muted hover:bg-muted dark:bg-white/[0.06]"
1008
894
  );
895
+ return item.external ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.href, className: cls, rel: "noreferrer", onClick: onNavigate, children: body }) : /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: item.href, className: cls, "aria-current": item.active ? "page" : void 0, onClick: onNavigate, children: body });
1009
896
  }
1010
- react.forwardRef(function Separator3({ className, orientation = "horizontal", decorative = true, ...props }, ref) {
897
+ var seeAll = (item) => typeof item.label === "string" ? `All ${item.label.toLowerCase()}` : "See all";
898
+ function NavDropdown({ item }) {
899
+ const [open, setOpen] = react.useState(false);
900
+ const active = item.active || item.children?.some((c) => c.active);
901
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
902
+ /* @__PURE__ */ jsxRuntime.jsxs(
903
+ PopoverTrigger,
904
+ {
905
+ className: cn(
906
+ "group inline-flex items-center gap-1 rounded-full py-2 pl-3 pr-2.5 text-[0.9375rem] transition-colors md:max-lg:pl-2.5 md:max-lg:pr-2",
907
+ active ? "text-foreground" : "text-muted-foreground hover:text-foreground data-[state=open]:text-foreground"
908
+ ),
909
+ children: [
910
+ item.label,
911
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { "aria-hidden": true, className: "size-3.5 transition-transform duration-200 group-data-[state=open]:rotate-180" })
912
+ ]
913
+ }
914
+ ),
915
+ /* @__PURE__ */ jsxRuntime.jsxs(PopoverContent, { align: "center", sideOffset: 10, className: "w-[min(calc(100vw-2rem),26rem)] p-2", children: [
916
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col", children: item.children?.map((c) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(NavRow, { item: c, onNavigate: () => setOpen(false) }) }, c.href)) }),
917
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 border-t border-border px-1 pt-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(
918
+ NavLink,
919
+ {
920
+ item: { ...item, label: seeAll(item), children: void 0 },
921
+ className: "block rounded-xl px-2 py-2 text-[0.875rem]",
922
+ onNavigate: () => setOpen(false)
923
+ }
924
+ ) })
925
+ ] })
926
+ ] });
927
+ }
928
+ function SiteHeader({
929
+ home = "/",
930
+ brand,
931
+ product,
932
+ family,
933
+ nav = [],
934
+ search,
935
+ actions,
936
+ variant = "site",
937
+ navOnPhone = true,
938
+ menu: menuExtra,
939
+ className
940
+ }) {
941
+ const [open, setOpen] = react.useState(false);
942
+ const tool = variant === "tool";
943
+ const size = tool ? 24 : 28;
944
+ const current = typeof family === "string" ? family : void 0;
945
+ const phoneNav = navOnPhone ? nav : [];
946
+ const menu = phoneNav.length > 0 || Boolean(family) || Boolean(menuExtra);
947
+ const brandNode = brand ?? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center", children: [
948
+ /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size }),
949
+ product ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
950
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "mx-3 h-5 w-px bg-oca-hairline-strong" }),
951
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium tracking-[-0.01em]", style: { fontSize: size * 0.56 }, children: product })
952
+ ] }) : null
953
+ ] });
954
+ const navigation = nav.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Main", className: "hidden items-center gap-1 md:flex", children: nav.map(
955
+ (n) => n.children?.length ? /* @__PURE__ */ jsxRuntime.jsx(NavDropdown, { item: n }, n.href) : (
956
+ // Between md and lg the family menu and the actions share the row: tighter.
957
+ /* @__PURE__ */ jsxRuntime.jsx(NavLink, { item: n, line: tool, className: tool ? void 0 : "md:max-lg:px-2.5" }, n.href)
958
+ )
959
+ ) }) : null;
1011
960
  return /* @__PURE__ */ jsxRuntime.jsx(
1012
- SeparatorPrimitive__namespace.Root,
961
+ "header",
1013
962
  {
1014
- ref,
1015
- decorative,
1016
- orientation,
1017
963
  className: cn(
1018
- "shrink-0 bg-border",
1019
- orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
964
+ "sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur-md supports-[backdrop-filter]:bg-background/90",
965
+ tool ? "border-border" : "border-transparent",
1020
966
  className
1021
967
  ),
1022
- ...props
1023
- }
1024
- );
1025
- });
1026
- function sharedContext(name, initial) {
1027
- const key = /* @__PURE__ */ Symbol.for(`@orangecheck/app-design/${name}`);
1028
- const store = globalThis;
1029
- return store[key] ?? (store[key] = react.createContext(initial));
1030
- }
1031
-
1032
- // src/lib/density.ts
1033
- var DensityContext = sharedContext("density", "comfortable");
1034
- function DensityProvider({ density, children }) {
1035
- return react.createElement(DensityContext.Provider, { value: density }, children);
1036
- }
1037
- var ShellContext = sharedContext("shell-place", {});
1038
- function useShellPlace() {
1039
- return react.useContext(ShellContext);
968
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
969
+ "div",
970
+ {
971
+ className: cn(
972
+ "flex items-center justify-between gap-4",
973
+ tool ? "h-14 px-4 md:px-6" : "oca-container h-16 md:h-[4.5rem] md:max-lg:gap-2",
974
+ // Three columns, the outer two each at least their content: the
975
+ // navigation stays centred whatever the actions hold, so it does
976
+ // not shift when they change (signed out to signed in).
977
+ !tool && nav.length > 0 && "md:grid md:grid-cols-[minmax(max-content,1fr)_auto_minmax(max-content,1fr)]"
978
+ ),
979
+ children: [
980
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex min-w-0 items-center gap-1", tool ? "md:gap-1" : "justify-self-start"), children: [
981
+ /* @__PURE__ */ jsxRuntime.jsxs(Link__default.default, { href: home, className: "-ml-1 min-w-0 rounded-lg p-1", children: [
982
+ brandNode,
983
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: " home" })
984
+ ] }),
985
+ family ? /* @__PURE__ */ jsxRuntime.jsx(FamilyMenu, { current, className: "max-md:hidden" }) : null,
986
+ tool && navigation ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-5", children: navigation }) : null
987
+ ] }),
988
+ !tool ? navigation : null,
989
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 justify-self-end", children: [
990
+ search,
991
+ actions,
992
+ menu ? /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open, onOpenChange: setOpen, children: [
993
+ /* @__PURE__ */ jsxRuntime.jsx(
994
+ SheetTrigger,
995
+ {
996
+ className: "inline-flex size-10 items-center justify-center rounded-full text-foreground hover:bg-accent md:hidden",
997
+ "aria-label": "Open menu",
998
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "size-5" })
999
+ }
1000
+ ),
1001
+ /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "right", className: "pt-16", children: [
1002
+ /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: "Menu" }),
1003
+ menuExtra ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col", (phoneNav.length > 0 || family) && "border-b border-border pb-3"), children: menuExtra }) : null,
1004
+ phoneNav.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Main", className: "flex flex-col", children: phoneNav.map(
1005
+ (n) => n.children?.length ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col py-2", children: [
1006
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-1 text-[0.8125rem] text-muted-foreground", children: n.label }),
1007
+ n.children.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
1008
+ NavLink,
1009
+ {
1010
+ item: c,
1011
+ className: "rounded-xl px-3 py-2.5 text-[1.0625rem]",
1012
+ onNavigate: () => setOpen(false)
1013
+ },
1014
+ c.href
1015
+ ))
1016
+ ] }, n.href) : /* @__PURE__ */ jsxRuntime.jsx(
1017
+ NavLink,
1018
+ {
1019
+ item: n,
1020
+ className: "rounded-xl px-3 py-3 text-[1.0625rem]",
1021
+ onNavigate: () => setOpen(false)
1022
+ },
1023
+ n.href
1024
+ )
1025
+ ) }) : null,
1026
+ family ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: cn("group", phoneNav.length > 0 && "mt-2 border-t border-border pt-4"), children: [
1027
+ /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2.5 text-[1.0625rem] [&::-webkit-details-marker]:hidden", children: [
1028
+ "All orangecheck sites",
1029
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { "aria-hidden": true, className: "size-4 text-muted-foreground transition-transform group-open:rotate-180" })
1030
+ ] }),
1031
+ /* @__PURE__ */ jsxRuntime.jsx(FamilyList, { current, onNavigate: () => setOpen(false), stack: true, className: "mt-2" })
1032
+ ] }) : null
1033
+ ] })
1034
+ ] }) : null
1035
+ ] })
1036
+ ]
1037
+ }
1038
+ )
1039
+ }
1040
+ );
1040
1041
  }
1041
- var buttonVariants = classVarianceAuthority.cva(
1042
- [
1043
- "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-full font-medium",
1044
- "transition-[background-color,border-color,color,box-shadow,transform] duration-150",
1045
- "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1046
- "active:translate-y-px disabled:pointer-events-none",
1047
- // Disabled is one look for every variant: a quiet grey pill, never a
1048
- // faded black or orange slab that reads as broken.
1049
- "disabled:border disabled:border-border disabled:bg-muted disabled:text-oca-subtle disabled:shadow-none",
1050
- "[&_svg]:pointer-events-none [&_svg]:size-[1.1em] [&_svg]:shrink-0"
1051
- ],
1052
- {
1053
- variants: {
1054
- variant: {
1055
- primary: "bg-primary text-primary-foreground hover:bg-primary/85",
1056
- secondary: "border border-border bg-secondary text-secondary-foreground hover:border-oca-hairline-strong hover:bg-accent",
1057
- brand: "bg-brand text-brand-foreground hover:bg-oca-orange-deep",
1058
- ghost: "text-foreground hover:bg-accent",
1059
- danger: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
1060
- link: "h-auto rounded-none px-0 text-foreground underline decoration-foreground/35 underline-offset-4 hover:decoration-foreground",
1061
- onInk: "bg-white text-oca-ink hover:bg-white/90",
1062
- onInkOutline: "border border-white/20 text-white hover:bg-white/10"
1063
- },
1064
- size: {
1065
- sm: "h-9 px-4 text-sm",
1066
- md: "h-11 px-5 text-[0.9375rem]",
1067
- lg: "h-14 px-7 text-[1.0625rem]",
1068
- icon: "size-10",
1069
- "icon-sm": "size-8"
1070
- }
1071
- },
1072
- compoundVariants: [{ variant: "link", className: "h-auto px-0" }],
1073
- defaultVariants: { variant: "primary", size: "md" }
1074
- }
1075
- );
1076
- var Button = react.forwardRef(function Button2({ className, variant, size, asChild = false, loading = false, disabled, children, type, ...props }, ref) {
1077
- const Comp = asChild ? reactSlot.Slot : "button";
1042
+ function SkipLink({ href = "#main-content", children = "Skip to content" }) {
1078
1043
  return /* @__PURE__ */ jsxRuntime.jsx(
1079
- Comp,
1044
+ "a",
1080
1045
  {
1081
- ref,
1082
- "data-ui": "button",
1083
- className: cn(buttonVariants({ variant, size }), className),
1084
- disabled: asChild ? void 0 : disabled || loading,
1085
- "aria-busy": loading || void 0,
1086
- type: asChild ? void 0 : type ?? "button",
1087
- ...props,
1088
- children: asChild ? children : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1089
- loading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { className: "size-[1.1em]" }) : null,
1090
- children
1091
- ] })
1046
+ href,
1047
+ className: "sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-3 focus:z-50 focus:rounded-full focus:border focus:border-border focus:bg-card focus:px-4 focus:py-2 focus:text-[0.9375rem] focus:font-medium focus:text-foreground focus:shadow-md",
1048
+ children
1092
1049
  }
1093
1050
  );
1094
- });
1095
- function useActiveHeading(ids) {
1096
- const [active, setActive] = react.useState(ids[0]);
1097
- const key = ids.join("|");
1098
- react.useEffect(() => {
1099
- setActive(key.split("|")[0] || void 0);
1100
- const els = key.split("|").map((id) => document.getElementById(id)).filter((el) => el !== null);
1101
- if (els.length === 0 || typeof IntersectionObserver === "undefined") return;
1102
- const io = new IntersectionObserver(
1103
- (entries) => {
1104
- const hit = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];
1105
- if (hit) setActive(hit.target.id);
1106
- },
1107
- { rootMargin: "-80px 0px -65% 0px" }
1108
- );
1109
- els.forEach((el) => io.observe(el));
1110
- return () => io.disconnect();
1111
- }, [key]);
1112
- return active;
1113
- }
1114
- function useHeadings(selector = "#docs-content", key) {
1115
- const [items, setItems] = react.useState([]);
1116
- react.useEffect(() => {
1117
- const read = () => {
1118
- const root = document.querySelector(selector);
1119
- if (!root) return setItems([]);
1120
- setItems(
1121
- Array.from(root.querySelectorAll("h2[id], h3[id]")).map((h) => ({
1122
- id: h.id,
1123
- // The anchor link's "#" is not part of the name.
1124
- label: (h.querySelector("[data-heading-text]") ?? h).textContent?.trim() ?? "",
1125
- level: h.tagName === "H3" ? 3 : 2
1126
- }))
1127
- );
1128
- };
1129
- const raf = requestAnimationFrame(read);
1130
- const t = setTimeout(read, 80);
1131
- return () => {
1132
- cancelAnimationFrame(raf);
1133
- clearTimeout(t);
1134
- };
1135
- }, [selector, key]);
1136
- const activeId = useActiveHeading(items.map((i) => i.id));
1137
- return { items, activeId };
1138
1051
  }
1139
- function OnThisPage({
1140
- items,
1141
- activeId,
1142
- title = "On this page",
1143
- onNavigate,
1052
+ var BANNER_TONE = {
1053
+ neutral: "border-border bg-muted",
1054
+ brand: "border-[color-mix(in_oklab,var(--oca-orange)_30%,var(--border))] bg-[color-mix(in_oklab,var(--oca-orange)_9%,var(--background))]",
1055
+ danger: "border-[color-mix(in_oklab,var(--destructive)_35%,var(--border))] bg-[color-mix(in_oklab,var(--destructive)_8%,var(--background))]"
1056
+ };
1057
+ function SiteBanner({
1058
+ tone = "neutral",
1059
+ children,
1060
+ action,
1144
1061
  className
1145
1062
  }) {
1146
- const id = react.useId();
1147
- if (items.length === 0) return null;
1148
- return (
1149
- // With no visible title (a folded list under its own summary) the name is still "On this page".
1150
- /* @__PURE__ */ jsxRuntime.jsxs("nav", { "aria-labelledby": title ? id : void 0, "aria-label": title ? void 0 : "On this page", className, children: [
1151
- title ? /* @__PURE__ */ jsxRuntime.jsx("p", { id, className: "mb-3 text-[0.875rem] font-medium", children: title }) : null,
1152
- /* @__PURE__ */ jsxRuntime.jsx("ul", { children: items.map((t) => {
1153
- const on = t.id === activeId;
1154
- return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
1155
- "a",
1156
- {
1157
- href: `#${t.id}`,
1158
- onClick: onNavigate,
1159
- "aria-current": on ? "location" : void 0,
1160
- className: cn(
1161
- "block border-l py-1.5 text-[0.875rem] leading-snug transition-colors",
1162
- t.level === 3 ? "pl-7" : "pl-4",
1163
- on ? "border-foreground text-foreground" : "border-border text-muted-foreground hover:text-foreground"
1164
- ),
1165
- children: t.label
1166
- }
1167
- ) }, t.id);
1168
- }) })
1063
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "data-tone": tone, className: cn("border-b text-[0.875rem]", BANNER_TONE[tone], className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oca-container flex flex-wrap items-center justify-between gap-x-4 gap-y-1 py-2.5", children: [
1064
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex min-w-0 items-baseline gap-2.5 leading-snug", children: [
1065
+ tone !== "neutral" ? /* @__PURE__ */ jsxRuntime.jsx(
1066
+ "span",
1067
+ {
1068
+ "aria-hidden": true,
1069
+ className: cn(
1070
+ "size-2 shrink-0 translate-y-[-0.05em] rounded-full",
1071
+ tone === "brand" ? "bg-oca-orange" : "bg-destructive"
1072
+ )
1073
+ }
1074
+ ) : null,
1075
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children })
1076
+ ] }),
1077
+ action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("shrink-0 font-medium", tone !== "neutral" && "max-sm:pl-[1.125rem]"), children: action }) : null
1078
+ ] }) });
1079
+ }
1080
+ var FOOT_LINK = "inline-flex min-h-11 items-center transition-colors hover:text-foreground md:min-h-0";
1081
+ function LegalRow({ links }) {
1082
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-x-6 gap-y-2 sm:flex-row sm:items-center sm:justify-between", children: [
1083
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Legal", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-wrap gap-x-5", children: links.map((l) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: l.href.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: l.href, className: FOOT_LINK, children: l.label }) : /* @__PURE__ */ jsxRuntime.jsx("a", { href: l.href, className: FOOT_LINK, children: l.label }) }, l.href)) }) }),
1084
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
1085
+ "\xA9 ",
1086
+ (/* @__PURE__ */ new Date()).getFullYear(),
1087
+ " OrangeCheck"
1169
1088
  ] })
1170
- );
1089
+ ] });
1171
1090
  }
1172
- var STATUS_WORDS = /* @__PURE__ */ new Set(["live", "testing", "not-yet", "paused", "good", "caution", "refused"]);
1173
- var pathOf = (href) => href.split(/[?#]/)[0].replace(/(.)\/$/, "$1");
1174
- function DocsNav({
1175
- sections,
1176
- current,
1177
- onNavigate,
1091
+ function SiteFooter({
1092
+ columns = [],
1093
+ family = false,
1094
+ legal = false,
1095
+ compact = false,
1096
+ fineprint,
1097
+ meta,
1098
+ signature = true,
1178
1099
  className
1179
1100
  }) {
1180
- const here = pathOf(current);
1181
- const activeSection = react.useMemo(
1182
- () => sections.findIndex((s) => s.items.some((i) => pathOf(i.href) === here)),
1183
- [sections, here]
1184
- );
1185
- const [peeked, setPeeked] = react.useState(() => /* @__PURE__ */ new Set());
1186
- react.useEffect(() => setPeeked(/* @__PURE__ */ new Set()), [activeSection]);
1187
- const activeRef = react.useRef(null);
1188
- react.useEffect(() => {
1189
- const id = requestAnimationFrame(() => activeRef.current?.scrollIntoView({ block: "nearest" }));
1190
- return () => cancelAnimationFrame(id);
1191
- }, [here]);
1192
- const base = react.useId();
1193
- return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Docs", className: cn("flex flex-col gap-0.5", className), children: sections.map((section, si) => {
1194
- const isActive = si === activeSection;
1195
- const open = isActive || peeked.has(si);
1196
- const panel = `${base}-s${si}`;
1197
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1198
- /* @__PURE__ */ jsxRuntime.jsxs(
1199
- "button",
1200
- {
1201
- type: "button",
1202
- "aria-expanded": open,
1203
- "aria-controls": panel,
1204
- onClick: () => {
1205
- if (isActive) return;
1206
- setPeeked((p) => {
1207
- const n = new Set(p);
1208
- if (n.has(si)) n.delete(si);
1209
- else n.add(si);
1210
- return n;
1211
- });
1212
- },
1213
- className: cn(
1214
- "flex min-h-9 w-full items-center justify-between gap-2 rounded-lg px-2.5 py-1.5 text-left text-[0.875rem] transition-colors",
1215
- isActive ? "font-medium text-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground",
1216
- isActive && "cursor-default"
1217
- ),
1218
- children: [
1219
- section.label,
1220
- /* @__PURE__ */ jsxRuntime.jsx(
1221
- lucideReact.ChevronRight,
1222
- {
1223
- "aria-hidden": true,
1224
- className: cn("size-3.5 shrink-0 text-oca-subtle transition-transform duration-200", open && "rotate-90")
1225
- }
1226
- )
1227
- ]
1228
- }
1229
- ),
1230
- /* @__PURE__ */ jsxRuntime.jsx("ul", { id: panel, hidden: !open, className: "mb-2 ml-2.5 mt-0.5 border-l border-border", children: section.items.map((item) => {
1231
- const on = pathOf(item.href) === here;
1232
- const badge = typeof item.badge === "string" && STATUS_WORDS.has(item.badge) ? /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: item.badge }) : item.badge ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { tone: "muted", children: item.badge }) : null;
1233
- return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
1234
- Link5__default.default,
1235
- {
1236
- href: item.href,
1237
- ref: on ? activeRef : void 0,
1238
- onClick: onNavigate,
1239
- "aria-current": on ? "page" : void 0,
1240
- className: cn(
1241
- "-ml-px flex min-h-9 items-center gap-2 border-l-2 py-1.5 pl-3.5 pr-2 text-[0.875rem] leading-snug transition-colors",
1242
- on ? "border-foreground font-medium text-foreground" : "border-transparent text-muted-foreground hover:border-oca-hairline-strong hover:text-foreground"
1243
- ),
1244
- children: [
1245
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1", children: item.label }),
1246
- badge
1247
- ]
1248
- }
1249
- ) }, item.href);
1250
- }) })
1251
- ] }, section.label);
1252
- }) });
1253
- }
1254
- var DOCS_HEADER_NAV = OCHK_IO_NAV.map((n) => n.href === DOCS_HREF ? { ...n, active: true } : n);
1255
- function DocsShell({
1256
- nav,
1257
- current,
1258
- header,
1259
- search,
1260
- actions,
1261
- breadcrumbs,
1262
- toc,
1263
- pager,
1264
- footer,
1265
- children
1266
- }) {
1267
- const [menu, setMenu] = react.useState(false);
1268
- react.useEffect(() => setMenu(false), [current]);
1269
- const read = useHeadings("#docs-content", current);
1270
- const items = toc ?? read.items;
1271
- const activeId = toc ? void 0 : read.activeId;
1272
- const here = pathOf(current);
1273
- const place = react.useMemo(() => {
1274
- for (const s of nav) {
1275
- const it = s.items.find((i) => pathOf(i.href) === here);
1276
- if (it) return { section: s.label, page: it.label };
1277
- }
1278
- return null;
1279
- }, [nav, here]);
1280
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-dvh flex-col bg-background", children: [
1281
- /* @__PURE__ */ jsxRuntime.jsx(SkipLink, {}),
1282
- header ?? /* @__PURE__ */ jsxRuntime.jsx(SiteHeader, { home: OCHK_IO, family: "docs", nav: DOCS_HEADER_NAV, search, actions }),
1283
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-16 z-30 border-b border-border bg-background/95 backdrop-blur-md supports-[backdrop-filter]:bg-background/90 md:top-[4.5rem] lg:hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oca-container flex h-12 items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open: menu, onOpenChange: setMenu, children: [
1284
- /* @__PURE__ */ jsxRuntime.jsxs(SheetTrigger, { className: "-ml-2 inline-flex h-10 min-w-0 items-center gap-2 rounded-full px-2.5 text-[0.875rem] hover:bg-accent", children: [
1285
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelLeft, { "aria-hidden": true, className: "size-[1.125rem] shrink-0" }),
1286
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Docs menu. " }),
1287
- place ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
1288
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: place.section }),
1289
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": true, className: "size-3.5 shrink-0 text-oca-subtle" }),
1290
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", children: place.page })
1291
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Docs" })
1292
- ] }),
1293
- /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "left", className: "w-[min(88vw,22rem)] gap-0 p-0", children: [
1294
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-border px-5 py-4 pr-14", children: /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "text-[1.0625rem] font-medium", children: "Docs" }) }),
1295
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-y-auto p-3", children: /* @__PURE__ */ jsxRuntime.jsx(DocsNav, { sections: nav, current, onNavigate: () => setMenu(false) }) })
1296
- ] })
1297
- ] }) }) }),
1298
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oca-container flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:grid lg:grid-cols-[13.5rem_minmax(0,1fr)] lg:gap-x-10 xl:grid-cols-[13.5rem_minmax(0,1fr)_11rem]", children: [
1299
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-[4.5rem] hidden max-h-[calc(100dvh-4.5rem)] self-start overflow-y-auto overscroll-contain py-8 pr-1 lg:block", children: /* @__PURE__ */ jsxRuntime.jsx(DocsNav, { sections: nav, current }) }),
1300
- /* @__PURE__ */ jsxRuntime.jsxs("main", { id: "main-content", className: "min-w-0 pb-16 pt-6 md:pt-8", children: [
1301
- breadcrumbs ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 max-lg:hidden", children: breadcrumbs }) : null,
1302
- items.length > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group mb-8 rounded-2xl border border-border bg-card xl:hidden", children: [
1303
- /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex min-h-12 cursor-pointer list-none items-center justify-between px-4 text-[0.9375rem] font-medium [&::-webkit-details-marker]:hidden", children: [
1304
- "On this page",
1305
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { "aria-hidden": true, className: "size-4 text-muted-foreground transition-transform group-open:rotate-180" })
1306
- ] }),
1307
- /* @__PURE__ */ jsxRuntime.jsx(OnThisPage, { items, activeId, title: null, className: "px-4 pb-4" })
1308
- ] }) : null,
1309
- /* @__PURE__ */ jsxRuntime.jsx("article", { id: "docs-content", className: "max-w-[46rem]", children }),
1310
- pager ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-16 max-w-[46rem]", children: pager }) : null
1101
+ const legalLinks = legal === true ? LEGAL_LINKS : legal || null;
1102
+ if (compact) {
1103
+ return /* @__PURE__ */ jsxRuntime.jsx("footer", { className: cn("border-t border-border bg-background text-[0.875rem] text-muted-foreground", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oca-container flex flex-col gap-x-8 gap-y-3 py-5 lg:flex-row lg:items-center lg:justify-between", children: [
1104
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-1", children: [
1105
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: OCHK_IO, className: "rounded-lg text-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 22 }) }),
1106
+ family ? /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Protocols", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-wrap gap-x-4", children: PROTOCOLS.map((p) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: p.pageHref, className: FOOT_LINK, children: p.label }) }, p.slug)) }) }) : null
1311
1107
  ] }),
1312
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-[4.5rem] hidden max-h-[calc(100dvh-4.5rem)] self-start overflow-y-auto py-10 xl:block", children: items.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(OnThisPage, { items, activeId }) : null })
1313
- ] }) }),
1314
- footer ?? /* @__PURE__ */ jsxRuntime.jsx(SiteFooter, { columns: OCHK_IO_FOOTER, family: true, legal: true, signature: false })
1315
- ] });
1316
- }
1317
- function SideNav({ groups, onNavigate }) {
1318
- return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Pages", className: "flex flex-col gap-5", children: groups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
1319
- g.label ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-1 text-[0.8125rem] text-muted-foreground", children: g.label }) : null,
1320
- g.items.map((it) => /* @__PURE__ */ jsxRuntime.jsxs(
1321
- Link5__default.default,
1108
+ legalLinks || meta ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-x-6 gap-y-2 sm:flex-row sm:flex-wrap sm:items-center", children: [
1109
+ legalLinks ? /* @__PURE__ */ jsxRuntime.jsx(LegalRow, { links: legalLinks }) : null,
1110
+ meta
1111
+ ] }) : null
1112
+ ] }) });
1113
+ }
1114
+ const cols = [
1115
+ ...columns,
1116
+ ...family ? familyByGroup().map((g) => ({
1117
+ // The lockup above already says orangecheck.
1118
+ heading: g.id === "orangecheck" ? "Start here" : g.label,
1119
+ links: g.sites.map((s) => ({ href: s.href, label: s.label, external: true }))
1120
+ })) : []
1121
+ ];
1122
+ const n = cols.length;
1123
+ const inline = n <= 5;
1124
+ const grid = {
1125
+ "--oca-footer-md": `repeat(${Math.max(1, Math.min(n, 4))}, minmax(0, 1fr))`,
1126
+ "--oca-footer-lg": inline ? `minmax(0, 1.3fr) repeat(${Math.max(1, n)}, minmax(0, 1fr))` : `repeat(${Math.min(n, 6)}, minmax(0, 1fr))`
1127
+ };
1128
+ return /* @__PURE__ */ jsxRuntime.jsxs("footer", { className: cn("border-t border-border bg-background", className), children: [
1129
+ /* @__PURE__ */ jsxRuntime.jsxs(
1130
+ "div",
1322
1131
  {
1323
- href: it.href,
1324
- onClick: onNavigate,
1325
- "aria-current": it.active ? "page" : void 0,
1326
- className: cn(
1327
- "flex h-9 items-center gap-3 rounded-xl px-3 text-[0.9375rem] transition-colors",
1328
- "[&_svg]:size-[1.0625rem] [&_svg]:shrink-0",
1329
- it.active ? "bg-card text-foreground shadow-sm ring-1 ring-border [&_svg]:text-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground"
1330
- ),
1132
+ style: grid,
1133
+ className: "oca-container grid grid-cols-2 gap-x-6 gap-y-10 py-14 md:grid-cols-[var(--oca-footer-md)] md:py-16 lg:grid-cols-[var(--oca-footer-lg)]",
1331
1134
  children: [
1332
- it.icon,
1333
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: it.label }),
1334
- it.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: it.badge }) : null
1135
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("col-span-full flex flex-col gap-4", inline && "lg:col-span-1"), children: [
1136
+ /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 26 }),
1137
+ fineprint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-xs text-[0.9375rem] leading-relaxed text-muted-foreground", children: fineprint }) : null
1138
+ ] }),
1139
+ cols.map((col, i) => {
1140
+ const spans = n % 2 === 1 && i === n - 1;
1141
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1142
+ "div",
1143
+ {
1144
+ className: cn("flex min-w-0 flex-col gap-1", spans && "col-span-2 md:col-span-1"),
1145
+ children: [
1146
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[0.875rem] font-medium", children: col.heading }),
1147
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col gap-1", spans && "grid grid-cols-2 gap-x-6 md:flex md:flex-col"), children: col.links.map((l) => /* @__PURE__ */ jsxRuntime.jsx(
1148
+ NavLink,
1149
+ {
1150
+ item: l,
1151
+ className: "-mx-3 py-2.5 text-[0.9375rem] md:py-1.5"
1152
+ },
1153
+ l.href
1154
+ )) })
1155
+ ]
1156
+ },
1157
+ col.heading
1158
+ );
1159
+ })
1335
1160
  ]
1336
- },
1337
- it.href
1338
- ))
1339
- ] }, g.label ?? gi)) });
1161
+ }
1162
+ ),
1163
+ signature ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "oca-container select-none overflow-hidden pb-8 md:pb-12", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-[0.18em] text-[clamp(3rem,11.5vw,10rem)] leading-[0.78] tracking-[-0.065em] [font-family:var(--oc-font-display)]", children: [
1164
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-[0.06em] inline-block w-[1.1em] shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(Mark, { className: "block h-auto w-full" }) }),
1165
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "orangecheck" })
1166
+ ] }) }) : null,
1167
+ legalLinks || meta ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oca-container", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 border-t border-border py-6 text-[0.875rem] text-muted-foreground", children: [
1168
+ legalLinks ? /* @__PURE__ */ jsxRuntime.jsx(LegalRow, { links: legalLinks }) : null,
1169
+ meta
1170
+ ] }) }) : null
1171
+ ] });
1340
1172
  }
1341
- function SectionSwitch({
1342
- sections,
1343
- current,
1344
- onNavigate
1345
- }) {
1346
- const cols = sections.length <= 3 ? sections.length : 2;
1173
+ function OchkIoFooter({ columns = OCHK_IO_FOOTER, className }) {
1347
1174
  return /* @__PURE__ */ jsxRuntime.jsx(
1348
- "nav",
1175
+ SiteFooter,
1349
1176
  {
1350
- "aria-label": "Sections",
1351
- className: cn("grid gap-1 bg-muted p-1", cols === sections.length ? "rounded-full" : "rounded-[1.25rem]"),
1352
- style: { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` },
1353
- children: sections.map((sec) => {
1354
- const on = sec.id === current.id;
1355
- return /* @__PURE__ */ jsxRuntime.jsx(
1356
- Link5__default.default,
1357
- {
1358
- href: sec.href,
1359
- onClick: onNavigate,
1360
- "aria-current": on ? "true" : void 0,
1361
- className: cn(
1362
- "flex h-8 min-w-0 items-center justify-center rounded-full px-1.5 text-[0.8125rem] font-medium transition-colors",
1363
- on ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
1364
- ),
1365
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: sec.label })
1366
- },
1367
- sec.id
1368
- );
1369
- })
1177
+ columns,
1178
+ family: true,
1179
+ legal: true,
1180
+ fineprint: "Open protocols on Bitcoin. Anyone can check the result.",
1181
+ meta: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
1182
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
1183
+ "Open-source work on orangecheck is ",
1184
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-nowrap", children: "supported by OpenSats." })
1185
+ ] }),
1186
+ /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, { className: "-mx-3" })
1187
+ ] }),
1188
+ className
1370
1189
  }
1371
1190
  );
1372
1191
  }
1373
- function TabBar({ items }) {
1374
- return /* @__PURE__ */ jsxRuntime.jsx(
1375
- "nav",
1376
- {
1377
- "aria-label": "Pages",
1378
- "data-oca-tabbar": "",
1379
- className: "fixed inset-x-0 bottom-0 z-40 border-t border-border bg-background/90 pb-[env(safe-area-inset-bottom)] backdrop-blur-md md:hidden",
1380
- children: /* @__PURE__ */ jsxRuntime.jsx(
1381
- "div",
1382
- {
1383
- className: "mx-auto grid h-14 max-w-[32rem] px-1.5",
1384
- style: { gridTemplateColumns: `repeat(${items.length}, minmax(0, 1fr))` },
1385
- children: items.map((it) => /* @__PURE__ */ jsxRuntime.jsxs(
1386
- Link5__default.default,
1387
- {
1388
- href: it.href,
1389
- "aria-current": it.active ? "page" : void 0,
1390
- className: cn(
1391
- "flex min-w-0 flex-col items-center justify-center gap-1 rounded-2xl text-[0.6875rem] leading-none outline-none transition-colors [-webkit-tap-highlight-color:transparent] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
1392
- it.active ? "text-foreground" : "text-muted-foreground hover:text-foreground"
1393
- ),
1394
- children: [
1395
- /* @__PURE__ */ jsxRuntime.jsxs(
1396
- "span",
1397
- {
1398
- className: cn(
1399
- "relative flex h-7 w-12 items-center justify-center rounded-full transition-[background-color,box-shadow] duration-200",
1400
- "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
1401
- it.active ? "bg-card shadow-sm ring-1 ring-border [&_svg]:stroke-2" : "[&_svg]:stroke-[1.75]"
1402
- ),
1403
- children: [
1404
- it.icon,
1405
- it.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "absolute right-2.5 top-0.5 size-2 rounded-full bg-oca-orange ring-2 ring-background" }) : null
1406
- ]
1407
- }
1408
- ),
1409
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-full truncate px-0.5", children: it.label }),
1410
- it.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: it.badge }) : null
1411
- ]
1412
- },
1413
- it.href
1414
- ))
1415
- }
1416
- )
1192
+ function IsoCheck({ size = 360, settle = false, shadow = true, tilt = false, title, className }) {
1193
+ const ref = react.useRef(null);
1194
+ react.useEffect(() => {
1195
+ const el = ref.current;
1196
+ if (!el || !tilt || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
1197
+ let raf = 0;
1198
+ let tx = 0;
1199
+ let ty = 0;
1200
+ let x = 0;
1201
+ let y = 0;
1202
+ const step = () => {
1203
+ x += (tx - x) * 0.08;
1204
+ y += (ty - y) * 0.08;
1205
+ el.style.setProperty("--rx", `${(-y * 10).toFixed(2)}deg`);
1206
+ el.style.setProperty("--ry", `${(x * 14).toFixed(2)}deg`);
1207
+ raf = Math.abs(tx - x) + Math.abs(ty - y) > 1e-3 ? requestAnimationFrame(step) : 0;
1208
+ };
1209
+ const move = (e) => {
1210
+ const b = el.getBoundingClientRect();
1211
+ tx = Math.max(-1, Math.min(1, (e.clientX - (b.left + b.width / 2)) / (window.innerWidth / 2)));
1212
+ ty = Math.max(-1, Math.min(1, (e.clientY - (b.top + b.height / 2)) / (window.innerHeight / 2)));
1213
+ if (!raf) raf = requestAnimationFrame(step);
1214
+ };
1215
+ window.addEventListener("pointermove", move, { passive: true });
1216
+ return () => {
1217
+ cancelAnimationFrame(raf);
1218
+ window.removeEventListener("pointermove", move);
1219
+ };
1220
+ }, [tilt]);
1221
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1222
+ "div",
1223
+ {
1224
+ ref,
1225
+ className: cn("relative inline-block", settle && "oca-animate-settle", className),
1226
+ style: { width: size, perspective: tilt ? 900 : void 0 },
1227
+ children: [
1228
+ tilt ? /* @__PURE__ */ jsxRuntime.jsx(
1229
+ "div",
1230
+ {
1231
+ "aria-hidden": true,
1232
+ className: "absolute left-1/2 top-1/2 -z-0 aspect-square w-[120%] -translate-x-1/2 -translate-y-1/2 rounded-full opacity-70 blur-3xl",
1233
+ style: { background: "radial-gradient(closest-side, rgb(255 140 70 / 0.35), transparent)" }
1234
+ }
1235
+ ) : null,
1236
+ /* @__PURE__ */ jsxRuntime.jsx(
1237
+ "div",
1238
+ {
1239
+ className: "relative z-10 transition-transform duration-300 ease-out",
1240
+ style: tilt ? { transform: "rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg))", transformStyle: "preserve-3d" } : void 0,
1241
+ children: /* @__PURE__ */ jsxRuntime.jsx(Mark, { size, title, className: "block" })
1242
+ }
1243
+ ),
1244
+ shadow ? /* @__PURE__ */ jsxRuntime.jsx(
1245
+ "div",
1246
+ {
1247
+ "aria-hidden": true,
1248
+ className: "absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl",
1249
+ style: { height: size * 0.08, bottom: -size * 0.04, background: "rgb(234 88 11 / 0.18)" }
1250
+ }
1251
+ ) : null
1252
+ ]
1417
1253
  }
1418
1254
  );
1419
1255
  }
1420
- function tabsFor(groups) {
1421
- const items = groups.flatMap((g) => g.items);
1422
- return items.length >= 2 && items.length <= 5 && items.every((it) => it.icon) ? items : null;
1423
- }
1424
- var activeItem = (groups) => groups.flatMap((g) => g.items).find((it) => it.active);
1425
- var warned = /* @__PURE__ */ new Set();
1426
- function warnDuplicateLabels(groups) {
1427
- if (typeof process === "undefined" || process.env.NODE_ENV === "production") return;
1428
- const seen = /* @__PURE__ */ new Set();
1429
- for (const it of groups.flatMap((g) => g.items)) {
1430
- if (seen.has(it.label) && !warned.has(it.label)) {
1431
- warned.add(it.label);
1432
- console.warn(`AppShell: two pages are labelled "${it.label}"; give each its own name.`);
1433
- }
1434
- seen.add(it.label);
1435
- }
1436
- }
1437
- function AppShell({
1438
- nav,
1439
- sections,
1440
- activeSection,
1441
- page: pageOverride,
1442
- persistent = [],
1443
- home = "/",
1444
- account,
1445
- banner,
1446
- footer,
1256
+ function SignInFrame({
1257
+ title = "Sign in",
1258
+ lead,
1447
1259
  children,
1260
+ error,
1261
+ fineprint,
1262
+ brand,
1263
+ home = "/",
1264
+ back,
1265
+ art,
1266
+ caption,
1448
1267
  className
1449
1268
  }) {
1450
- const [open, setOpen] = react.useState(false);
1451
- const current = sections && sections.length > 0 ? sections.find((sec) => sec.id === activeSection) ?? sections.find((sec) => activeItem(sec.groups)) ?? sections[0] : void 0;
1452
- const groups = current ? [...current.groups, ...persistent] : [...nav ?? [], ...persistent];
1453
- warnDuplicateLabels(groups);
1454
- const tabs = current ? null : tabsFor(groups);
1455
- const page = activeItem(groups);
1456
- const place = react.useMemo(
1457
- () => ({
1458
- section: current ? current.title ?? current.label : void 0,
1459
- page: pageOverride ?? page?.label
1460
- }),
1461
- [current, page?.label, pageOverride]
1462
- );
1463
- const switcher = sections && current && sections.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(SectionSwitch, { sections, current, onNavigate: () => setOpen(false) }) : null;
1464
- return /* @__PURE__ */ jsxRuntime.jsx(ShellContext.Provider, { value: place, children: /* @__PURE__ */ jsxRuntime.jsxs(
1465
- "div",
1269
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1270
+ "main",
1466
1271
  {
1467
- className: cn(
1468
- "min-h-dvh bg-background",
1469
- tabs && "max-md:pb-[calc(3.5rem_+_env(safe-area-inset-bottom))]",
1470
- className
1471
- ),
1272
+ id: "main-content",
1273
+ className: cn("grid min-h-dvh grid-cols-1 bg-background text-foreground lg:grid-cols-2", className),
1472
1274
  children: [
1473
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sticky top-0 z-40 border-b border-border bg-background/90 backdrop-blur-md lg:hidden", children: [
1474
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-14 items-center gap-2 px-3", children: [
1475
- /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open, onOpenChange: setOpen, children: [
1476
- /* @__PURE__ */ jsxRuntime.jsx(
1477
- SheetTrigger,
1478
- {
1479
- className: cn(
1480
- "inline-flex size-10 items-center justify-center rounded-full hover:bg-accent",
1481
- // The tab bar holds every page; the sheet is left for a footer.
1482
- tabs && !footer && "max-md:hidden"
1483
- ),
1484
- "aria-label": "Open navigation",
1485
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "size-5" })
1486
- }
1487
- ),
1488
- /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "left", className: "flex flex-col gap-6 overflow-y-auto p-4 pt-16", children: [
1489
- /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: "Navigation" }),
1490
- switcher,
1491
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: tabs ? "max-md:hidden" : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups, onNavigate: () => setOpen(false) }) }),
1492
- footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer }) : null
1493
- ] })
1275
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "flex flex-col px-4 pb-8 pt-5 sm:px-8 lg:px-14 lg:pt-8", children: [
1276
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex items-center justify-between gap-4", children: [
1277
+ /* @__PURE__ */ jsxRuntime.jsxs(Link__default.default, { href: home, className: "w-fit rounded-lg", children: [
1278
+ brand ?? /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 28 }),
1279
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: " home" })
1494
1280
  ] }),
1495
- /* @__PURE__ */ jsxRuntime.jsx(Link5__default.default, { href: home, "aria-label": "Home", className: "rounded-lg p-1", children: /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 22 }) }),
1496
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex items-center", children: account })
1497
- ] }),
1498
- place.page ? /* @__PURE__ */ jsxRuntime.jsxs(
1499
- "div",
1500
- {
1501
- className: cn(
1502
- "flex h-9 items-center gap-1.5 border-t border-border px-4 text-[0.875rem]",
1503
- // The active tab already names the page.
1504
- tabs && !pageOverride && "max-md:hidden"
1505
- ),
1506
- children: [
1507
- place.section ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1508
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: place.section }),
1509
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": true, className: "size-3.5 shrink-0 text-muted-foreground" })
1510
- ] }) : null,
1511
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", children: place.page })
1512
- ]
1513
- }
1514
- ) : null
1515
- ] }),
1516
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-[90rem]", children: [
1517
- /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "sticky top-0 hidden h-dvh w-[17rem] shrink-0 flex-col gap-6 overflow-y-auto px-4 py-6 lg:flex", children: [
1518
- /* @__PURE__ */ jsxRuntime.jsx(Link5__default.default, { href: home, className: "px-2", "aria-label": "Home", children: /* @__PURE__ */ jsxRuntime.jsx(Lockup, { size: 26 }) }),
1519
- switcher,
1520
- /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups }),
1521
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer })
1281
+ back === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1282
+ Link__default.default,
1283
+ {
1284
+ href: home,
1285
+ className: "text-[0.9375rem] text-muted-foreground underline-offset-4 hover:text-foreground hover:underline",
1286
+ children: "Back to home"
1287
+ }
1288
+ ) : back
1522
1289
  ] }),
1523
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
1524
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden h-[4.5rem] items-center justify-end gap-3 px-8 lg:flex", children: account }),
1525
- banner ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pb-2 pt-4 md:px-8 lg:pt-0", children: banner }) : null,
1526
- /* @__PURE__ */ jsxRuntime.jsx("main", { id: "main-content", className: "flex-1 px-4 pb-16 pt-6 md:px-8 lg:pt-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full max-w-[72rem]", children }) })
1290
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-[25rem] flex-1 flex-col justify-center gap-9 py-12", children: [
1291
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
1292
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "oca-display-2", children: title }),
1293
+ lead ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oca-lead", children: lead }) : null
1294
+ ] }),
1295
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children }),
1296
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-[0.9375rem] text-destructive", children: error }) : null,
1297
+ fineprint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.8125rem] leading-relaxed text-muted-foreground", children: fineprint }) : null
1527
1298
  ] })
1528
1299
  ] }),
1529
- tabs ? /* @__PURE__ */ jsxRuntime.jsx(TabBar, { items: tabs }) : null
1300
+ /* @__PURE__ */ jsxRuntime.jsxs(
1301
+ "aside",
1302
+ {
1303
+ "aria-hidden": true,
1304
+ className: "relative hidden flex-col justify-between gap-10 overflow-hidden border-l border-[var(--oca-band-line)] bg-oca-ink p-14 text-oca-ink-foreground lg:flex",
1305
+ children: [
1306
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: art ?? /* @__PURE__ */ jsxRuntime.jsx(IsoCheck, { size: 340, settle: true, tilt: true, className: "max-w-full" }) }),
1307
+ caption ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-w-[24rem] flex-col gap-3", children: caption }) : null
1308
+ ]
1309
+ }
1310
+ )
1530
1311
  ]
1531
1312
  }
1532
- ) });
1313
+ );
1533
1314
  }
1534
- function AccountMenu({
1535
- name,
1536
- detail,
1537
- items,
1538
- nameFrom = "sm"
1539
- }) {
1540
- const initial = name.trim().charAt(0).toUpperCase() || "\xB7";
1541
- return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
1542
- /* @__PURE__ */ jsxRuntime.jsxs(
1543
- DropdownMenuTrigger,
1544
- {
1545
- className: cn(
1546
- "inline-flex h-10 items-center gap-2.5 rounded-full border border-border bg-card pl-1 pr-1 text-[0.9375rem] outline-none transition-colors hover:border-oca-hairline-strong focus-visible:ring-2 focus-visible:ring-ring",
1547
- nameFrom === "lg" ? "lg:pr-3.5" : "sm:pr-3.5"
1548
- ),
1549
- "aria-label": `${name}, account menu`,
1550
- children: [
1551
- /* @__PURE__ */ jsxRuntime.jsx(
1552
- "span",
1553
- {
1554
- "aria-hidden": true,
1555
- "data-initial": initial,
1556
- className: "flex size-8 items-center justify-center rounded-full bg-foreground text-[0.8125rem] font-medium text-background before:content-[attr(data-initial)]"
1557
- }
1558
- ),
1559
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("max-w-[10rem] truncate", nameFrom === "lg" ? "max-lg:hidden" : "max-sm:hidden"), children: name })
1560
- ]
1561
- }
1562
- ),
1563
- /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuContent, { align: "end", className: "w-64", children: [
1564
- /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuLabel, { className: "flex flex-col gap-0.5", children: [
1565
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[0.9375rem] font-medium text-foreground", children: name }),
1566
- detail ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-mono text-[0.75rem]", children: detail }) : null
1567
- ] }),
1568
- /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}),
1569
- items.map(
1570
- (it, i) => it === "separator" ? /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}, `sep-${i}`) : it.href ? /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuItem, { asChild: true, destructive: it.destructive, children: /* @__PURE__ */ jsxRuntime.jsxs(Link5__default.default, { href: it.href, children: [
1571
- it.icon,
1572
- it.label
1573
- ] }) }, it.label) : /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onSelect: it.onSelect, destructive: it.destructive, children: [
1574
- it.icon,
1575
- it.label
1576
- ] }, it.label)
1577
- )
1578
- ] })
1579
- ] });
1315
+ function Spinner({ className, label }) {
1316
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1317
+ "svg",
1318
+ {
1319
+ viewBox: "0 0 24 24",
1320
+ fill: "none",
1321
+ role: label ? "status" : void 0,
1322
+ "aria-label": label,
1323
+ "aria-hidden": label ? void 0 : true,
1324
+ className: cn("size-4 animate-spin", className),
1325
+ children: [
1326
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeOpacity: "0.2", strokeWidth: "2.5" }),
1327
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 0 0-9-9", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" })
1328
+ ]
1329
+ }
1330
+ );
1580
1331
  }
1581
- function ThemeToggle({ className }) {
1582
- const { theme, setTheme } = nextThemes.useTheme();
1583
- const [mounted, setMounted] = react.useState(false);
1584
- react.useEffect(() => setMounted(true), []);
1585
- const current = mounted ? theme ?? "system" : "system";
1586
- const next = current === "light" ? "dark" : current === "dark" ? "system" : "light";
1587
- const Icon = current === "light" ? lucideReact.Sun : current === "dark" ? lucideReact.Moon : lucideReact.SunMoon;
1332
+ react.forwardRef(function Separator3({ className, orientation = "horizontal", decorative = true, ...props }, ref) {
1588
1333
  return /* @__PURE__ */ jsxRuntime.jsx(
1589
- "button",
1334
+ SeparatorPrimitive__namespace.Root,
1590
1335
  {
1591
- type: "button",
1592
- onClick: () => setTheme(next),
1593
- "aria-label": `Theme: ${current}. Switch to ${next}`,
1594
- title: `Theme: ${current}`,
1336
+ ref,
1337
+ decorative,
1338
+ orientation,
1595
1339
  className: cn(
1596
- "inline-flex size-10 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1340
+ "shrink-0 bg-border",
1341
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
1597
1342
  className
1598
1343
  ),
1599
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-[1.125rem]" })
1344
+ ...props
1600
1345
  }
1601
1346
  );
1347
+ });
1348
+ var DensityContext = sharedContext("density", "comfortable");
1349
+ function DensityProvider({ density, children }) {
1350
+ return react.createElement(DensityContext.Provider, { value: density }, children);
1351
+ }
1352
+ var buttonVariants = classVarianceAuthority.cva(
1353
+ [
1354
+ "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-full font-medium",
1355
+ "transition-[background-color,border-color,color,box-shadow,transform] duration-150",
1356
+ "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1357
+ "active:translate-y-px disabled:pointer-events-none",
1358
+ // Disabled is one look for every variant: a quiet grey pill, never a
1359
+ // faded black or orange slab that reads as broken.
1360
+ "disabled:border disabled:border-border disabled:bg-muted disabled:text-oca-subtle disabled:shadow-none",
1361
+ "[&_svg]:pointer-events-none [&_svg]:size-[1.1em] [&_svg]:shrink-0"
1362
+ ],
1363
+ {
1364
+ variants: {
1365
+ variant: {
1366
+ primary: "bg-primary text-primary-foreground hover:bg-primary/85",
1367
+ secondary: "border border-border bg-secondary text-secondary-foreground hover:border-oca-hairline-strong hover:bg-accent",
1368
+ brand: "bg-brand text-brand-foreground hover:bg-oca-orange-deep",
1369
+ ghost: "text-foreground hover:bg-accent",
1370
+ danger: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
1371
+ link: "h-auto rounded-none px-0 text-foreground underline decoration-foreground/35 underline-offset-4 hover:decoration-foreground",
1372
+ onInk: "bg-white text-oca-ink hover:bg-white/90",
1373
+ onInkOutline: "border border-white/20 text-white hover:bg-white/10"
1374
+ },
1375
+ size: {
1376
+ sm: "h-9 px-4 text-sm",
1377
+ md: "h-11 px-5 text-[0.9375rem]",
1378
+ lg: "h-14 px-7 text-[1.0625rem]",
1379
+ icon: "size-10",
1380
+ "icon-sm": "size-8"
1381
+ }
1382
+ },
1383
+ compoundVariants: [{ variant: "link", className: "h-auto px-0" }],
1384
+ defaultVariants: { variant: "primary", size: "md" }
1385
+ }
1386
+ );
1387
+ var Button = react.forwardRef(function Button2({ className, variant, size, asChild = false, loading = false, disabled, children, type, ...props }, ref) {
1388
+ const Comp = asChild ? reactSlot.Slot : "button";
1389
+ return /* @__PURE__ */ jsxRuntime.jsx(
1390
+ Comp,
1391
+ {
1392
+ ref,
1393
+ "data-ui": "button",
1394
+ className: cn(buttonVariants({ variant, size }), className),
1395
+ disabled: asChild ? void 0 : disabled || loading,
1396
+ "aria-busy": loading || void 0,
1397
+ type: asChild ? void 0 : type ?? "button",
1398
+ ...props,
1399
+ children: asChild ? children : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1400
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { className: "size-[1.1em]" }) : null,
1401
+ children
1402
+ ] })
1403
+ }
1404
+ );
1405
+ });
1406
+ function useActiveHeading(ids) {
1407
+ const [active, setActive] = react.useState(ids[0]);
1408
+ const key = ids.join("|");
1409
+ react.useEffect(() => {
1410
+ setActive(key.split("|")[0] || void 0);
1411
+ const els = key.split("|").map((id) => document.getElementById(id)).filter((el) => el !== null);
1412
+ if (els.length === 0 || typeof IntersectionObserver === "undefined") return;
1413
+ const io = new IntersectionObserver(
1414
+ (entries) => {
1415
+ const hit = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];
1416
+ if (hit) setActive(hit.target.id);
1417
+ },
1418
+ { rootMargin: "-80px 0px -65% 0px" }
1419
+ );
1420
+ els.forEach((el) => io.observe(el));
1421
+ return () => io.disconnect();
1422
+ }, [key]);
1423
+ return active;
1424
+ }
1425
+ function useHeadings(selector = "#docs-content", key) {
1426
+ const [items, setItems] = react.useState([]);
1427
+ react.useEffect(() => {
1428
+ const read = () => {
1429
+ const root = document.querySelector(selector);
1430
+ if (!root) return setItems([]);
1431
+ setItems(
1432
+ Array.from(root.querySelectorAll("h2[id], h3[id]")).map((h) => ({
1433
+ id: h.id,
1434
+ // The anchor link's "#" is not part of the name.
1435
+ label: (h.querySelector("[data-heading-text]") ?? h).textContent?.trim() ?? "",
1436
+ level: h.tagName === "H3" ? 3 : 2
1437
+ }))
1438
+ );
1439
+ };
1440
+ const raf = requestAnimationFrame(read);
1441
+ const t = setTimeout(read, 80);
1442
+ return () => {
1443
+ cancelAnimationFrame(raf);
1444
+ clearTimeout(t);
1445
+ };
1446
+ }, [selector, key]);
1447
+ const activeId = useActiveHeading(items.map((i) => i.id));
1448
+ return { items, activeId };
1449
+ }
1450
+ function OnThisPage({
1451
+ items,
1452
+ activeId,
1453
+ title = "On this page",
1454
+ onNavigate,
1455
+ className
1456
+ }) {
1457
+ const id = react.useId();
1458
+ if (items.length === 0) return null;
1459
+ return (
1460
+ // With no visible title (a folded list under its own summary) the name is still "On this page".
1461
+ /* @__PURE__ */ jsxRuntime.jsxs("nav", { "aria-labelledby": title ? id : void 0, "aria-label": title ? void 0 : "On this page", className, children: [
1462
+ title ? /* @__PURE__ */ jsxRuntime.jsx("p", { id, className: "mb-3 text-[0.875rem] font-medium", children: title }) : null,
1463
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { children: items.map((t) => {
1464
+ const on = t.id === activeId;
1465
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
1466
+ "a",
1467
+ {
1468
+ href: `#${t.id}`,
1469
+ onClick: onNavigate,
1470
+ "aria-current": on ? "location" : void 0,
1471
+ className: cn(
1472
+ "block border-l py-1.5 text-[0.875rem] leading-snug transition-colors",
1473
+ t.level === 3 ? "pl-7" : "pl-4",
1474
+ on ? "border-foreground text-foreground" : "border-border text-muted-foreground hover:text-foreground"
1475
+ ),
1476
+ children: t.label
1477
+ }
1478
+ ) }, t.id);
1479
+ }) })
1480
+ ] })
1481
+ );
1482
+ }
1483
+ var InDocsContext = sharedContext("in-docs", false);
1484
+ var STATUS_WORDS = /* @__PURE__ */ new Set(["live", "testing", "not-yet", "paused", "good", "caution", "refused"]);
1485
+ var pathOf = (href) => href.split(/[?#]/)[0].replace(/(.)\/$/, "$1");
1486
+ function DocsNav({
1487
+ sections,
1488
+ current,
1489
+ onNavigate,
1490
+ className
1491
+ }) {
1492
+ const here = pathOf(current);
1493
+ const activeSection = react.useMemo(
1494
+ () => sections.findIndex((s) => s.items.some((i) => pathOf(i.href) === here)),
1495
+ [sections, here]
1496
+ );
1497
+ const [peeked, setPeeked] = react.useState(() => /* @__PURE__ */ new Set());
1498
+ react.useEffect(() => setPeeked(/* @__PURE__ */ new Set()), [activeSection]);
1499
+ const activeRef = react.useRef(null);
1500
+ react.useEffect(() => {
1501
+ const id = requestAnimationFrame(() => activeRef.current?.scrollIntoView({ block: "nearest" }));
1502
+ return () => cancelAnimationFrame(id);
1503
+ }, [here]);
1504
+ const base = react.useId();
1505
+ return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Docs", className: cn("flex flex-col gap-0.5", className), children: sections.map((section, si) => {
1506
+ const isActive = si === activeSection;
1507
+ const open = isActive || peeked.has(si);
1508
+ const panel = `${base}-s${si}`;
1509
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1510
+ /* @__PURE__ */ jsxRuntime.jsxs(
1511
+ "button",
1512
+ {
1513
+ type: "button",
1514
+ "aria-expanded": open,
1515
+ "aria-controls": panel,
1516
+ onClick: () => {
1517
+ if (isActive) return;
1518
+ setPeeked((p) => {
1519
+ const n = new Set(p);
1520
+ if (n.has(si)) n.delete(si);
1521
+ else n.add(si);
1522
+ return n;
1523
+ });
1524
+ },
1525
+ className: cn(
1526
+ "flex min-h-9 w-full items-center justify-between gap-2 rounded-lg px-2.5 py-1.5 text-left text-[0.875rem] transition-colors",
1527
+ isActive ? "font-medium text-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground",
1528
+ isActive && "cursor-default"
1529
+ ),
1530
+ children: [
1531
+ section.label,
1532
+ /* @__PURE__ */ jsxRuntime.jsx(
1533
+ lucideReact.ChevronRight,
1534
+ {
1535
+ "aria-hidden": true,
1536
+ className: cn("size-3.5 shrink-0 text-oca-subtle transition-transform duration-200", open && "rotate-90")
1537
+ }
1538
+ )
1539
+ ]
1540
+ }
1541
+ ),
1542
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { id: panel, hidden: !open, className: "mb-2 ml-2.5 mt-0.5 border-l border-border", children: section.items.map((item) => {
1543
+ const on = pathOf(item.href) === here;
1544
+ const badge = typeof item.badge === "string" && STATUS_WORDS.has(item.badge) ? /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: item.badge }) : item.badge ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { tone: "muted", children: item.badge }) : null;
1545
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
1546
+ Link__default.default,
1547
+ {
1548
+ href: item.href,
1549
+ ref: on ? activeRef : void 0,
1550
+ onClick: onNavigate,
1551
+ "aria-current": on ? "page" : void 0,
1552
+ className: cn(
1553
+ "-ml-px flex min-h-9 items-center gap-2 border-l-2 py-1.5 pl-3.5 pr-2 text-[0.875rem] leading-snug transition-colors",
1554
+ on ? "border-foreground font-medium text-foreground" : "border-transparent text-muted-foreground hover:border-oca-hairline-strong hover:text-foreground"
1555
+ ),
1556
+ children: [
1557
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1", children: item.label }),
1558
+ badge
1559
+ ]
1560
+ }
1561
+ ) }, item.href);
1562
+ }) })
1563
+ ] }, section.label);
1564
+ }) });
1565
+ }
1566
+ var DOCS_HEADER_NAV = OCHK_IO_NAV.map((n) => n.href === DOCS_HREF ? { ...n, active: true } : n);
1567
+ function DocsShell({
1568
+ nav,
1569
+ current,
1570
+ navCurrent,
1571
+ header,
1572
+ search,
1573
+ actions,
1574
+ breadcrumbs,
1575
+ toc,
1576
+ pager,
1577
+ footer,
1578
+ children
1579
+ }) {
1580
+ const [menu, setMenu] = react.useState(false);
1581
+ react.useEffect(() => setMenu(false), [current]);
1582
+ const read = useHeadings("#docs-content", current);
1583
+ const items = toc ?? read.items;
1584
+ const activeId = toc ? void 0 : read.activeId;
1585
+ const marked = navCurrent ?? current;
1586
+ const here = pathOf(marked);
1587
+ const place = react.useMemo(() => {
1588
+ for (const s of nav) {
1589
+ const it = s.items.find((i) => pathOf(i.href) === here);
1590
+ if (it) return { section: s.label, page: it.label };
1591
+ }
1592
+ return null;
1593
+ }, [nav, here]);
1594
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-dvh flex-col bg-background", children: [
1595
+ /* @__PURE__ */ jsxRuntime.jsx(SkipLink, {}),
1596
+ header ?? /* @__PURE__ */ jsxRuntime.jsx(SiteHeader, { home: OCHK_IO, family: "docs", nav: DOCS_HEADER_NAV, search, actions }),
1597
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-16 z-30 border-b border-border bg-background/95 backdrop-blur-md supports-[backdrop-filter]:bg-background/90 md:top-[4.5rem] lg:hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oca-container flex h-12 items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open: menu, onOpenChange: setMenu, children: [
1598
+ /* @__PURE__ */ jsxRuntime.jsxs(SheetTrigger, { className: "-ml-2 inline-flex h-10 min-w-0 items-center gap-2 rounded-full px-2.5 text-[0.875rem] hover:bg-accent", children: [
1599
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelLeft, { "aria-hidden": true, className: "size-[1.125rem] shrink-0" }),
1600
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Docs menu. " }),
1601
+ place ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
1602
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: place.section }),
1603
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": true, className: "size-3.5 shrink-0 text-oca-subtle" }),
1604
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", children: place.page })
1605
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Docs" })
1606
+ ] }),
1607
+ /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "left", className: "w-[min(88vw,22rem)] gap-0 p-0", children: [
1608
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-border px-5 py-4 pr-14", children: /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "text-[1.0625rem] font-medium", children: "Docs" }) }),
1609
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-y-auto p-3", children: /* @__PURE__ */ jsxRuntime.jsx(DocsNav, { sections: nav, current: marked, onNavigate: () => setMenu(false) }) })
1610
+ ] })
1611
+ ] }) }) }),
1612
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oca-container flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:grid lg:grid-cols-[13.5rem_minmax(0,1fr)] lg:gap-x-10 xl:grid-cols-[13.5rem_minmax(0,1fr)_11rem]", children: [
1613
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-[4.5rem] hidden max-h-[calc(100dvh-4.5rem)] self-start overflow-y-auto overscroll-contain py-8 pr-1 lg:block", children: /* @__PURE__ */ jsxRuntime.jsx(DocsNav, { sections: nav, current: marked }) }),
1614
+ /* @__PURE__ */ jsxRuntime.jsxs("main", { id: "main-content", className: "min-w-0 pb-16 pt-6 md:pt-8", children: [
1615
+ breadcrumbs ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 max-lg:hidden", children: breadcrumbs }) : null,
1616
+ items.length > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group mb-8 rounded-2xl border border-border bg-card xl:hidden", children: [
1617
+ /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex min-h-12 cursor-pointer list-none items-center justify-between px-4 text-[0.9375rem] font-medium [&::-webkit-details-marker]:hidden", children: [
1618
+ "On this page",
1619
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { "aria-hidden": true, className: "size-4 text-muted-foreground transition-transform group-open:rotate-180" })
1620
+ ] }),
1621
+ /* @__PURE__ */ jsxRuntime.jsx(OnThisPage, { items, activeId, title: null, className: "px-4 pb-4" })
1622
+ ] }) : null,
1623
+ /* @__PURE__ */ jsxRuntime.jsx("article", { id: "docs-content", className: "max-w-[46rem]", children: /* @__PURE__ */ jsxRuntime.jsx(InDocsContext.Provider, { value: true, children }) }),
1624
+ pager ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-16 max-w-[46rem]", children: pager }) : null
1625
+ ] }),
1626
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-[4.5rem] hidden max-h-[calc(100dvh-4.5rem)] self-start overflow-y-auto py-10 xl:block", children: items.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(OnThisPage, { items, activeId }) : null })
1627
+ ] }) }),
1628
+ footer ?? /* @__PURE__ */ jsxRuntime.jsx(OchkIoFooter, {})
1629
+ ] });
1602
1630
  }
1603
1631
  var OCHK_APP_ACCOUNT = "https://ochk.app/me/account";
1604
1632
  function onHistory(change) {
@@ -1743,6 +1771,7 @@ exports.DocsNav = DocsNav;
1743
1771
  exports.DocsShell = DocsShell;
1744
1772
  exports.FamilyMenu = FamilyMenu;
1745
1773
  exports.OCHK_APP_ACCOUNT = OCHK_APP_ACCOUNT;
1774
+ exports.OchkIoFooter = OchkIoFooter;
1746
1775
  exports.SessionAccountView = SessionAccountView;
1747
1776
  exports.ShellContext = ShellContext;
1748
1777
  exports.SignInFrame = SignInFrame;