@sableui/react 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/AGENTS.md +157 -0
  2. package/CHANGELOG.md +55 -0
  3. package/CONTRIBUTING.md +221 -0
  4. package/DESIGN.md +548 -0
  5. package/LICENSE +21 -0
  6. package/README.md +194 -2
  7. package/THIRD_PARTY_NOTICES.md +44 -0
  8. package/bin/sable-lint.mjs +0 -0
  9. package/bin/sable.mjs +1223 -0
  10. package/dist/_contract-CYScOdf5.d.ts +150 -0
  11. package/dist/chunk-3IWZYBGH.js +398 -0
  12. package/dist/chunk-5Z5KLOAU.js +167 -0
  13. package/dist/chunk-7MG4SHYX.js +15 -0
  14. package/dist/chunk-ADLLDWLW.js +15 -0
  15. package/dist/chunk-EP55KSYA.js +14 -0
  16. package/dist/chunk-J23ELQQZ.js +9396 -0
  17. package/dist/chunk-JI7WJP2L.js +286 -0
  18. package/dist/chunk-K7RUHUGC.js +14 -0
  19. package/dist/chunk-KO3NAEBF.js +15 -0
  20. package/dist/chunk-L4IEIQQA.js +14 -0
  21. package/dist/chunk-L6Y46C62.js +270 -0
  22. package/dist/chunk-NEQNXI2G.js +25 -0
  23. package/dist/chunk-PAOZBZME.js +40 -0
  24. package/dist/chunk-RWP4XBSK.js +139 -0
  25. package/dist/chunk-SRQWQUTW.js +63 -0
  26. package/dist/chunk-YXBOYWBN.js +15 -0
  27. package/dist/foundations/index.d.ts +88 -0
  28. package/dist/foundations/index.js +792 -0
  29. package/dist/icon/index.d.ts +60 -0
  30. package/dist/icon/index.js +2 -0
  31. package/dist/icon-sets/brands.d.ts +44 -0
  32. package/dist/icon-sets/brands.js +147 -0
  33. package/dist/icon-sets/reicon.d.ts +8 -0
  34. package/dist/icon-sets/reicon.js +1 -0
  35. package/dist/index.d.ts +7013 -0
  36. package/dist/index.js +15462 -0
  37. package/dist/ios/chrome.css +83 -0
  38. package/dist/ios/frame.css +5 -0
  39. package/dist/ios/index.d.ts +70 -0
  40. package/dist/ios/index.js +313 -0
  41. package/dist/ios/preview.d.ts +64 -0
  42. package/dist/ios/preview.js +133 -0
  43. package/dist/ios/styles.css +77 -0
  44. package/dist/layout/index.d.ts +280 -0
  45. package/dist/layout/index.js +2 -0
  46. package/dist/meta-6dUzgC6M.d.ts +205 -0
  47. package/dist/patterns/index.d.ts +36 -0
  48. package/dist/patterns/index.js +934 -0
  49. package/dist/presets/index.d.ts +37 -0
  50. package/dist/presets/index.js +117 -0
  51. package/dist/registry-CyL28OzJ.d.ts +28 -0
  52. package/dist/styles.css +1893 -0
  53. package/dist/theme-provider-BWcizhAt.d.ts +105 -0
  54. package/dist/themes/atelier/atelier.css +213 -0
  55. package/dist/themes/atelier/index.d.ts +9 -0
  56. package/dist/themes/atelier/index.js +1 -0
  57. package/dist/themes/base/base.css +443 -0
  58. package/dist/themes/base/index.d.ts +9 -0
  59. package/dist/themes/base/index.js +1 -0
  60. package/dist/themes/ember/ember.css +270 -0
  61. package/dist/themes/ember/index.d.ts +9 -0
  62. package/dist/themes/ember/index.js +1 -0
  63. package/dist/themes/index.d.ts +10 -0
  64. package/dist/themes/index.js +8 -0
  65. package/dist/themes/meridian/index.d.ts +9 -0
  66. package/dist/themes/meridian/index.js +1 -0
  67. package/dist/themes/meridian/meridian.css +255 -0
  68. package/dist/themes/poppy/index.d.ts +6 -0
  69. package/dist/themes/poppy/index.js +1 -0
  70. package/dist/themes/poppy/poppy.css +174 -0
  71. package/dist/themes/prism/index.d.ts +6 -0
  72. package/dist/themes/prism/index.js +1 -0
  73. package/dist/themes/prism/prism.css +183 -0
  74. package/dist/themes/verdant/index.d.ts +9 -0
  75. package/dist/themes/verdant/index.js +1 -0
  76. package/dist/themes/verdant/verdant.css +210 -0
  77. package/dist/themes.css +30 -0
  78. package/llms/components/accordion.md +93 -0
  79. package/llms/components/app-header.md +70 -0
  80. package/llms/components/app-shell.md +117 -0
  81. package/llms/components/attachment.md +105 -0
  82. package/llms/components/avatar.md +141 -0
  83. package/llms/components/badge.md +117 -0
  84. package/llms/components/banner.md +119 -0
  85. package/llms/components/bottom-nav.md +80 -0
  86. package/llms/components/breadcrumbs.md +75 -0
  87. package/llms/components/browser.md +67 -0
  88. package/llms/components/bubble.md +119 -0
  89. package/llms/components/button.md +199 -0
  90. package/llms/components/calendar.md +75 -0
  91. package/llms/components/card.md +135 -0
  92. package/llms/components/carousel.md +87 -0
  93. package/llms/components/chat-composer.md +126 -0
  94. package/llms/components/checkbox-card.md +98 -0
  95. package/llms/components/checkbox.md +90 -0
  96. package/llms/components/code-block.md +81 -0
  97. package/llms/components/code.md +62 -0
  98. package/llms/components/collapsible.md +83 -0
  99. package/llms/components/color-field.md +100 -0
  100. package/llms/components/combobox.md +119 -0
  101. package/llms/components/command-menu.md +158 -0
  102. package/llms/components/context-card.md +73 -0
  103. package/llms/components/context-menu.md +79 -0
  104. package/llms/components/data-table.md +99 -0
  105. package/llms/components/date-picker.md +97 -0
  106. package/llms/components/description.md +96 -0
  107. package/llms/components/destructive-action-modal.md +76 -0
  108. package/llms/components/dialog.md +127 -0
  109. package/llms/components/dots-menu.md +64 -0
  110. package/llms/components/empty-state.md +113 -0
  111. package/llms/components/entity.md +67 -0
  112. package/llms/components/feedback.md +63 -0
  113. package/llms/components/fieldset.md +106 -0
  114. package/llms/components/file-dropzone.md +103 -0
  115. package/llms/components/form.md +111 -0
  116. package/llms/components/gliding-highlight.md +84 -0
  117. package/llms/components/inline-status.md +62 -0
  118. package/llms/components/input.md +127 -0
  119. package/llms/components/kbd.md +57 -0
  120. package/llms/components/label.md +73 -0
  121. package/llms/components/marker.md +99 -0
  122. package/llms/components/marquee.md +66 -0
  123. package/llms/components/menu.md +122 -0
  124. package/llms/components/message-scroller.md +110 -0
  125. package/llms/components/message.md +114 -0
  126. package/llms/components/meter.md +124 -0
  127. package/llms/components/middle-truncate.md +62 -0
  128. package/llms/components/note.md +81 -0
  129. package/llms/components/notification-badge.md +67 -0
  130. package/llms/components/number-field.md +70 -0
  131. package/llms/components/otp-field.md +101 -0
  132. package/llms/components/page-header.md +76 -0
  133. package/llms/components/page-loader.md +67 -0
  134. package/llms/components/pagination.md +92 -0
  135. package/llms/components/panel-group.md +120 -0
  136. package/llms/components/panel-section.md +122 -0
  137. package/llms/components/phone.md +70 -0
  138. package/llms/components/pill.md +73 -0
  139. package/llms/components/popover.md +79 -0
  140. package/llms/components/progress.md +98 -0
  141. package/llms/components/radio-card.md +90 -0
  142. package/llms/components/radio.md +86 -0
  143. package/llms/components/relative-time.md +60 -0
  144. package/llms/components/scroller.md +88 -0
  145. package/llms/components/scrub-field.md +97 -0
  146. package/llms/components/search-input.md +84 -0
  147. package/llms/components/select.md +106 -0
  148. package/llms/components/separator.md +70 -0
  149. package/llms/components/settings-menu.md +139 -0
  150. package/llms/components/sheet.md +97 -0
  151. package/llms/components/show-more.md +59 -0
  152. package/llms/components/side-nav.md +106 -0
  153. package/llms/components/sidebar.md +87 -0
  154. package/llms/components/skeleton.md +62 -0
  155. package/llms/components/slider.md +114 -0
  156. package/llms/components/spinner.md +87 -0
  157. package/llms/components/status-bar.md +91 -0
  158. package/llms/components/status-dot.md +70 -0
  159. package/llms/components/stepper.md +125 -0
  160. package/llms/components/streaming-text.md +86 -0
  161. package/llms/components/switch.md +87 -0
  162. package/llms/components/table.md +112 -0
  163. package/llms/components/tabs.md +84 -0
  164. package/llms/components/textarea.md +88 -0
  165. package/llms/components/theme-selector.md +95 -0
  166. package/llms/components/theme-switcher.md +71 -0
  167. package/llms/components/thinking-orb.md +91 -0
  168. package/llms/components/toast.md +130 -0
  169. package/llms/components/toggle.md +95 -0
  170. package/llms/components/toolbar.md +168 -0
  171. package/llms/components/tooltip.md +93 -0
  172. package/llms/components/tree.md +227 -0
  173. package/llms/components/video.md +66 -0
  174. package/llms/components/workspace.md +138 -0
  175. package/llms/foundations/accessibility.md +60 -0
  176. package/llms/foundations/motion.md +96 -0
  177. package/llms/foundations/scrolling.md +41 -0
  178. package/llms/foundations/shape.md +73 -0
  179. package/llms/foundations/spacing.md +82 -0
  180. package/llms/foundations/surfaces.md +69 -0
  181. package/llms/foundations/typography.md +120 -0
  182. package/llms/layout/aspect-box.md +37 -0
  183. package/llms/layout/bleed.md +31 -0
  184. package/llms/layout/center.md +37 -0
  185. package/llms/layout/columns.md +51 -0
  186. package/llms/layout/grid-overlay.md +40 -0
  187. package/llms/layout/grid.md +47 -0
  188. package/llms/layout/page.md +39 -0
  189. package/llms/layout/region.md +49 -0
  190. package/llms/layout/row.md +40 -0
  191. package/llms/layout/safe-area.md +37 -0
  192. package/llms/layout/spacer.md +39 -0
  193. package/llms/layout/split-pane.md +41 -0
  194. package/llms/layout/stack.md +39 -0
  195. package/llms/patterns/auth-page.md +68 -0
  196. package/llms/patterns/brand-landing-page.md +423 -0
  197. package/llms/patterns/chat-panel.md +210 -0
  198. package/llms/patterns/dashboard-page.md +170 -0
  199. package/llms/patterns/feed-page.md +103 -0
  200. package/llms/patterns/landing-page.md +122 -0
  201. package/llms/patterns/list-detail-page.md +100 -0
  202. package/llms/patterns/settings-page.md +121 -0
  203. package/llms/reference/token-intent.md +324 -0
  204. package/llms/reference/utilities.md +129 -0
  205. package/llms/tasks/adding-a-component.md +113 -0
  206. package/llms/tasks/auditing-an-app.md +216 -0
  207. package/llms/tasks/building-a-page.md +109 -0
  208. package/llms/tasks/building-a-tool.md +141 -0
  209. package/llms/tasks/building-an-ios-prototype.md +105 -0
  210. package/llms/tasks/building-with-sable.md +73 -0
  211. package/llms/tasks/choosing-a-component.md +109 -0
  212. package/llms/tasks/converting-an-app.md +139 -0
  213. package/llms/tasks/creating-a-preset.md +43 -0
  214. package/llms/tasks/creating-design-assets.md +96 -0
  215. package/llms/tasks/designing-a-surface.md +115 -0
  216. package/llms/tasks/developing-taste.md +157 -0
  217. package/llms/tasks/getting-started.md +63 -0
  218. package/llms/tasks/learning-from-builds.md +74 -0
  219. package/llms/tasks/reviewing-a-design.md +114 -0
  220. package/llms/tasks/reviewing-a-journey.md +84 -0
  221. package/llms/tasks/reviewing-motion.md +92 -0
  222. package/llms/tasks/using-presets.md +87 -0
  223. package/llms/taste/asset-plan.template.json +53 -0
  224. package/llms/taste/composition-follows-content.md +20 -0
  225. package/llms/taste/hierarchy-is-a-relationship.md +22 -0
  226. package/llms/taste/lessons.json +57 -0
  227. package/llms/taste/motion-needs-a-reason.md +21 -0
  228. package/llms/taste/surface-states-travel-together.md +20 -0
  229. package/llms-full.txt +14906 -0
  230. package/llms.txt +405 -0
  231. package/package.json +185 -3
  232. package/registry.json +18812 -0
  233. package/scripts/agent-setup.mjs +54 -0
  234. package/scripts/design-atlas.mjs +221 -0
  235. package/scripts/design-directions.mjs +123 -0
  236. package/scripts/design-memory.mjs +166 -0
  237. package/scripts/design-preferences.mjs +227 -0
  238. package/scripts/design-presets.mjs +48 -0
  239. package/scripts/grade.mjs +509 -0
  240. package/scripts/lint-rules.mjs +1065 -0
  241. package/scripts/review-evidence.mjs +120 -0
  242. package/scripts/skeleton.mjs +248 -0
  243. package/skills/entry/SKILL.md +34 -0
  244. package/skills/sable/SKILL.md +187 -0
  245. package/skills/sable/agents/openai.yaml +6 -0
  246. package/targets/ios.json +982 -0
  247. package/third-party/reicon-LICENSE.txt +20 -0
@@ -0,0 +1,934 @@
1
+ import { Card, CardHeader, CardTitle, CardDescription, CardContent, Form, Field, Input, Button, Separator, Badge, TextReveal, StatusDot, CardAction, MessageScroller, Marker, Message, Bubble, Avatar, CodeBlock, BubbleReactions, BubbleReaction, Attachment, StreamingText, CardFooter, ChatComposer, SettingsMenu, SettingsRow, MenuRadioGroup, MenuRadioItem, AppShell, PageHeader, DataTable, RelativeTime, CardList, Entity, Sidebar, SidebarHeader, SidebarNav, SidebarItem, BottomNav, BottomNavItem, AppHeader, Reveal, SearchInput, Description, DescriptionItem, Fieldset, FieldsetLegend, FieldsetDescription, FieldsetContent, Textarea, Select, SelectTrigger, SelectContent, SelectItem, FieldsetFooter, Switch } from '../chunk-J23ELQQZ.js';
2
+ import { Region, Stack, Row, Page, Grid, AspectBox, Columns, Col } from '../chunk-3IWZYBGH.js';
3
+ import '../chunk-SRQWQUTW.js';
4
+ import { Icon } from '../chunk-PAOZBZME.js';
5
+ import '../chunk-5Z5KLOAU.js';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+
8
+ function AuthPage() {
9
+ return /* @__PURE__ */ jsx("main", { className: "grid min-h-dvh place-items-center bg-background", children: /* @__PURE__ */ jsx(Region, { max: "form", safeArea: ["top", "bottom"], children: /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
10
+ /* @__PURE__ */ jsxs(Card, { children: [
11
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
12
+ /* @__PURE__ */ jsx(CardTitle, { children: /* @__PURE__ */ jsx("h1", { children: "Sign in" }) }),
13
+ /* @__PURE__ */ jsx(CardDescription, { children: "Welcome back \u2014 pick up where you left off." })
14
+ ] }),
15
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs(Form, { onSubmit: (event) => event.preventDefault(), children: [
16
+ /* @__PURE__ */ jsx(Field, { label: "Email", required: true, children: /* @__PURE__ */ jsx(Input, { type: "email", name: "email", autoComplete: "email", placeholder: "you@example.com" }) }),
17
+ /* @__PURE__ */ jsx(Field, { label: "Password", required: true, children: /* @__PURE__ */ jsx(Input, { type: "password", name: "password", autoComplete: "current-password" }) }),
18
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "w-full", children: "Sign in" }),
19
+ /* @__PURE__ */ jsx(Separator, { label: "or" }),
20
+ /* @__PURE__ */ jsx(Button, { variant: "outline", className: "w-full", children: "Continue with wallet" })
21
+ ] }) })
22
+ ] }),
23
+ /* @__PURE__ */ jsxs("p", { className: "text-center text-body-sm text-muted-foreground", children: [
24
+ "No account?",
25
+ " ",
26
+ /* @__PURE__ */ jsx("a", { href: "/signup", className: "link-underline motion-interactive text-foreground", children: "Create one" })
27
+ ] })
28
+ ] }) }) });
29
+ }
30
+
31
+ // src/patterns/auth-page.meta.ts
32
+ var authPageMeta = {
33
+ name: "AuthPage",
34
+ summary: "Centered auth card: a form-width region vertically centered in the viewport, one Card holding the Form, an alternate-action divider, and a quiet switch-to-sign-up link beneath it.",
35
+ intent: "Reach for this for sign-in, sign-up, forgot-password, and any single-purpose one-column form route (invite accept, unsubscribe). Not for multi-section forms (use SettingsPage).",
36
+ notes: [
37
+ "Region cap is `form` (480px) \u2014 the narrowest named cap; auth never needs more.",
38
+ "The card is the only surface on the page: no app chrome, no sidebar \u2014 minimal exit paths.",
39
+ "min-h-dvh + grid place-items-center centers the card without magic offsets.",
40
+ "Field handles label/error wiring; the submit button is full-width \u2014 the primary (and only) action.",
41
+ "Secondary actions (here, switch to sign-up; add forgot password the same way) are quiet `link-underline` links, never competing buttons."
42
+ ],
43
+ a11y: [
44
+ "The route renders inside <main> with the page h1 in the card header (an h1 inside CardTitle, which carries the type).",
45
+ "Field auto-wires labels, describedby, and invalid state onto the inputs.",
46
+ "Autocomplete attributes (email, current-password) keep password managers working."
47
+ ]
48
+ };
49
+ var navItems = ["Platform", "Signals", "Security", "Company"];
50
+ var metrics = [
51
+ { label: "Checks cleared", value: "1.8M", detail: "in the last 30 days" },
52
+ { label: "Review time", value: "42s", detail: "median approval path" },
53
+ { label: "Spaces online", value: "284", detail: "across active teams" }
54
+ ];
55
+ var workflow = [
56
+ { label: "Intake", state: "Open", tone: "info" },
57
+ { label: "Review", state: "Live", tone: "edition" },
58
+ { label: "Archive", state: "Safe", tone: "success" }
59
+ ];
60
+ var bars = [
61
+ { height: "38%", tone: "bg-tone-info" },
62
+ { height: "56%", tone: "bg-tone-edition" },
63
+ { height: "44%", tone: "bg-tone-standard" },
64
+ { height: "68%", tone: "bg-success" },
65
+ { height: "52%", tone: "bg-tone-info" },
66
+ { height: "78%", tone: "bg-highlight" },
67
+ { height: "60%", tone: "bg-tone-edition" },
68
+ { height: "86%", tone: "bg-success" },
69
+ { height: "64%", tone: "bg-tone-standard" }
70
+ ];
71
+ var features = [
72
+ {
73
+ icon: "lock",
74
+ title: "Private by default",
75
+ body: "Workspace roles, approval trails, and handoff history stay clear without turning the interface into compliance fog."
76
+ },
77
+ {
78
+ icon: "layout",
79
+ title: "Brand and product together",
80
+ body: "Editorial sections and dense console views share the same semantic tokens, radius, and motion contract."
81
+ },
82
+ {
83
+ icon: "circle-check",
84
+ title: "Calm confidence",
85
+ body: "Large type, tactile controls, and focused status tones make complex work feel understandable on first pass."
86
+ }
87
+ ];
88
+ var reviews = [
89
+ { name: "Waypoint", value: "98.4%", label: "clear routes" },
90
+ { name: "Fielddesk", value: "12", label: "open holds" },
91
+ { name: "Archive", value: "4.8k", label: "sealed records" }
92
+ ];
93
+ var footerGroups = [
94
+ {
95
+ title: "Platform",
96
+ links: [
97
+ { label: "Overview", href: "#platform" },
98
+ { label: "Signals", href: "#signals" },
99
+ { label: "Approvals", href: "#approvals" },
100
+ { label: "Records", href: "#records" }
101
+ ]
102
+ },
103
+ {
104
+ title: "Company",
105
+ links: [
106
+ { label: "About", href: "/about" },
107
+ { label: "Security", href: "/security" },
108
+ { label: "Customers", href: "/customers" },
109
+ { label: "Careers", href: "/careers" }
110
+ ]
111
+ },
112
+ {
113
+ title: "Resources",
114
+ links: [
115
+ { label: "Guides", href: "/guides" },
116
+ { label: "Status", href: "/status" },
117
+ { label: "Changelog", href: "/changelog" },
118
+ { label: "Docs", href: "/docs" }
119
+ ]
120
+ }
121
+ ];
122
+ var footerSignals = [
123
+ { label: "Uptime", value: "99.98%" },
124
+ { label: "Regions", value: "12" },
125
+ { label: "Response", value: "42s" }
126
+ ];
127
+ function BrandLandingPage() {
128
+ return /* @__PURE__ */ jsxs("div", { className: "min-h-dvh overflow-hidden bg-background text-foreground", children: [
129
+ /* @__PURE__ */ jsx("header", { className: "relative z-10 pt-4", children: /* @__PURE__ */ jsx(Region, { max: "wide", children: /* @__PURE__ */ jsxs(
130
+ "nav",
131
+ {
132
+ "aria-label": "Main",
133
+ className: "flex min-h-15 items-center justify-between gap-3 rounded-full border border-border bg-card/90 px-3 py-2 shadow-sm backdrop-blur",
134
+ children: [
135
+ /* @__PURE__ */ jsxs("a", { href: "/", className: "flex items-center gap-2 rounded-full px-2 motion-interactive motion-press", children: [
136
+ /* @__PURE__ */ jsx("span", { className: "grid size-8 place-items-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx(Icon, { name: "sparkles", className: "text-sm" }) }),
137
+ /* @__PURE__ */ jsx("span", { className: "text-title-lg", children: "Cove" })
138
+ ] }),
139
+ /* @__PURE__ */ jsx("div", { className: "hidden lg:block", children: /* @__PURE__ */ jsx(Row, { gap: 1, children: navItems.map((item) => /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", render: /* @__PURE__ */ jsx("a", { href: `#${item.toLowerCase()}` }), children: item }, item)) }) }),
140
+ /* @__PURE__ */ jsxs(Row, { gap: 1, children: [
141
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", className: "max-sm:hidden", render: /* @__PURE__ */ jsx("a", { href: "/login" }), children: "Sign in" }),
142
+ /* @__PURE__ */ jsx(Button, { size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/demo" }), children: "Request access" })
143
+ ] })
144
+ ]
145
+ }
146
+ ) }) }),
147
+ /* @__PURE__ */ jsxs("main", { children: [
148
+ /* @__PURE__ */ jsx(Region, { as: "section", max: "wide", className: "pb-16 pt-14 sm:pt-20 lg:pb-24", children: /* @__PURE__ */ jsxs("div", { className: "grid items-center gap-10 lg:grid-cols-[0.92fr_1.08fr]", children: [
149
+ /* @__PURE__ */ jsxs(Stack, { gap: 4, className: "max-w-3xl", children: [
150
+ /* @__PURE__ */ jsx(Badge, { appearance: "outline", icon: /* @__PURE__ */ jsx(Icon, { name: "lock", className: "text-sm" }), children: "Private operations, made legible" }),
151
+ /* @__PURE__ */ jsx(
152
+ TextReveal,
153
+ {
154
+ as: "h1",
155
+ text: "A calmer command center for high-trust digital work",
156
+ className: "max-w-4xl text-balance text-display-xl sm:text-display-3xl",
157
+ once: true
158
+ }
159
+ ),
160
+ /* @__PURE__ */ jsx("p", { className: "max-w-(--measure-prose) text-body-lg text-muted-foreground", children: "Cove gives teams a polished front door and a serious operating surface: approvals, signals, records, and handoffs in one understandable flow." }),
161
+ /* @__PURE__ */ jsxs(Row, { gap: 2, wrap: true, children: [
162
+ /* @__PURE__ */ jsx(Button, { size: "cta", render: /* @__PURE__ */ jsx("a", { href: "/demo" }), children: "Book a walkthrough" }),
163
+ /* @__PURE__ */ jsx(Button, { size: "cta", variant: "outline", render: /* @__PURE__ */ jsx("a", { href: "#platform" }), children: "Explore platform" })
164
+ ] })
165
+ ] }),
166
+ /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-xl border border-border-floating bg-overlay p-3 text-overlay-foreground shadow-xl", children: /* @__PURE__ */ jsxs("div", { className: "rounded-concentric-xl/3 border border-overlay-foreground/10 bg-overlay-foreground/8", children: [
167
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-overlay-foreground/10 px-4 py-3", children: [
168
+ /* @__PURE__ */ jsxs(Row, { gap: 1, children: [
169
+ /* @__PURE__ */ jsx(StatusDot, { tone: "warning" }),
170
+ /* @__PURE__ */ jsx(StatusDot, { tone: "edition" }),
171
+ /* @__PURE__ */ jsx(StatusDot, { tone: "success" })
172
+ ] }),
173
+ /* @__PURE__ */ jsx("span", { className: "text-label-sm text-overlay-foreground/70", children: "Live workspace" })
174
+ ] }),
175
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-3 p-3 md:grid-cols-[0.7fr_1.3fr]", children: [
176
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
177
+ /* @__PURE__ */ jsxs("div", { className: "rounded-concentric-xl/6 border border-overlay-foreground/10 bg-overlay-foreground/8 p-4", children: [
178
+ /* @__PURE__ */ jsx("p", { className: "text-label-sm text-overlay-foreground/60", children: "Spaces" }),
179
+ /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: reviews.map((review) => /* @__PURE__ */ jsxs("div", { className: "rounded-concentric-xl/10 bg-overlay p-3", children: [
180
+ /* @__PURE__ */ jsxs(Row, { justify: "between", children: [
181
+ /* @__PURE__ */ jsx("span", { className: "text-title-sm", children: review.name }),
182
+ /* @__PURE__ */ jsx("span", { className: "text-mono-sm text-overlay-foreground", children: review.value })
183
+ ] }),
184
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-body-xs text-overlay-foreground/60", children: review.label })
185
+ ] }, review.name)) })
186
+ ] }),
187
+ /* @__PURE__ */ jsxs("div", { className: "rounded-concentric-xl/6 border border-overlay-foreground/10 bg-overlay-foreground/8 p-4", children: [
188
+ /* @__PURE__ */ jsxs(Row, { justify: "between", children: [
189
+ /* @__PURE__ */ jsx("span", { className: "text-title-sm", children: "Route status" }),
190
+ /* @__PURE__ */ jsx(Badge, { tone: "success", size: "sm", children: "Healthy" })
191
+ ] }),
192
+ /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: workflow.map((item) => /* @__PURE__ */ jsxs(Row, { justify: "between", className: "rounded-full bg-overlay px-3 py-2", children: [
193
+ /* @__PURE__ */ jsxs(Row, { gap: 1, children: [
194
+ /* @__PURE__ */ jsx(StatusDot, { tone: item.tone }),
195
+ /* @__PURE__ */ jsx("span", { className: "text-body-sm", children: item.label })
196
+ ] }),
197
+ /* @__PURE__ */ jsx("span", { className: "text-label-sm text-overlay-foreground/60", children: item.state })
198
+ ] }, item.label)) })
199
+ ] })
200
+ ] }),
201
+ /* @__PURE__ */ jsxs("div", { className: "rounded-concentric-xl/6 border border-overlay-foreground/10 bg-overlay-foreground/8 p-4", children: [
202
+ /* @__PURE__ */ jsxs(Row, { justify: "between", className: "border-b border-overlay-foreground/10 pb-4", children: [
203
+ /* @__PURE__ */ jsxs("div", { children: [
204
+ /* @__PURE__ */ jsx("p", { className: "text-label-sm text-overlay-foreground/60", children: "Signal map" }),
205
+ /* @__PURE__ */ jsx("h2", { className: "mt-1 text-heading-3", children: "Every approval path in view" })
206
+ ] }),
207
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", children: "Review" })
208
+ ] }),
209
+ /* @__PURE__ */ jsxs("div", { className: "mt-6 grid min-h-72 grid-rows-[1fr_auto] gap-5", children: [
210
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-9 items-end gap-2 rounded-concentric-xl/10 bg-overlay p-4", children: bars.map((bar, index) => /* @__PURE__ */ jsx("div", { className: "flex h-56 items-end", children: /* @__PURE__ */ jsx(
211
+ "div",
212
+ {
213
+ className: `w-full rounded-full ${bar.tone}`,
214
+ style: { height: bar.height }
215
+ }
216
+ ) }, `${bar.tone}-${index}`)) }),
217
+ /* @__PURE__ */ jsx("div", { className: "grid gap-2 sm:grid-cols-3", children: metrics.map((metric) => /* @__PURE__ */ jsxs("div", { className: "rounded-concentric-xl/10 border border-overlay-foreground/10 bg-overlay p-3", children: [
218
+ /* @__PURE__ */ jsx("p", { className: "text-label-sm text-overlay-foreground/60", children: metric.label }),
219
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-heading-3", children: metric.value }),
220
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-body-xs text-overlay-foreground/60", children: metric.detail })
221
+ ] }, metric.label)) })
222
+ ] })
223
+ ] })
224
+ ] })
225
+ ] }) }) })
226
+ ] }) }),
227
+ /* @__PURE__ */ jsx("section", { id: "platform", className: "border-y border-border bg-muted/40 py-14", children: /* @__PURE__ */ jsx(Region, { max: "wide", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-5 lg:grid-cols-[0.8fr_1.2fr]", children: [
228
+ /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
229
+ /* @__PURE__ */ jsx(Badge, { children: "Design mood" }),
230
+ /* @__PURE__ */ jsx("h2", { className: "text-heading-1", children: "Marketing warmth, product discipline." }),
231
+ /* @__PURE__ */ jsx("p", { className: "max-w-(--measure-prose) text-body-md text-muted-foreground", children: "The page uses a pill nav, tactile surfaces, soft status tones, and slower ease-out movement. It stays broad in color so it does not collapse into purple-brand sameness." })
232
+ ] }),
233
+ /* @__PURE__ */ jsx("div", { className: "grid gap-3 md:grid-cols-3", children: features.map((feature) => /* @__PURE__ */ jsx(Card, { render: /* @__PURE__ */ jsx("article", {}), className: "h-full", children: /* @__PURE__ */ jsxs(CardContent, { children: [
234
+ /* @__PURE__ */ jsx("span", { className: "grid size-10 place-items-center rounded-md bg-highlight text-highlight-foreground", children: /* @__PURE__ */ jsx(Icon, { name: feature.icon, className: "text-xl" }) }),
235
+ /* @__PURE__ */ jsx("h3", { className: "mt-5 text-title-lg", children: feature.title }),
236
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-body-sm text-muted-foreground", children: feature.body })
237
+ ] }) }, feature.title)) })
238
+ ] }) }) }),
239
+ /* @__PURE__ */ jsx(Region, { as: "section", max: "wide", className: "py-14", children: /* @__PURE__ */ jsx("div", { className: "grid gap-3 md:grid-cols-3", children: metrics.map((metric) => /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { children: [
240
+ /* @__PURE__ */ jsx("p", { className: "text-label-md text-muted-foreground", children: metric.label }),
241
+ /* @__PURE__ */ jsx("p", { className: "mt-3 text-display-lg", children: metric.value }),
242
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-body-sm text-muted-foreground", children: metric.detail })
243
+ ] }) }, metric.label)) }) })
244
+ ] }),
245
+ /* @__PURE__ */ jsx("footer", { className: "border-t border-border bg-muted/35", children: /* @__PURE__ */ jsxs(Region, { max: "wide", className: "py-10 sm:py-12", children: [
246
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-8 lg:grid-cols-[1fr_1.35fr_1fr]", children: [
247
+ /* @__PURE__ */ jsxs(Stack, { gap: 3, className: "max-w-md", children: [
248
+ /* @__PURE__ */ jsxs("a", { href: "/", className: "flex w-fit items-center gap-2 rounded-full motion-interactive motion-press", children: [
249
+ /* @__PURE__ */ jsx("span", { className: "grid size-9 place-items-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx(Icon, { name: "sparkles", className: "text-sm" }) }),
250
+ /* @__PURE__ */ jsx("span", { className: "text-title-lg", children: "Cove" })
251
+ ] }),
252
+ /* @__PURE__ */ jsx("p", { className: "text-body-sm text-muted-foreground", children: "A calm operating surface for teams that need trust, speed, and context without the noise." }),
253
+ /* @__PURE__ */ jsxs(Row, { gap: 1, wrap: true, children: [
254
+ /* @__PURE__ */ jsx(Badge, { size: "sm", icon: /* @__PURE__ */ jsx(Icon, { name: "lock" }), children: "Private" }),
255
+ /* @__PURE__ */ jsx(Badge, { tone: "success", size: "sm", icon: /* @__PURE__ */ jsx(Icon, { name: "circle-check" }), children: "Live" })
256
+ ] })
257
+ ] }),
258
+ /* @__PURE__ */ jsx("nav", { "aria-label": "Footer", className: "grid gap-6 sm:grid-cols-3", children: footerGroups.map((group) => /* @__PURE__ */ jsxs("div", { children: [
259
+ /* @__PURE__ */ jsx("h2", { className: "text-label-md text-muted-foreground", children: group.title }),
260
+ /* @__PURE__ */ jsx("ul", { className: "mt-3 space-y-1", children: group.links.map((link) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
261
+ "a",
262
+ {
263
+ href: link.href,
264
+ className: "flex min-h-9 items-center text-body-sm text-muted-foreground motion-interactive hover:text-foreground",
265
+ children: link.label
266
+ }
267
+ ) }, link.label)) })
268
+ ] }, group.title)) }),
269
+ /* @__PURE__ */ jsxs(Card, { children: [
270
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
271
+ /* @__PURE__ */ jsx(CardTitle, { children: /* @__PURE__ */ jsx("h2", { children: "Signal digest" }) }),
272
+ /* @__PURE__ */ jsx(CardDescription, { children: "One weekly note on trusted workflows and product releases." }),
273
+ /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx("span", { className: "grid size-9 place-items-center rounded-full bg-highlight text-highlight-foreground", children: /* @__PURE__ */ jsx(Icon, { name: "sparkles", className: "text-sm" }) }) })
274
+ ] }),
275
+ /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col gap-4", children: [
276
+ /* @__PURE__ */ jsxs("form", { action: "/newsletter", className: "grid gap-2 sm:grid-cols-[1fr_auto]", children: [
277
+ /* @__PURE__ */ jsx("label", { htmlFor: "brand-footer-email", className: "sr-only", children: "Work email" }),
278
+ /* @__PURE__ */ jsx(
279
+ Input,
280
+ {
281
+ id: "brand-footer-email",
282
+ name: "email",
283
+ type: "email",
284
+ autoComplete: "email",
285
+ placeholder: "you@company.com"
286
+ }
287
+ ),
288
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "w-full sm:w-auto", children: "Join" })
289
+ ] }),
290
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2", children: footerSignals.map((signal) => /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-muted p-3", children: [
291
+ /* @__PURE__ */ jsx("p", { className: "text-mono-sm", children: signal.value }),
292
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-caption text-muted-foreground", children: signal.label })
293
+ ] }, signal.label)) })
294
+ ] })
295
+ ] })
296
+ ] }),
297
+ /* @__PURE__ */ jsxs("div", { className: "mt-10 flex flex-col gap-3 border-t border-border pt-5 text-caption text-muted-foreground sm:flex-row sm:items-center sm:justify-between", children: [
298
+ /* @__PURE__ */ jsx("p", { children: "\xA9 2026 Cove. All rights reserved." }),
299
+ /* @__PURE__ */ jsxs(Row, { gap: 3, wrap: true, children: [
300
+ /* @__PURE__ */ jsx(
301
+ "a",
302
+ {
303
+ href: "/privacy",
304
+ className: "flex min-h-9 min-w-9 items-center motion-interactive hover:text-foreground",
305
+ children: "Privacy"
306
+ }
307
+ ),
308
+ /* @__PURE__ */ jsx(
309
+ "a",
310
+ {
311
+ href: "/terms",
312
+ className: "flex min-h-9 min-w-9 items-center motion-interactive hover:text-foreground",
313
+ children: "Terms"
314
+ }
315
+ ),
316
+ /* @__PURE__ */ jsx(
317
+ "a",
318
+ {
319
+ href: "/contact",
320
+ className: "flex min-h-9 min-w-9 items-center motion-interactive hover:text-foreground",
321
+ children: "Contact"
322
+ }
323
+ )
324
+ ] })
325
+ ] })
326
+ ] }) })
327
+ ] });
328
+ }
329
+
330
+ // src/patterns/brand-landing-page.meta.ts
331
+ var brandLandingPageMeta = {
332
+ name: "BrandLandingPage",
333
+ summary: "High-fidelity brand/product landing page: pill navigation, editorial hero type, a dark product scene, calm status tones, CTA pair, feature cards, metrics, and a complete branded footer.",
334
+ intent: "Use this for a polished product marketing route that still needs to show a credible app surface. It is warmer and more tactile than the canonical LandingPage, but avoids trend-driven neon and generic hype cues.",
335
+ notes: [
336
+ "One display headline owns the first viewport; supporting copy stays in body-lg at prose measure.",
337
+ "The nav spends one compact pill surface, with a clear brand signal and two primary paths: sign in and request access.",
338
+ "The product scene uses overlay tokens for a stable dark media surface, then semantic tones for status and chart signals.",
339
+ "The footer closes the page with brand reassurance, a three-column sitemap, a digest signup, operational mini-metrics, and legal links.",
340
+ "Every surface outside the product scene is a real Card (feature, metric and digest cards); status marks are StatusDot. The scene's nested panels take `rounded-concentric-xl/<inset>` from the shell's radius, so the corners stay parallel at any radius preset.",
341
+ "Radius and shadow are theme-controlled. In Cove the same rounded-lg/xl classes read larger and softer; in other themes the structure still holds.",
342
+ "Color is intentionally broad: porcelain/plum/lavender/blue/mint/saffron/coral through semantic roles, not a one-note purple gradient."
343
+ ],
344
+ a11y: [
345
+ "The brand nav is a labeled nav landmark with real links and visible text.",
346
+ "The footer is a real footer landmark with a labeled footer nav, visible link text, and a labeled email field.",
347
+ "TextReveal preserves the full headline as one accessible label and honors reduced-motion policy.",
348
+ "Status dots are paired with visible text labels so color is never the only signal."
349
+ ]
350
+ };
351
+ var pending = true;
352
+ var streamed = "Anchor on the oldest rendered message and add back however far it moved. Appends";
353
+ var turns = [
354
+ {
355
+ id: "t_01",
356
+ mine: true,
357
+ body: "Our chat scroll jumps every time older messages load. What are we doing wrong?"
358
+ },
359
+ {
360
+ id: "t_02",
361
+ mine: false,
362
+ body: "Almost always the anchor. If you preserve the scroll position by measuring the container, prepending history moves everything under the reader."
363
+ },
364
+ {
365
+ id: "t_03",
366
+ mine: false,
367
+ body: "Anchor on the oldest rendered message instead and add back however far it moved. Appends leave it alone, so the same rule covers both."
368
+ }
369
+ ];
370
+ function ChatPanel() {
371
+ return /* @__PURE__ */ jsx(Page, { as: "main", max: "feed", gap: 0, className: "py-6", children: /* @__PURE__ */ jsxs(
372
+ Card,
373
+ {
374
+ render: /* @__PURE__ */ jsx("section", { "aria-labelledby": "chat-title" }),
375
+ className: "flex h-[calc(100dvh_-_--spacing(12))] flex-col overflow-hidden",
376
+ children: [
377
+ /* @__PURE__ */ jsxs(CardHeader, { className: "border-b border-border pb-(--card-p)", children: [
378
+ /* @__PURE__ */ jsx(CardTitle, { children: /* @__PURE__ */ jsx("h1", { id: "chat-title", children: "Scroll behaviour" }) }),
379
+ /* @__PURE__ */ jsx(CardDescription, { children: "Assistant \xB7 answers from your team's docs" }),
380
+ /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon-sm", "aria-label": "Start a new chat", children: /* @__PURE__ */ jsx(Icon, { name: "plus" }) }) })
381
+ ] }),
382
+ /* @__PURE__ */ jsxs(MessageScroller, { className: "min-h-0 flex-1 p-(--card-p)", children: [
383
+ /* @__PURE__ */ jsx(Marker, { variant: "separator", children: "Today" }),
384
+ turns.map((turn, i) => /* @__PURE__ */ jsx(
385
+ Message,
386
+ {
387
+ messageId: turn.id,
388
+ align: turn.mine ? "end" : "start",
389
+ grouped: i > 0 && turns[i - 1].mine === turn.mine,
390
+ avatar: turn.mine ? void 0 : /* @__PURE__ */ jsx(Avatar, { size: "sm", fallback: "AI" }),
391
+ children: /* @__PURE__ */ jsx(
392
+ Bubble,
393
+ {
394
+ variant: turn.mine ? "primary" : "muted",
395
+ align: turn.mine ? "end" : "start",
396
+ tail: i === 0 || turns[i - 1].mine !== turn.mine,
397
+ children: turn.body
398
+ }
399
+ )
400
+ },
401
+ turn.id
402
+ )),
403
+ /* @__PURE__ */ jsx(Marker, { variant: "note", icon: "sparkles", children: "Read message-scroller.md" }),
404
+ /* @__PURE__ */ jsxs(Message, { messageId: "t_04", avatar: /* @__PURE__ */ jsx(Avatar, { size: "sm", fallback: "AI" }), children: [
405
+ /* @__PURE__ */ jsxs(Bubble, { variant: "plain", children: [
406
+ /* @__PURE__ */ jsx("p", { className: "text-body-md text-foreground", children: "Here is the shape of it:" }),
407
+ /* @__PURE__ */ jsx(
408
+ CodeBlock,
409
+ {
410
+ code: "const shift = anchor.offsetTop - lastTop\nif (shift !== 0) viewport.scrollTop += shift",
411
+ filename: "preserve.ts"
412
+ }
413
+ )
414
+ ] }),
415
+ /* @__PURE__ */ jsx(BubbleReactions, { children: /* @__PURE__ */ jsx(BubbleReaction, { emoji: "\u{1F3AF}", count: 2, active: true }) })
416
+ ] }),
417
+ /* @__PURE__ */ jsx(Message, { messageId: "t_05", align: "end", children: /* @__PURE__ */ jsx(Attachment, { name: "scroller.tsx", meta: "4.2 KB \xB7 TSX" }) }),
418
+ /* @__PURE__ */ jsx(Message, { messageId: "t_06", avatar: /* @__PURE__ */ jsx(Avatar, { size: "sm", fallback: "AI" }), children: /* @__PURE__ */ jsx(Bubble, { variant: "plain", children: /* @__PURE__ */ jsx(StreamingText, { text: streamed, streaming: pending }) }) })
419
+ ] }),
420
+ /* @__PURE__ */ jsx(CardFooter, { className: "border-t border-border pt-(--card-p)", children: /* @__PURE__ */ jsx(
421
+ ChatComposer,
422
+ {
423
+ className: "flex-1",
424
+ placeholder: "Ask a follow-up\u2026",
425
+ busy: pending,
426
+ onStop: () => void 0,
427
+ onAttach: () => void 0,
428
+ footer: "Enter to send \xB7 Shift+Enter for a new line",
429
+ actions: /* @__PURE__ */ jsxs(
430
+ SettingsMenu,
431
+ {
432
+ variant: "subtle",
433
+ size: "xs",
434
+ label: "Change model and effort",
435
+ summary: /* @__PURE__ */ jsxs(Fragment, { children: [
436
+ /* @__PURE__ */ jsx("span", { className: "text-foreground", children: "5.6 Sol" }),
437
+ " ",
438
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "High" })
439
+ ] }),
440
+ children: [
441
+ /* @__PURE__ */ jsx(SettingsRow, { label: "Model", value: "5.6 Sol", children: /* @__PURE__ */ jsxs(MenuRadioGroup, { defaultValue: "sol", children: [
442
+ /* @__PURE__ */ jsx(MenuRadioItem, { value: "sol", children: "5.6 Sol" }),
443
+ /* @__PURE__ */ jsx(MenuRadioItem, { value: "lite", children: "5.6 Lite" })
444
+ ] }) }),
445
+ /* @__PURE__ */ jsx(SettingsRow, { label: "Effort", value: "High", children: /* @__PURE__ */ jsxs(MenuRadioGroup, { defaultValue: "high", children: [
446
+ /* @__PURE__ */ jsx(MenuRadioItem, { value: "low", children: "Low" }),
447
+ /* @__PURE__ */ jsx(MenuRadioItem, { value: "standard", children: "Standard" }),
448
+ /* @__PURE__ */ jsx(MenuRadioItem, { value: "high", children: "High" })
449
+ ] }) }),
450
+ /* @__PURE__ */ jsx(SettingsRow, { label: "Speed", value: "Standard", children: /* @__PURE__ */ jsxs(MenuRadioGroup, { defaultValue: "standard", children: [
451
+ /* @__PURE__ */ jsx(MenuRadioItem, { value: "standard", children: "Standard" }),
452
+ /* @__PURE__ */ jsx(MenuRadioItem, { value: "priority", children: "Priority" })
453
+ ] }) })
454
+ ]
455
+ }
456
+ )
457
+ }
458
+ ) })
459
+ ]
460
+ }
461
+ ) });
462
+ }
463
+
464
+ // src/patterns/chat-panel.meta.ts
465
+ var chatPanelMeta = {
466
+ name: "ChatPanel",
467
+ summary: "A full-height conversation surface: titled header, scrolling transcript, and a composer pinned to the bottom \u2014 the assembled shape of an AI assistant, a support thread, or a DM.",
468
+ intent: "Reach for this for any conversation that owns its screen \u2014 an assistant, a support inbox thread, a DM. Not for a chat docked beside other work (compose the same three parts inside a Sheet or a PanelGroup instead), and not for a comment thread under an article, which is a FeedPage concern.",
469
+ notes: [
470
+ "The panel is a fixed-height column, not a growing page. `h-[calc(100dvh_-_--spacing(12))]` on the Card (the viewport minus the Page's py-6, in spacing units so density moves both together \u2014 a fixed `3rem` drifts the moment density moves the padding) and `flex-1 min-h-0` on the MessageScroller are what make the transcript scroll instead of the document \u2014 the single most common thing to get wrong here, and it fails by pushing the composer off the bottom of the screen.",
471
+ "The panel is a Card, not a hand-built bordered section: CardHeader (title, description, CardAction), the MessageScroller and a CardFooter all inset by the card's own `--card-p`, so the three regions share one edge at any card density.",
472
+ "`dvh` rather than `vh`: on mobile the URL bar collapses, and a `vh`-sized panel puts the composer under it exactly when someone is trying to type.",
473
+ "Region cap is `feed` (640px) \u2014 a conversation is a reading column, and the Bubble measure cap is tuned to the same assumption.",
474
+ "The composer sits outside the scroll region, in a bordered CardFooter, so it stays put while the transcript moves.",
475
+ "Grouping is derived from the data (`turns[i - 1].mine === turn.mine`) rather than hand-set, and the tail is dropped on continuations \u2014 that pairing is what makes a run read as one block.",
476
+ 'The assistant\'s long-form answer uses `variant="plain"`. A tinted box around a code block reads as a quote rather than as a turn.',
477
+ "The answer in flight is a `StreamingText` in a `plain` Bubble, and ONE `pending` flag drives both it and the composer's `busy`. They are the same wait reported at the two ends of the panel; letting them drift gives you a composer still saying 'sending' under an answer that already finished.",
478
+ "A spinner `Marker` (or `ThinkingOrb`) belongs BEFORE the first token and is replaced once tokens land, not kept alongside. Two indicators for one wait read as two things happening \u2014 which is also why `StreamingText` takes `caret={false}` when something adjacent already reports it.",
479
+ 'MessageScroller needs no configuring for this: it re-follows on "a message grew" (ResizeObserver) as well as "a message arrived", so the newest token stays pinned while a reader who has scrolled up is left alone.',
480
+ "For a new thread, swap the transcript's children for an `<EmptyState>` inside the same MessageScroller \u2014 the panel, header and composer are unchanged, which is the point of keeping them separate."
481
+ ],
482
+ a11y: [
483
+ "The panel header's title is the page's <h1> (an h1 inside CardTitle, which carries the type); the transcript is a scroll region with native overflow, so keyboard scrolling works unchanged.",
484
+ "The answer in flight carries `aria-busy` rather than sitting in a live region. That is deliberate: a live region around streamed text announces every token, which is unusable. Announce once when the turn settles, from whatever owns the turn \u2014 the incomplete state is still exposed, just not narrated.",
485
+ "The composer is a real <form> whose Enter handling skips IME composition, and every icon-only control in the panel carries an accessible label."
486
+ ]
487
+ };
488
+ var STATUS_TONE = { ready: "standard", building: "warning", queued: "neutral", failed: "destructive" };
489
+ var statusTone = (status) => STATUS_TONE[status];
490
+ var MIN = 60 * 1e3;
491
+ var HOUR = 60 * MIN;
492
+ var stats = [
493
+ { label: "Revenue", value: "$12,480", delta: "+8.2%", up: true, note: "vs. previous 30 days" },
494
+ { label: "Collects", value: "1,284", delta: "+3.1%", up: true, note: "182 from new collectors" },
495
+ { label: "New collectors", value: "312", delta: "+12.4%", up: true, note: "best month this year" },
496
+ { label: "Sell-through", value: "64%", delta: "\u22121.8%", up: false, note: "across 48 active editions" }
497
+ ];
498
+ var deployments = [
499
+ { id: "dp_01", project: "storefront", commit: "a41f2c9", status: "ready", duration: "42s", updated: 2 * MIN },
500
+ { id: "dp_02", project: "api", commit: "7c03bd1", status: "building", duration: "1m 08s", updated: 9 * MIN },
501
+ { id: "dp_03", project: "docs", commit: "e992a70", status: "queued", duration: "\u2014", updated: 12 * MIN },
502
+ { id: "dp_04", project: "worker", commit: "51b8e04", status: "failed", duration: "18s", updated: 1 * HOUR },
503
+ { id: "dp_05", project: "storefront", commit: "90cc41a", status: "ready", duration: "39s", updated: 3 * HOUR },
504
+ { id: "dp_06", project: "renderer", commit: "b7714dd", status: "ready", duration: "2m 31s", updated: 5 * HOUR },
505
+ { id: "dp_07", project: "api", commit: "f30a9e2", status: "ready", duration: "1m 02s", updated: 8 * HOUR }
506
+ ];
507
+ var activity = [
508
+ { id: "ac_01", who: "Nina Simone", initials: "NS", what: "collected Gold Rush #42", when: 2 * MIN },
509
+ { id: "ac_02", who: "Grace Hopper", initials: "GH", what: "minted a 100-run edition", when: 26 * MIN },
510
+ { id: "ac_03", who: "Ada Lovelace", initials: "AL", what: "followed you", when: 1 * HOUR },
511
+ { id: "ac_04", who: "Mary Shelley", initials: "MS", what: "collected Quiet Harbor", when: 3 * HOUR },
512
+ { id: "ac_05", who: "Frida Kahlo", initials: "FK", what: "tipped 0.2 SOL on Process", when: 6 * HOUR }
513
+ ];
514
+ function DashboardPage() {
515
+ return /* @__PURE__ */ jsx(
516
+ AppShell,
517
+ {
518
+ header: /* @__PURE__ */ jsx(AppHeader, { title: /* @__PURE__ */ jsx("span", { className: "text-title-sm", children: "Atelier" }) }),
519
+ bottomNav: /* @__PURE__ */ jsxs(BottomNav, { "aria-label": "Primary", children: [
520
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "grid" }), label: "Overview", showLabel: true, active: true, render: /* @__PURE__ */ jsx("a", { href: "/" }) }),
521
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "image-stack" }), label: "Editions", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/editions" }) }),
522
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "user" }), label: "Collectors", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/collectors" }) }),
523
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "bell" }), label: "Activity", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/activity" }) }),
524
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "gear" }), label: "Settings", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/settings" }) })
525
+ ] }),
526
+ sidebar: /* @__PURE__ */ jsxs(Sidebar, { children: [
527
+ /* @__PURE__ */ jsx(SidebarHeader, { children: /* @__PURE__ */ jsx("span", { className: "text-title-sm", children: "Atelier" }) }),
528
+ /* @__PURE__ */ jsxs(SidebarNav, { children: [
529
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "grid" }), label: "Overview", active: true, render: /* @__PURE__ */ jsx("a", { href: "/" }) }),
530
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "image-stack" }), label: "Editions", render: /* @__PURE__ */ jsx("a", { href: "/editions" }) }),
531
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "user" }), label: "Collectors", render: /* @__PURE__ */ jsx("a", { href: "/collectors" }) }),
532
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "bell" }), label: "Activity", render: /* @__PURE__ */ jsx("a", { href: "/activity" }) }),
533
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "gear" }), label: "Settings", render: /* @__PURE__ */ jsx("a", { href: "/settings" }) })
534
+ ] })
535
+ ] }),
536
+ children: /* @__PURE__ */ jsxs(Page, { max: "wide", gap: 4, className: "py-6", children: [
537
+ /* @__PURE__ */ jsx(
538
+ PageHeader,
539
+ {
540
+ title: "Overview",
541
+ description: "Sales, collects, and edition activity for the last 30 days.",
542
+ actions: /* @__PURE__ */ jsxs(Row, { gap: 2, children: [
543
+ /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Export" }),
544
+ /* @__PURE__ */ jsx(Button, { children: "New edition" })
545
+ ] })
546
+ }
547
+ ),
548
+ /* @__PURE__ */ jsx(Grid, { cols: { base: 1, sm: 2, xl: 4 }, gap: 3, children: stats.map((stat) => /* @__PURE__ */ jsxs(Card, { children: [
549
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
550
+ /* @__PURE__ */ jsx(CardDescription, { children: stat.label }),
551
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-mono-xl", children: stat.value }),
552
+ /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsxs(Badge, { tone: stat.up ? "success" : "neutral", size: "sm", children: [
553
+ /* @__PURE__ */ jsx(Icon, { name: stat.up ? "chevron-up" : "chevron-down" }),
554
+ stat.delta
555
+ ] }) })
556
+ ] }),
557
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("p", { className: "text-caption text-muted-foreground", children: stat.note }) })
558
+ ] }, stat.label)) }),
559
+ /* @__PURE__ */ jsxs(Grid, { cols: { base: 1, xl: 3 }, gap: 3, children: [
560
+ /* @__PURE__ */ jsxs(Card, { className: "xl:col-span-2", children: [
561
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
562
+ /* @__PURE__ */ jsx(CardTitle, { children: "Recent deployments" }),
563
+ /* @__PURE__ */ jsx(CardDescription, { children: "Builds across every project, newest first." }),
564
+ /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/deployments" }), children: "View all" }) })
565
+ ] }),
566
+ /* @__PURE__ */ jsx(CardContent, { flush: true, children: /* @__PURE__ */ jsx(
567
+ DataTable,
568
+ {
569
+ data: deployments,
570
+ rowKey: (row) => row.id,
571
+ columns: [
572
+ { key: "project", header: "Project", sortable: true, className: "text-label-md" },
573
+ { key: "commit", header: "Commit", cell: (row) => /* @__PURE__ */ jsx("span", { className: "text-mono-caption text-muted-foreground", children: row.commit }) },
574
+ { key: "status", header: "Status", cell: (row) => /* @__PURE__ */ jsx(Badge, { appearance: "plain", tone: statusTone(row.status), dot: true, children: row.status }) },
575
+ { key: "duration", header: "Duration", align: "end", cell: (row) => /* @__PURE__ */ jsx("span", { className: "text-mono-caption", children: row.duration }) },
576
+ { key: "updated", header: "Updated", align: "end", cell: (row) => /* @__PURE__ */ jsx(RelativeTime, { date: Date.now() - row.updated, className: "text-body-xs text-muted-foreground" }) }
577
+ ]
578
+ }
579
+ ) })
580
+ ] }),
581
+ /* @__PURE__ */ jsxs(Card, { children: [
582
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
583
+ /* @__PURE__ */ jsx(CardTitle, { children: "Activity" }),
584
+ /* @__PURE__ */ jsx(CardDescription, { children: "Collects, mints, and follows." })
585
+ ] }),
586
+ /* @__PURE__ */ jsx(CardList, { children: activity.map((item) => /* @__PURE__ */ jsx(
587
+ Entity,
588
+ {
589
+ density: "compact",
590
+ leading: /* @__PURE__ */ jsx(Avatar, { size: "sm", fallback: item.initials, alt: item.who }),
591
+ title: /* @__PURE__ */ jsxs("span", { className: "text-body-sm", children: [
592
+ /* @__PURE__ */ jsx("span", { className: "text-label-md", children: item.who }),
593
+ " ",
594
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: item.what })
595
+ ] }),
596
+ action: /* @__PURE__ */ jsx(RelativeTime, { date: Date.now() - item.when, className: "text-caption text-muted-foreground" })
597
+ },
598
+ item.id
599
+ )) })
600
+ ] })
601
+ ] })
602
+ ] })
603
+ }
604
+ );
605
+ }
606
+
607
+ // src/patterns/dashboard-page.meta.ts
608
+ var dashboardPageMeta = {
609
+ name: "DashboardPage",
610
+ summary: "App-shell dashboard: sidebar chrome (AppHeader + BottomNav below lg), a wide content region, a stat-card row that breathes, and a data table \u2014 the default shape for operator/analytics home routes.",
611
+ intent: "Reach for this when a route's job is monitoring or managing many records (metrics, deployments, orders, posts). Not for reading-focused routes (use FeedPage) or single-record routes (use ListDetailPage's detail side).",
612
+ notes: [
613
+ "Region cap is `wide` (1280px): dashboards hold tables and multi-column grids, never a reading column.",
614
+ "Stat cards: CardHeader carries label (CardDescription) + value + trend badge (CardAction pins it top-right); KPI values are `text-mono-xl` \u2014 the metric mono tier, tabular figures \u2014 never a heading tier.",
615
+ "Stat cards use an explicit cols map (1 \u2192 2 \u2192 4) \u2014 a 4-stat row must fold to 2\xD72, never strand an orphan the way auto-fit can.",
616
+ "The table runs flush to the card edges with `<CardContent flush>`, which hands the card's padding to the table (`--table-edge-px`), so the first and last columns line up with the CardHeader at any card density \u2014 never hand-set pl-6/pr-6. Numeric columns align end in mono.",
617
+ "The Activity list is Entity rows in CardList, inset to the card padding and divided by hairlines \u2014 the same list shape as ListDetailPage, never CardContent with hand-tuned px.",
618
+ "Believable density is part of the pattern: 7 rows, commit hashes in mono-caption, RelativeTime muted \u2014 never 3 rows of filler.",
619
+ "The page has exactly one heading-1 (PageHeader); card titles are Title tier, not headings.",
620
+ "Chrome (Sidebar) is outside the content grid; the Page region caps only content.",
621
+ "Sidebar hides below lg, so the shell's header carries an AppHeader and its footer a BottomNav with the same destinations \u2014 never strand mobile users without navigation."
622
+ ],
623
+ a11y: [
624
+ 'AppShell provides the skip link and <main id="main-content"> landmark.',
625
+ "The active SidebarItem and BottomNavItem carry aria-current via `active`.",
626
+ "DataTable headers are real <th>; sortable columns are announced."
627
+ ]
628
+ };
629
+ var posts = [
630
+ {
631
+ id: "p_01",
632
+ author: "Ada Lovelace",
633
+ handle: "@ada \xB7 2h",
634
+ initials: "AL",
635
+ body: "New edition drops Friday \u2014 100 signed prints, each with the on-chain original.",
636
+ image: "/media/edition-cover.svg",
637
+ alt: "Edition cover \u2014 Gold Rush series",
638
+ likes: 128,
639
+ comments: 24
640
+ },
641
+ {
642
+ id: "p_02",
643
+ author: "Grace Hopper",
644
+ handle: "@grace \xB7 5h",
645
+ initials: "GH",
646
+ body: "Process shot from this week's series. Compilers and copper ink.",
647
+ image: "/media/process-shot.svg",
648
+ alt: "Work-in-progress plotter drawing in copper ink",
649
+ likes: 86,
650
+ comments: 9
651
+ }
652
+ ];
653
+ function FeedPage() {
654
+ return /* @__PURE__ */ jsxs(Page, { as: "main", max: "feed", gap: 4, className: "py-6", children: [
655
+ /* @__PURE__ */ jsx(PageHeader, { title: "Home", description: "Latest posts from artists you follow." }),
656
+ /* @__PURE__ */ jsx(Stack, { gap: 3, children: posts.map((post) => /* @__PURE__ */ jsx("article", { children: /* @__PURE__ */ jsxs(Card, { className: "overflow-hidden", children: [
657
+ /* @__PURE__ */ jsx(CardList, { children: /* @__PURE__ */ jsx(
658
+ Entity,
659
+ {
660
+ leading: /* @__PURE__ */ jsx(Avatar, { fallback: post.initials, alt: post.author }),
661
+ title: post.author,
662
+ subtitle: post.handle,
663
+ action: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Post options", children: /* @__PURE__ */ jsx(Icon, { name: "ellipsis" }) })
664
+ }
665
+ ) }),
666
+ /* @__PURE__ */ jsx(AspectBox, { ratio: 16 / 9, children: /* @__PURE__ */ jsx("img", { src: post.image, alt: post.alt, className: "size-full object-cover" }) }),
667
+ /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col gap-2", children: [
668
+ /* @__PURE__ */ jsx("p", { className: "text-body-md text-foreground", children: post.body }),
669
+ /* @__PURE__ */ jsxs(Row, { gap: 1, className: "-ms-3", children: [
670
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", "aria-label": `Like \u2014 ${post.likes}`, children: [
671
+ /* @__PURE__ */ jsx(Icon, { name: "heart" }),
672
+ post.likes
673
+ ] }),
674
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", "aria-label": `Comments \u2014 ${post.comments}`, children: [
675
+ /* @__PURE__ */ jsx(Icon, { name: "comment" }),
676
+ post.comments
677
+ ] }),
678
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": "Share", children: /* @__PURE__ */ jsx(Icon, { name: "share" }) })
679
+ ] })
680
+ ] })
681
+ ] }) }, post.id)) }),
682
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", loadingLabel: "Loading\u2026", children: "Load more posts" })
683
+ ] });
684
+ }
685
+
686
+ // src/patterns/feed-page.meta.ts
687
+ var feedPageMeta = {
688
+ name: "FeedPage",
689
+ summary: "Single-column reading feed: a feed-width region, one Card per post (Entity byline, zero-shift media, body copy, quiet action row), and explicit load-more pagination.",
690
+ intent: "Reach for this for timelines, activity streams, and any reverse-chronological reading surface. Not for record management (DashboardPage) or side-by-side browsing (ListDetailPage).",
691
+ notes: [
692
+ "Region cap is `feed` (640px) \u2014 a single reading column; media and copy share one measure.",
693
+ "Post media sits in an AspectBox so late-loading images never shift the column.",
694
+ "The byline is Entity (Avatar + name + handle) in a CardList \u2014 the same row primitive as everywhere else, inset to the card padding by CardList rather than by zeroing paddings on CardHeader and Entity.",
695
+ "Actions are ghost sm Buttons (icon + count) in a Row, pulled out by the button's own padding (`-ms-3`) so the first icon sits on the text edge: quiet chrome under content, not competing pills.",
696
+ "Pagination is an explicit load-more Button (secondary; set `loading` while the next page fetches \u2014 `loadingLabel` names that state), not infinite scroll \u2014 predictable for keyboard and AT users."
697
+ ],
698
+ a11y: [
699
+ "Each post is an <article>, so AT users can jump between posts.",
700
+ "Icon-only action buttons carry aria-labels.",
701
+ "Media images get meaningful alt text from the post, empty alt only if decorative."
702
+ ]
703
+ };
704
+ var features2 = [
705
+ {
706
+ icon: "sparkles",
707
+ title: "Own the surface",
708
+ body: "Semantic tokens end-to-end \u2014 re-theme the whole product without touching a component."
709
+ },
710
+ {
711
+ icon: "component",
712
+ title: "Behavior included",
713
+ body: "Focus, keyboard nav, and ARIA ride on Base UI primitives under every surface."
714
+ },
715
+ {
716
+ icon: "layout",
717
+ title: "Pages, not just parts",
718
+ body: "Purpose-named regions, an 8px rhythm, and canonical page shells for the routes every product needs."
719
+ }
720
+ ];
721
+ function LandingPage() {
722
+ return /* @__PURE__ */ jsxs("div", { className: "min-h-dvh bg-background", children: [
723
+ /* @__PURE__ */ jsx("header", { children: /* @__PURE__ */ jsxs(Region, { as: "nav", "aria-label": "Main", className: "grid h-16 grid-cols-[1fr_auto_1fr] items-center", children: [
724
+ /* @__PURE__ */ jsx("span", { className: "justify-self-start text-title-sm", children: "Sable" }),
725
+ /* @__PURE__ */ jsxs(Row, { gap: 1, className: "justify-self-center max-sm:hidden", children: [
726
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/docs" }), children: "Docs" }),
727
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/patterns" }), children: "Patterns" }),
728
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/themes" }), children: "Themes" })
729
+ ] }),
730
+ /* @__PURE__ */ jsx(Button, { size: "sm", className: "justify-self-end", render: /* @__PURE__ */ jsx("a", { href: "/signup" }), children: "Get started" })
731
+ ] }) }),
732
+ /* @__PURE__ */ jsxs("main", { children: [
733
+ /* @__PURE__ */ jsx(Region, { as: "section", max: "wide", className: "py-24", children: /* @__PURE__ */ jsxs(Stack, { gap: 4, align: "center", className: "text-center", children: [
734
+ /* @__PURE__ */ jsx(Badge, { appearance: "outline", size: "sm", children: "Now in beta" }),
735
+ /* @__PURE__ */ jsx("h1", { className: "max-w-4xl text-balance text-display-2xl", children: "Ship product UI your agents can't get wrong" }),
736
+ /* @__PURE__ */ jsx("p", { className: "max-w-(--measure-prose) text-body-lg text-muted-foreground", children: "An owned, themeable component system with enforced tokens, typography, and motion \u2014 documented for machines as carefully as for people." }),
737
+ /* @__PURE__ */ jsxs(Row, { gap: 2, justify: "center", children: [
738
+ /* @__PURE__ */ jsx(Button, { size: "cta", children: "Get started" }),
739
+ /* @__PURE__ */ jsx(Button, { size: "cta", variant: "outline", children: "Read the docs" })
740
+ ] })
741
+ ] }) }),
742
+ /* @__PURE__ */ jsx(Region, { as: "section", max: "wide", className: "pb-24", children: /* @__PURE__ */ jsx(Reveal, { children: /* @__PURE__ */ jsx(Grid, { minItemWidth: "calc(var(--spacing) * 64)", children: features2.map((feature) => /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col gap-2", children: [
743
+ /* @__PURE__ */ jsx(Icon, { name: feature.icon, className: "text-xl text-muted-foreground" }),
744
+ /* @__PURE__ */ jsx("h2", { className: "text-title-lg", children: feature.title }),
745
+ /* @__PURE__ */ jsx("p", { className: "text-body-sm text-muted-foreground", children: feature.body })
746
+ ] }) }, feature.title)) }) }) })
747
+ ] }),
748
+ /* @__PURE__ */ jsx("footer", { className: "border-t border-border", children: /* @__PURE__ */ jsxs(Region, { className: "flex h-16 items-center justify-between", children: [
749
+ /* @__PURE__ */ jsx("span", { className: "text-caption text-muted-foreground", children: "\xA9 2026 Sable" }),
750
+ /* @__PURE__ */ jsxs(Row, { gap: 3, children: [
751
+ /* @__PURE__ */ jsx("a", { href: "/github", className: "text-caption text-muted-foreground motion-interactive hover:text-foreground", children: "GitHub" }),
752
+ /* @__PURE__ */ jsx("a", { href: "/changelog", className: "text-caption text-muted-foreground motion-interactive hover:text-foreground", children: "Changelog" })
753
+ ] })
754
+ ] }) })
755
+ ] });
756
+ }
757
+
758
+ // src/patterns/landing-page.meta.ts
759
+ var landingPageMeta = {
760
+ name: "LandingPage",
761
+ summary: "Marketing hero + feature grid: display-tier headline as the page's single loud moment, an eyebrow kicker, a two-action CTA row, and a three-up feature grid that reveals on scroll.",
762
+ intent: "Reach for this for the marketing/landing route of a product \u2014 the one page where Display type and scroll reveals belong. Not for product UI (display type and one-per-view rules forbid it inside the app).",
763
+ notes: [
764
+ "Display type is the marketing voice: exactly one display headline per page, tight leading, weight 700+.",
765
+ 'The eyebrow is a small outline Badge (`size="sm"`) \u2014 the badge\'s own size, never a type class forced onto its label; hierarchy above the headline is eyebrow \u2192 display \u2192 lede.',
766
+ "The lede is body-lg capped at the prose measure \u2014 the headline may be wide, reading text may not.",
767
+ "Two CTAs max: one primary, one quiet outline. More actions dilute the hero.",
768
+ "Reveal wraps below-the-fold sections only; it no-ops under reduced motion via the motion policy.",
769
+ "Feature cards are Cards in a Grid with minItemWidth (three-up that reflows on its own width), each icon + title + body-sm copy."
770
+ ],
771
+ a11y: [
772
+ "The hero headline is the page h1; feature titles are h2s restyled with the Title tier.",
773
+ "Reveal renders content immediately (no animation) when prefers-reduced-motion is set.",
774
+ "CTA buttons are real links or buttons with visible text \u2014 no icon-only CTAs."
775
+ ]
776
+ };
777
+ var members = [
778
+ { id: "u_01", name: "Ada Lovelace", handle: "@ada", role: "Owner", initials: "AL", pending: false },
779
+ { id: "u_02", name: "Grace Hopper", handle: "@grace", role: "Admin", initials: "GH", pending: false },
780
+ { id: "u_03", name: "Mary Shelley", handle: "@mary", role: "Member", initials: "MS", pending: false },
781
+ { id: "u_04", name: "Nina Simone", handle: "@nina", role: "Member", initials: "NS", pending: false },
782
+ { id: "u_05", name: "Frida Kahlo", handle: "@frida", role: "Member", initials: "FK", pending: false },
783
+ { id: "u_06", name: "hopper@navy.mil", handle: "invited 2 days ago", role: "Member", initials: "?", pending: true }
784
+ ];
785
+ function ListDetailPage() {
786
+ return /* @__PURE__ */ jsxs(Page, { as: "main", max: "wide", gap: 4, className: "py-6", children: [
787
+ /* @__PURE__ */ jsx(
788
+ PageHeader,
789
+ {
790
+ title: "Members",
791
+ description: "Everyone with access to this workspace.",
792
+ actions: /* @__PURE__ */ jsx(Button, { children: "Invite member" })
793
+ }
794
+ ),
795
+ /* @__PURE__ */ jsxs(Columns, { count: { base: 1, lg: 12 }, children: [
796
+ /* @__PURE__ */ jsx(Col, { span: { base: 1, lg: 8 }, children: /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
797
+ /* @__PURE__ */ jsx(SearchInput, { placeholder: "Search members", "aria-label": "Search members" }),
798
+ /* @__PURE__ */ jsx(Card, { className: "overflow-hidden", children: /* @__PURE__ */ jsx(CardList, { children: members.map((member) => /* @__PURE__ */ jsx(
799
+ Entity,
800
+ {
801
+ leading: /* @__PURE__ */ jsx(
802
+ Avatar,
803
+ {
804
+ fallback: member.pending ? "\u2022" : member.initials,
805
+ alt: member.pending ? "" : member.name
806
+ }
807
+ ),
808
+ title: /* @__PURE__ */ jsx("span", { className: member.pending ? "text-muted-foreground" : void 0, children: member.name }),
809
+ subtitle: member.handle,
810
+ action: member.pending ? /* @__PURE__ */ jsx(Badge, { children: "Invited" }) : /* @__PURE__ */ jsx(Badge, { appearance: "outline", children: member.role })
811
+ },
812
+ member.id
813
+ )) }) })
814
+ ] }) }),
815
+ /* @__PURE__ */ jsx(Col, { span: { base: 1, lg: 4 }, children: /* @__PURE__ */ jsx("aside", { className: "sticky top-6", children: /* @__PURE__ */ jsxs(Card, { children: [
816
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { children: "Workspace" }) }),
817
+ /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col gap-3", children: [
818
+ /* @__PURE__ */ jsxs(Description, { children: [
819
+ /* @__PURE__ */ jsx(DescriptionItem, { term: "Members", detail: /* @__PURE__ */ jsx("span", { className: "text-mono-md", children: "5" }) }),
820
+ /* @__PURE__ */ jsx(DescriptionItem, { term: "Pending invites", detail: /* @__PURE__ */ jsx("span", { className: "text-mono-md", children: "1" }) }),
821
+ /* @__PURE__ */ jsx(DescriptionItem, { term: "Seats used", detail: /* @__PURE__ */ jsx("span", { className: "text-mono-md", children: "6 / 10" }) })
822
+ ] }),
823
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-1 self-start", children: "Manage plan" })
824
+ ] })
825
+ ] }) }) })
826
+ ] })
827
+ ] });
828
+ }
829
+
830
+ // src/patterns/list-detail-page.meta.ts
831
+ var listDetailPageMeta = {
832
+ name: "ListDetailPage",
833
+ summary: "Two-column list-detail: a wide region split 8/4, the record list as Entity rows in the main column, and a sticky supporting rail (detail, filters, or summary) beside it.",
834
+ intent: "Reach for this when a route lists records AND keeps one selected/summarized in view (members + member detail, inbox + message, orders + order). For pure browsing with no rail, a single-column FeedPage or DashboardPage table is simpler.",
835
+ notes: [
836
+ "Region cap is `wide`; the split is Columns 12 \u2192 Col 8 + Col 4, collapsing to stacked full-width columns below lg via breakpoint maps.",
837
+ "List rows are Entity inside one bordered Card, in CardList: it insets each row to the card padding and divides them with hairlines \u2014 on a dark canvas rows need an anchoring surface, not a flat float.",
838
+ "Pending records live in the same list, muted (muted title + `Invited` badge), instead of a separate section.",
839
+ "The rail card holds label/value pairs as a Description list \u2014 muted terms, mono (tabular) values \u2014 plus one quiet action. Never hand-built `justify-between` rows.",
840
+ "The rail is sticky (top on the 4px scale) so the summary stays in view while the list scrolls."
841
+ ],
842
+ a11y: [
843
+ "Entity rows given `interactive` + `onSelect` (to open a member's detail) render as real buttons; keyboard focus follows list order. Rows here are presentational.",
844
+ "The rail is an <aside> landmark; the list column is the main content."
845
+ ]
846
+ };
847
+ function SettingsPage() {
848
+ return /* @__PURE__ */ jsx(
849
+ AppShell,
850
+ {
851
+ header: /* @__PURE__ */ jsx(AppHeader, { title: /* @__PURE__ */ jsx("span", { className: "text-title-sm", children: "Atelier" }) }),
852
+ bottomNav: /* @__PURE__ */ jsxs(BottomNav, { "aria-label": "Primary", children: [
853
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "grid" }), label: "Overview", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/" }) }),
854
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "image-stack" }), label: "Editions", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/editions" }) }),
855
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "user" }), label: "Collectors", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/collectors" }) }),
856
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "bell" }), label: "Activity", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/activity" }) }),
857
+ /* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "gear" }), label: "Settings", showLabel: true, active: true, render: /* @__PURE__ */ jsx("a", { href: "/settings" }) })
858
+ ] }),
859
+ sidebar: /* @__PURE__ */ jsxs(Sidebar, { children: [
860
+ /* @__PURE__ */ jsx(SidebarHeader, { children: /* @__PURE__ */ jsx("span", { className: "text-title-sm", children: "Atelier" }) }),
861
+ /* @__PURE__ */ jsxs(SidebarNav, { children: [
862
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "grid" }), label: "Overview", render: /* @__PURE__ */ jsx("a", { href: "/" }) }),
863
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "image-stack" }), label: "Editions", render: /* @__PURE__ */ jsx("a", { href: "/editions" }) }),
864
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "user" }), label: "Collectors", render: /* @__PURE__ */ jsx("a", { href: "/collectors" }) }),
865
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "bell" }), label: "Activity", render: /* @__PURE__ */ jsx("a", { href: "/activity" }) }),
866
+ /* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "gear" }), label: "Settings", active: true, render: /* @__PURE__ */ jsx("a", { href: "/settings" }) })
867
+ ] })
868
+ ] }),
869
+ children: /* @__PURE__ */ jsxs(Page, { max: "content", gap: 4, className: "py-6", children: [
870
+ /* @__PURE__ */ jsx(
871
+ PageHeader,
872
+ {
873
+ title: "Settings",
874
+ description: "Your profile, notifications, and account controls."
875
+ }
876
+ ),
877
+ /* @__PURE__ */ jsx(Form, { onSubmit: (event) => event.preventDefault(), children: /* @__PURE__ */ jsxs(Fieldset, { children: [
878
+ /* @__PURE__ */ jsx(FieldsetLegend, { children: "Profile" }),
879
+ /* @__PURE__ */ jsx(FieldsetDescription, { children: "How you appear across the app." }),
880
+ /* @__PURE__ */ jsxs(FieldsetContent, { children: [
881
+ /* @__PURE__ */ jsx(Field, { label: "Display name", required: true, children: /* @__PURE__ */ jsx(Input, { name: "displayName", defaultValue: "ada" }) }),
882
+ /* @__PURE__ */ jsx(Field, { label: "Bio", description: "Shown on your public profile.", children: /* @__PURE__ */ jsx(Textarea, { name: "bio", rows: 3, placeholder: "Tell collectors who you are" }) }),
883
+ /* @__PURE__ */ jsx(Field, { label: "Timezone", children: /* @__PURE__ */ jsxs(Select, { defaultValue: "utc", name: "timezone", children: [
884
+ /* @__PURE__ */ jsx(SelectTrigger, {}),
885
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
886
+ /* @__PURE__ */ jsx(SelectItem, { value: "utc", children: "UTC" }),
887
+ /* @__PURE__ */ jsx(SelectItem, { value: "est", children: "Eastern (UTC\u22125)" }),
888
+ /* @__PURE__ */ jsx(SelectItem, { value: "pst", children: "Pacific (UTC\u22128)" })
889
+ ] })
890
+ ] }) })
891
+ ] }),
892
+ /* @__PURE__ */ jsx(FieldsetFooter, { children: /* @__PURE__ */ jsx(Button, { type: "submit", children: "Save profile" }) })
893
+ ] }) }),
894
+ /* @__PURE__ */ jsx(Form, { onSubmit: (event) => event.preventDefault(), children: /* @__PURE__ */ jsxs(Fieldset, { children: [
895
+ /* @__PURE__ */ jsx(FieldsetLegend, { children: "Notifications" }),
896
+ /* @__PURE__ */ jsx(FieldsetDescription, { children: "What we email you about." }),
897
+ /* @__PURE__ */ jsxs(FieldsetContent, { children: [
898
+ /* @__PURE__ */ jsx(Field, { label: "New collector alerts", orientation: "horizontal", children: /* @__PURE__ */ jsx(Switch, { name: "collectorAlerts", defaultChecked: true }) }),
899
+ /* @__PURE__ */ jsx(Field, { label: "Weekly digest", orientation: "horizontal", children: /* @__PURE__ */ jsx(Switch, { name: "weeklyDigest" }) })
900
+ ] }),
901
+ /* @__PURE__ */ jsx(FieldsetFooter, { children: /* @__PURE__ */ jsx(Button, { type: "submit", children: "Save notifications" }) })
902
+ ] }) }),
903
+ /* @__PURE__ */ jsxs(Fieldset, { children: [
904
+ /* @__PURE__ */ jsx(FieldsetLegend, { children: "Danger zone" }),
905
+ /* @__PURE__ */ jsx(FieldsetDescription, { children: "Deleting your account removes your posts and collections permanently." }),
906
+ /* @__PURE__ */ jsx(FieldsetFooter, { children: /* @__PURE__ */ jsx(Button, { variant: "destructive", children: "Delete account" }) })
907
+ ] })
908
+ ] })
909
+ }
910
+ );
911
+ }
912
+
913
+ // src/patterns/settings-page.meta.ts
914
+ var settingsPageMeta = {
915
+ name: "SettingsPage",
916
+ summary: "Stacked settings sections inside the app shell: a content-width region, one Fieldset card per concern (each with its own save), auto-wired Form/Field rows, and a destructive zone last.",
917
+ intent: "Reach for this for account/profile/workspace settings and any 'edit a handful of grouped preferences' route. Not for a single short form (use AuthPage's centered form shape) or bulk record editing (use DashboardPage + DataTable).",
918
+ notes: [
919
+ "Region cap is `content` (896px): settings panes hold labeled rows and the occasional 2-up field, never a full grid.",
920
+ "One Fieldset per concern, each with its own footer save \u2014 per-section saves beat one page-wide submit for settings.",
921
+ "Field wires label/description/error a11y automatically; never hand-roll <label> + <input> pairs here.",
922
+ "The destructive section is visually last, isolated in its own Fieldset with a destructive-variant action.",
923
+ 'Toggles use orientation="horizontal" Fields so the label sits beside the Switch.',
924
+ "Lives in the same AppShell as DashboardPage: Sidebar from lg up, AppHeader + BottomNav below it, with Settings marked active in both."
925
+ ],
926
+ a11y: [
927
+ 'AppShell provides the skip link and <main id="main-content"> landmark; the active SidebarItem and BottomNavItem carry aria-current.',
928
+ "Each section is a real <fieldset> with a <legend> (FieldsetLegend).",
929
+ "Field auto-wires htmlFor/id, aria-describedby (description + error), aria-invalid, and required.",
930
+ "The danger action is a real button labeled by its visible text \u2014 no icon-only destructive controls."
931
+ ]
932
+ };
933
+
934
+ export { AuthPage, BrandLandingPage, ChatPanel, DashboardPage, FeedPage, LandingPage, ListDetailPage, SettingsPage, authPageMeta, brandLandingPageMeta, chatPanelMeta, dashboardPageMeta, feedPageMeta, landingPageMeta, listDetailPageMeta, settingsPageMeta };