@pylonsync/create-pylon 0.19.1 → 0.20.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.
Files changed (675) hide show
  1. package/bin/create-pylon.js +30 -7
  2. package/package.json +1 -1
  3. package/templates/agency/.env.example +7 -0
  4. package/templates/agency/README.md +28 -3
  5. package/templates/agency/app/(marketing)/contact-form.tsx +20 -18
  6. package/templates/agency/app/(marketing)/page.tsx +3 -3
  7. package/templates/agency/app/(marketing)/work/page.tsx +3 -3
  8. package/templates/agency/app/auth-form.tsx +4 -4
  9. package/templates/agency/app/dashboard/dashboard-client.tsx +110 -64
  10. package/templates/agency/app/dashboard/page.tsx +14 -16
  11. package/templates/agency/app/icon.svg +3 -0
  12. package/templates/agency/app/login/page.tsx +12 -14
  13. package/templates/agency/components/marketing.tsx +12 -5
  14. package/templates/agency/functions/seedStudioBackoffice.ts +42 -11
  15. package/templates/agency/lib/agency.ts +22 -0
  16. package/templates/agency/lib/demo.ts +118 -0
  17. package/templates/agency/lib/site.config.ts +19 -13
  18. package/templates/agency/public/favicon.ico +0 -0
  19. package/templates/agency/tests/agency.test.ts +57 -0
  20. package/templates/ai-chat/.env.example +13 -27
  21. package/templates/ai-chat/README.md +53 -60
  22. package/templates/ai-chat/app/(chat)/c/[id]/page.tsx +19 -0
  23. package/templates/ai-chat/app/(chat)/chat-client.tsx +657 -622
  24. package/templates/ai-chat/app/(chat)/layout.tsx +6 -43
  25. package/templates/ai-chat/app/(chat)/not-found.tsx +6 -10
  26. package/templates/ai-chat/app/(chat)/page.tsx +7 -11
  27. package/templates/ai-chat/app/auth-form.tsx +45 -38
  28. package/templates/ai-chat/app/error.tsx +9 -12
  29. package/templates/ai-chat/app/globals.css +464 -102
  30. package/templates/ai-chat/app/layout.tsx +11 -19
  31. package/templates/ai-chat/app/login/page.tsx +39 -24
  32. package/templates/ai-chat/app.ts +118 -43
  33. package/templates/ai-chat/components/chat/composer.tsx +193 -0
  34. package/templates/ai-chat/components/chat/empty-state.tsx +65 -0
  35. package/templates/ai-chat/components/chat/icons.tsx +198 -0
  36. package/templates/ai-chat/components/chat/markdown.tsx +164 -0
  37. package/templates/ai-chat/components/chat/message.tsx +183 -0
  38. package/templates/ai-chat/components/chat/setup.tsx +71 -0
  39. package/templates/ai-chat/components/chat/sidebar.tsx +288 -0
  40. package/templates/ai-chat/functions/_beginTurn.ts +145 -0
  41. package/templates/ai-chat/functions/_finishTurn.ts +55 -0
  42. package/templates/ai-chat/functions/assistantStatus.ts +13 -0
  43. package/templates/ai-chat/functions/claimGuestHistory.ts +39 -0
  44. package/templates/ai-chat/functions/createConversation.ts +28 -0
  45. package/templates/ai-chat/functions/deleteConversation.ts +15 -20
  46. package/templates/ai-chat/functions/prepareGuestClaim.ts +32 -0
  47. package/templates/ai-chat/functions/renameConversation.ts +19 -0
  48. package/templates/ai-chat/functions/respond.ts +88 -0
  49. package/templates/ai-chat/functions/seedExamples.ts +46 -0
  50. package/templates/ai-chat/functions/stopResponse.ts +30 -0
  51. package/templates/ai-chat/lib/brand-icon.ts +7 -0
  52. package/templates/ai-chat/lib/chat.ts +277 -0
  53. package/templates/ai-chat/lib/claim.ts +15 -0
  54. package/templates/ai-chat/lib/examples.ts +179 -0
  55. package/templates/ai-chat/lib/highlight.ts +107 -0
  56. package/templates/ai-chat/lib/markdown.ts +317 -0
  57. package/templates/ai-chat/lib/provider.ts +38 -0
  58. package/templates/ai-chat/lib/site.config.ts +55 -39
  59. package/templates/ai-chat/tests/chat.test.ts +174 -0
  60. package/templates/ai-chat/tests/components.test.tsx +57 -0
  61. package/templates/ai-chat/tests/markdown.test.ts +88 -0
  62. package/templates/ai-chat/tests/provider.test.ts +32 -0
  63. package/templates/backend/consumer/apps/api/app/layout.tsx +33 -0
  64. package/templates/backend/consumer/apps/api/app/page.tsx +54 -0
  65. package/templates/backend/consumer/apps/api/app.ts +166 -0
  66. package/templates/backend/consumer/apps/api/functions/addComment.ts +24 -0
  67. package/templates/backend/consumer/apps/api/functions/createPost.ts +19 -36
  68. package/templates/backend/consumer/apps/api/functions/deletePost.ts +15 -13
  69. package/templates/backend/consumer/apps/api/functions/myProfile.ts +5 -8
  70. package/templates/backend/consumer/apps/api/functions/seedDemo.ts +95 -0
  71. package/templates/backend/consumer/apps/api/functions/setFollow.ts +36 -0
  72. package/templates/backend/consumer/apps/api/functions/setLike.ts +36 -0
  73. package/templates/backend/consumer/apps/api/functions/upsertProfile.ts +43 -37
  74. package/templates/backend/consumer/apps/api/lib/seed.ts +89 -0
  75. package/templates/backend/consumer/apps/api/lib/social.ts +50 -0
  76. package/templates/backend/consumer/apps/api/package.json +8 -2
  77. package/templates/backend/consumer/apps/api/public/images/posts/arch.jpg +0 -0
  78. package/templates/backend/consumer/apps/api/public/images/posts/coffee.jpg +0 -0
  79. package/templates/backend/consumer/apps/api/public/images/posts/dog.jpg +0 -0
  80. package/templates/backend/consumer/apps/api/public/images/posts/lisbon.jpg +0 -0
  81. package/templates/backend/consumer/apps/api/public/images/posts/ramen.jpg +0 -0
  82. package/templates/backend/consumer/apps/api/public/images/posts/run.jpg +0 -0
  83. package/templates/backend/consumer/apps/api/public/images/posts/sunset.jpg +0 -0
  84. package/templates/backend/consumer/apps/api/tsconfig.json +12 -2
  85. package/templates/chat/README.md +43 -21
  86. package/templates/chat/app/chat-client.tsx +495 -85
  87. package/templates/chat/app/error.tsx +1 -1
  88. package/templates/chat/app/globals.css +170 -54
  89. package/templates/chat/app/layout.tsx +12 -9
  90. package/templates/chat/app/not-found.tsx +4 -2
  91. package/templates/chat/app/page.tsx +9 -19
  92. package/templates/chat/app.ts +96 -17
  93. package/templates/chat/components/chat/avatar.tsx +45 -0
  94. package/templates/chat/components/chat/composer.tsx +110 -0
  95. package/templates/chat/components/chat/message-list.tsx +285 -0
  96. package/templates/chat/components/chat/sidebar.tsx +190 -0
  97. package/templates/chat/functions/joinChat.ts +83 -0
  98. package/templates/chat/functions/renameMember.ts +25 -0
  99. package/templates/chat/lib/chat.ts +188 -0
  100. package/templates/chat/lib/seed.ts +78 -0
  101. package/templates/chat/tests/chat.test.ts +159 -0
  102. package/templates/chat/tests/composer.test.tsx +54 -0
  103. package/templates/consumer/README.md +3 -3
  104. package/templates/consumer/app/globals.css +10 -3
  105. package/templates/consumer/app/icon.svg +1 -0
  106. package/templates/consumer/components/social/avatar.tsx +25 -11
  107. package/templates/consumer/functions/seedFeed.ts +3 -3
  108. package/templates/consumer/lib/seed.ts +43 -85
  109. package/templates/consumer/public/favicon.ico +0 -0
  110. package/templates/consumer/public/images/posts/arch.jpg +0 -0
  111. package/templates/consumer/public/images/posts/coffee.jpg +0 -0
  112. package/templates/consumer/public/images/posts/dog.jpg +0 -0
  113. package/templates/consumer/public/images/posts/lisbon.jpg +0 -0
  114. package/templates/consumer/public/images/posts/ramen.jpg +0 -0
  115. package/templates/consumer/public/images/posts/run.jpg +0 -0
  116. package/templates/consumer/public/images/posts/sunset.jpg +0 -0
  117. package/templates/consumer/tests/seed.test.ts +39 -0
  118. package/templates/creator/.env.example +7 -0
  119. package/templates/creator/README.md +24 -5
  120. package/templates/creator/app/(marketing)/newsletter-signup.tsx +33 -12
  121. package/templates/creator/app/auth-form.tsx +7 -7
  122. package/templates/creator/app/dashboard/dashboard-client.tsx +137 -100
  123. package/templates/creator/app/dashboard/page.tsx +18 -18
  124. package/templates/creator/app/icon.svg +2 -0
  125. package/templates/creator/app/login/page.tsx +14 -14
  126. package/templates/creator/app.ts +4 -4
  127. package/templates/creator/functions/seedDemo.ts +32 -0
  128. package/templates/creator/functions/subscribe.ts +4 -15
  129. package/templates/creator/lib/demo.ts +94 -0
  130. package/templates/creator/lib/site.config.ts +15 -10
  131. package/templates/creator/lib/stats.ts +35 -0
  132. package/templates/creator/public/favicon.ico +0 -0
  133. package/templates/creator/tests/counter.test.ts +92 -0
  134. package/templates/creator/tests/live-count.test.tsx +45 -0
  135. package/templates/crm/README.md +22 -1
  136. package/templates/crm/app/apple-icon.png +0 -0
  137. package/templates/crm/app/companies/companies-view.tsx +32 -14
  138. package/templates/crm/app/companies/page.tsx +2 -1
  139. package/templates/crm/app/contacts/contacts-view.tsx +34 -10
  140. package/templates/crm/app/contacts/page.tsx +2 -1
  141. package/templates/crm/app/deals/[id]/deal-view.tsx +137 -98
  142. package/templates/crm/app/deals/[id]/page.tsx +2 -1
  143. package/templates/crm/app/globals.css +29 -14
  144. package/templates/crm/app/icon.svg +1 -0
  145. package/templates/crm/app/layout.tsx +7 -5
  146. package/templates/crm/app/page.tsx +2 -1
  147. package/templates/crm/app/pipeline-view.tsx +5 -5
  148. package/templates/crm/app/workspace.tsx +29 -17
  149. package/templates/crm/app.ts +5 -5
  150. package/templates/crm/components/activity-timeline.tsx +14 -4
  151. package/templates/crm/components/app-shell.tsx +94 -0
  152. package/templates/crm/components/auth-form.tsx +35 -5
  153. package/templates/crm/components/avatar.tsx +5 -1
  154. package/templates/crm/components/board-skeleton.tsx +5 -5
  155. package/templates/crm/components/brand-mark.tsx +25 -0
  156. package/templates/crm/components/command-palette.tsx +17 -17
  157. package/templates/crm/components/data-table.tsx +97 -36
  158. package/templates/crm/components/deal-card.tsx +23 -27
  159. package/templates/crm/components/filter-tabs.tsx +101 -0
  160. package/templates/crm/components/metrics-bar.tsx +6 -6
  161. package/templates/crm/components/nav.tsx +15 -0
  162. package/templates/crm/components/page-header.tsx +43 -7
  163. package/templates/crm/components/pipeline-board.tsx +16 -10
  164. package/templates/crm/components/require-auth.tsx +24 -26
  165. package/templates/crm/components/sidebar.tsx +54 -49
  166. package/templates/crm/components/stage-badge.tsx +4 -2
  167. package/templates/crm/components/ui/button.tsx +5 -4
  168. package/templates/crm/components/ui/dialog.tsx +1 -1
  169. package/templates/crm/components/ui/input.tsx +1 -1
  170. package/templates/crm/components/ui/select.tsx +1 -1
  171. package/templates/crm/components/ui/textarea.tsx +1 -1
  172. package/templates/crm/functions/seedWorkspace.ts +32 -17
  173. package/templates/crm/lib/brand.ts +9 -0
  174. package/templates/crm/lib/demo-team.ts +51 -0
  175. package/templates/crm/lib/pipeline.ts +37 -0
  176. package/templates/crm/lib/search.ts +4 -3
  177. package/templates/crm/lib/seed.ts +53 -8
  178. package/templates/crm/public/favicon.ico +0 -0
  179. package/templates/crm/tests/components.test.tsx +5 -5
  180. package/templates/crm/tests/demo-team.test.ts +18 -0
  181. package/templates/crm/tests/pipeline.test.ts +59 -3
  182. package/templates/directory/.env.example +7 -0
  183. package/templates/directory/README.md +22 -3
  184. package/templates/directory/app/(marketing)/directory-browse.tsx +30 -12
  185. package/templates/directory/app/(marketing)/page.tsx +6 -4
  186. package/templates/directory/app/auth-form.tsx +4 -4
  187. package/templates/directory/app/dashboard/dashboard-client.tsx +177 -90
  188. package/templates/directory/app/dashboard/page.tsx +16 -14
  189. package/templates/directory/app/icon.svg +4 -0
  190. package/templates/directory/app/login/page.tsx +16 -15
  191. package/templates/directory/functions/seedDemo.ts +43 -0
  192. package/templates/directory/lib/demo.ts +145 -0
  193. package/templates/directory/lib/directory.ts +27 -0
  194. package/templates/directory/lib/site.config.ts +21 -16
  195. package/templates/directory/public/favicon.ico +0 -0
  196. package/templates/directory/tests/directory.test.ts +46 -0
  197. package/templates/expo/consumer/apps/expo/App.tsx +360 -288
  198. package/templates/expo/consumer/apps/expo/app.json +9 -1
  199. package/templates/expo/consumer/apps/expo/package.json +2 -1
  200. package/templates/helpdesk/README.md +21 -0
  201. package/templates/helpdesk/app/apple-icon.png +0 -0
  202. package/templates/helpdesk/app/customers/customers-view.tsx +23 -7
  203. package/templates/helpdesk/app/customers/page.tsx +2 -1
  204. package/templates/helpdesk/app/globals.css +29 -14
  205. package/templates/helpdesk/app/icon.svg +1 -0
  206. package/templates/helpdesk/app/inbox-view.tsx +66 -47
  207. package/templates/helpdesk/app/layout.tsx +7 -5
  208. package/templates/helpdesk/app/page.tsx +4 -7
  209. package/templates/helpdesk/app/tickets/[id]/page.tsx +2 -1
  210. package/templates/helpdesk/app/tickets/[id]/ticket-view.tsx +134 -97
  211. package/templates/helpdesk/app/workspace.tsx +33 -22
  212. package/templates/helpdesk/app.ts +2 -2
  213. package/templates/helpdesk/components/app-shell.tsx +94 -0
  214. package/templates/helpdesk/components/auth-form.tsx +35 -5
  215. package/templates/helpdesk/components/avatar.tsx +5 -1
  216. package/templates/helpdesk/components/brand-mark.tsx +28 -0
  217. package/templates/helpdesk/components/command-palette.tsx +17 -17
  218. package/templates/helpdesk/components/data-table.tsx +97 -36
  219. package/templates/helpdesk/components/filter-tabs.tsx +101 -0
  220. package/templates/helpdesk/components/nav.tsx +14 -0
  221. package/templates/helpdesk/components/page-header.tsx +43 -7
  222. package/templates/helpdesk/components/priority-badge.tsx +49 -6
  223. package/templates/helpdesk/components/require-auth.tsx +24 -26
  224. package/templates/helpdesk/components/sidebar.tsx +54 -48
  225. package/templates/helpdesk/components/sla-indicator.tsx +5 -8
  226. package/templates/helpdesk/components/thread.tsx +7 -7
  227. package/templates/helpdesk/components/ticket-list.tsx +79 -19
  228. package/templates/helpdesk/components/ui/button.tsx +5 -4
  229. package/templates/helpdesk/components/ui/dialog.tsx +1 -1
  230. package/templates/helpdesk/components/ui/input.tsx +1 -1
  231. package/templates/helpdesk/components/ui/select.tsx +1 -1
  232. package/templates/helpdesk/components/ui/textarea.tsx +1 -1
  233. package/templates/helpdesk/functions/seedWorkspace.ts +25 -15
  234. package/templates/helpdesk/lib/brand.ts +9 -0
  235. package/templates/helpdesk/lib/demo-team.ts +51 -0
  236. package/templates/helpdesk/lib/search.ts +4 -3
  237. package/templates/helpdesk/lib/seed.ts +43 -2
  238. package/templates/helpdesk/lib/tickets.ts +68 -20
  239. package/templates/helpdesk/public/favicon.ico +0 -0
  240. package/templates/helpdesk/tests/components.test.tsx +10 -0
  241. package/templates/helpdesk/tests/demo-team.test.ts +18 -0
  242. package/templates/helpdesk/tests/tickets.test.ts +70 -0
  243. package/templates/imessage/.env.example +49 -0
  244. package/templates/imessage/AGENTS.md +153 -0
  245. package/templates/imessage/CLAUDE.md +7 -0
  246. package/templates/imessage/README.md +187 -0
  247. package/templates/imessage/app/console.tsx +382 -0
  248. package/templates/imessage/app/contacts/page.tsx +13 -0
  249. package/templates/imessage/app/error.tsx +16 -0
  250. package/templates/imessage/app/gate.tsx +31 -0
  251. package/templates/imessage/app/globals.css +183 -0
  252. package/templates/imessage/app/layout.tsx +17 -0
  253. package/templates/imessage/app/login/login-form.tsx +115 -0
  254. package/templates/imessage/app/login/page.tsx +30 -0
  255. package/templates/imessage/app/not-found.tsx +13 -0
  256. package/templates/imessage/app/page.tsx +14 -0
  257. package/templates/imessage/app/robots.ts +6 -0
  258. package/templates/imessage/app/setup/page.tsx +13 -0
  259. package/templates/imessage/app/sign-out.tsx +20 -0
  260. package/templates/imessage/app.ts +299 -0
  261. package/templates/imessage/bunfig +4 -0
  262. package/templates/imessage/components/avatar.tsx +46 -0
  263. package/templates/imessage/components/contact-panel.tsx +263 -0
  264. package/templates/imessage/components/contacts-view.tsx +172 -0
  265. package/templates/imessage/components/conversation-list.tsx +129 -0
  266. package/templates/imessage/components/nav-rail.tsx +67 -0
  267. package/templates/imessage/components/setup-view.tsx +408 -0
  268. package/templates/imessage/components/thread.tsx +229 -0
  269. package/templates/imessage/components/ui/badge.tsx +37 -0
  270. package/templates/imessage/components/ui/button.tsx +56 -0
  271. package/templates/imessage/components/ui/card.tsx +90 -0
  272. package/templates/imessage/components/ui/dialog.tsx +110 -0
  273. package/templates/imessage/components/ui/input.tsx +30 -0
  274. package/templates/imessage/components/ui/label.tsx +21 -0
  275. package/templates/imessage/components/ui/select.tsx +39 -0
  276. package/templates/imessage/components/ui/table.tsx +124 -0
  277. package/templates/imessage/components/ui/textarea.tsx +24 -0
  278. package/templates/imessage/components.json +20 -0
  279. package/templates/imessage/functions/answerNow.ts +34 -0
  280. package/templates/imessage/functions/assistant.ts +96 -0
  281. package/templates/imessage/functions/beginTurn.ts +101 -0
  282. package/templates/imessage/functions/cancelReminder.ts +13 -0
  283. package/templates/imessage/functions/claimOutbound.ts +44 -0
  284. package/templates/imessage/functions/clearDemo.ts +18 -0
  285. package/templates/imessage/functions/deleteNote.ts +11 -0
  286. package/templates/imessage/functions/deliverOutbound.ts +55 -0
  287. package/templates/imessage/functions/finishTurn.ts +84 -0
  288. package/templates/imessage/functions/fireReminder.ts +42 -0
  289. package/templates/imessage/functions/ingestInbound.ts +22 -0
  290. package/templates/imessage/functions/ownerAccount.ts +13 -0
  291. package/templates/imessage/functions/processTurn.ts +74 -0
  292. package/templates/imessage/functions/relayApply.ts +102 -0
  293. package/templates/imessage/functions/relaySync.ts +63 -0
  294. package/templates/imessage/functions/releaseOutbound.ts +18 -0
  295. package/templates/imessage/functions/resolveTurn.ts +25 -0
  296. package/templates/imessage/functions/saveContact.ts +27 -0
  297. package/templates/imessage/functions/seedDemo.ts +70 -0
  298. package/templates/imessage/functions/sendOwnerMessage.ts +28 -0
  299. package/templates/imessage/functions/sendblueWebhook.ts +49 -0
  300. package/templates/imessage/functions/setContactStatus.ts +17 -0
  301. package/templates/imessage/functions/settleOutbound.ts +35 -0
  302. package/templates/imessage/functions/setupStatus.ts +33 -0
  303. package/templates/imessage/functions/sweep.ts +58 -0
  304. package/templates/imessage/functions/toolCancelReminder.ts +17 -0
  305. package/templates/imessage/functions/toolForgetNote.ts +15 -0
  306. package/templates/imessage/functions/toolListNotes.ts +16 -0
  307. package/templates/imessage/functions/toolListReminders.ts +17 -0
  308. package/templates/imessage/functions/toolSaveNote.ts +22 -0
  309. package/templates/imessage/functions/toolSetReminder.ts +55 -0
  310. package/templates/imessage/functions/updateSettings.ts +48 -0
  311. package/templates/imessage/gitignore +11 -0
  312. package/templates/imessage/lib/agent-call.ts +251 -0
  313. package/templates/imessage/lib/assistant.ts +74 -0
  314. package/templates/imessage/lib/chunk.ts +135 -0
  315. package/templates/imessage/lib/demo.ts +68 -0
  316. package/templates/imessage/lib/format.ts +170 -0
  317. package/templates/imessage/lib/gate.ts +104 -0
  318. package/templates/imessage/lib/handles.ts +67 -0
  319. package/templates/imessage/lib/provider.ts +38 -0
  320. package/templates/imessage/lib/relay-protocol.ts +95 -0
  321. package/templates/imessage/lib/reminders.ts +41 -0
  322. package/templates/imessage/lib/secrets.ts +59 -0
  323. package/templates/imessage/lib/sendblue.ts +173 -0
  324. package/templates/imessage/lib/store.ts +407 -0
  325. package/templates/imessage/lib/transport.ts +135 -0
  326. package/templates/imessage/lib/utils.ts +10 -0
  327. package/templates/imessage/package.json +40 -0
  328. package/templates/imessage/relay/.env.example +14 -0
  329. package/templates/imessage/relay/applescript.ts +70 -0
  330. package/templates/imessage/relay/chatdb.ts +165 -0
  331. package/templates/imessage/relay/client.ts +41 -0
  332. package/templates/imessage/relay/main.ts +137 -0
  333. package/templates/imessage/relay/state.ts +35 -0
  334. package/templates/imessage/tests/agent-call.test.ts +112 -0
  335. package/templates/imessage/tests/applescript.test.ts +88 -0
  336. package/templates/imessage/tests/chatdb.test.ts +139 -0
  337. package/templates/imessage/tests/chunk.test.ts +61 -0
  338. package/templates/imessage/tests/fake-ctx.ts +88 -0
  339. package/templates/imessage/tests/ingest.test.ts +141 -0
  340. package/templates/imessage/tests/lib.test.ts +99 -0
  341. package/templates/imessage/tests/review-fixes.test.ts +138 -0
  342. package/templates/imessage/tests/secrets.test.ts +36 -0
  343. package/templates/imessage/tests/sendblue.test.ts +143 -0
  344. package/templates/imessage/tests/setup.ts +5 -0
  345. package/templates/imessage/tsconfig.json +18 -0
  346. package/templates/inventory/README.md +21 -0
  347. package/templates/inventory/app/apple-icon.png +0 -0
  348. package/templates/inventory/app/globals.css +29 -14
  349. package/templates/inventory/app/icon.svg +1 -0
  350. package/templates/inventory/app/layout.tsx +7 -5
  351. package/templates/inventory/app/movements/movements-view.tsx +32 -10
  352. package/templates/inventory/app/movements/page.tsx +2 -1
  353. package/templates/inventory/app/page.tsx +4 -6
  354. package/templates/inventory/app/products/[id]/page.tsx +2 -1
  355. package/templates/inventory/app/products/[id]/product-view.tsx +55 -25
  356. package/templates/inventory/app/products-view.tsx +74 -33
  357. package/templates/inventory/app/workspace.tsx +34 -16
  358. package/templates/inventory/app.ts +10 -2
  359. package/templates/inventory/components/app-shell.tsx +94 -0
  360. package/templates/inventory/components/auth-form.tsx +35 -5
  361. package/templates/inventory/components/avatar.tsx +5 -1
  362. package/templates/inventory/components/brand-mark.tsx +25 -0
  363. package/templates/inventory/components/category-tile.tsx +49 -0
  364. package/templates/inventory/components/command-palette.tsx +17 -17
  365. package/templates/inventory/components/data-table.tsx +97 -36
  366. package/templates/inventory/components/filter-tabs.tsx +101 -0
  367. package/templates/inventory/components/nav.tsx +14 -0
  368. package/templates/inventory/components/page-header.tsx +43 -7
  369. package/templates/inventory/components/require-auth.tsx +24 -26
  370. package/templates/inventory/components/sidebar.tsx +54 -48
  371. package/templates/inventory/components/stock-level.tsx +30 -14
  372. package/templates/inventory/components/summary-bar.tsx +8 -15
  373. package/templates/inventory/components/ui/button.tsx +5 -4
  374. package/templates/inventory/components/ui/dialog.tsx +1 -1
  375. package/templates/inventory/components/ui/input.tsx +1 -1
  376. package/templates/inventory/components/ui/select.tsx +1 -1
  377. package/templates/inventory/components/ui/textarea.tsx +1 -1
  378. package/templates/inventory/functions/seedWorkspace.ts +18 -8
  379. package/templates/inventory/lib/brand.ts +9 -0
  380. package/templates/inventory/lib/demo-team.ts +51 -0
  381. package/templates/inventory/lib/search.ts +4 -3
  382. package/templates/inventory/lib/seed.ts +29 -2
  383. package/templates/inventory/lib/stock.ts +32 -0
  384. package/templates/inventory/public/favicon.ico +0 -0
  385. package/templates/inventory/tests/demo-team.test.ts +18 -0
  386. package/templates/inventory/tests/stock.test.ts +29 -0
  387. package/templates/invoices/README.md +16 -0
  388. package/templates/invoices/app/apple-icon.png +0 -0
  389. package/templates/invoices/app/clients/clients-view.tsx +18 -6
  390. package/templates/invoices/app/clients/page.tsx +2 -1
  391. package/templates/invoices/app/globals.css +29 -14
  392. package/templates/invoices/app/icon.svg +1 -0
  393. package/templates/invoices/app/invoices/[id]/invoice-view.tsx +72 -48
  394. package/templates/invoices/app/invoices/[id]/page.tsx +2 -1
  395. package/templates/invoices/app/invoices-view.tsx +81 -32
  396. package/templates/invoices/app/layout.tsx +7 -5
  397. package/templates/invoices/app/page.tsx +4 -6
  398. package/templates/invoices/app/workspace.tsx +26 -16
  399. package/templates/invoices/app.ts +2 -2
  400. package/templates/invoices/components/app-shell.tsx +94 -0
  401. package/templates/invoices/components/auth-form.tsx +35 -5
  402. package/templates/invoices/components/avatar.tsx +5 -1
  403. package/templates/invoices/components/brand-mark.tsx +26 -0
  404. package/templates/invoices/components/command-palette.tsx +17 -17
  405. package/templates/invoices/components/data-table.tsx +97 -36
  406. package/templates/invoices/components/filter-tabs.tsx +101 -0
  407. package/templates/invoices/components/line-items.tsx +19 -14
  408. package/templates/invoices/components/nav.tsx +14 -0
  409. package/templates/invoices/components/page-header.tsx +43 -7
  410. package/templates/invoices/components/require-auth.tsx +24 -26
  411. package/templates/invoices/components/sidebar.tsx +54 -48
  412. package/templates/invoices/components/status-badge.tsx +7 -7
  413. package/templates/invoices/components/summary-bar.tsx +5 -5
  414. package/templates/invoices/components/ui/button.tsx +5 -4
  415. package/templates/invoices/components/ui/dialog.tsx +1 -1
  416. package/templates/invoices/components/ui/input.tsx +1 -1
  417. package/templates/invoices/components/ui/select.tsx +1 -1
  418. package/templates/invoices/components/ui/textarea.tsx +1 -1
  419. package/templates/invoices/functions/recordPayment.ts +3 -0
  420. package/templates/invoices/lib/billing.ts +40 -0
  421. package/templates/invoices/lib/brand.ts +9 -0
  422. package/templates/invoices/lib/search.ts +4 -3
  423. package/templates/invoices/public/favicon.ico +0 -0
  424. package/templates/invoices/tests/billing.test.ts +27 -0
  425. package/templates/ios/consumer/apps/ios/Package.swift +4 -3
  426. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ActivityView.swift +107 -0
  427. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AppModel.swift +171 -0
  428. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AuthView.swift +182 -0
  429. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/CommentsView.swift +136 -0
  430. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ComposeView.swift +152 -0
  431. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ExploreView.swift +117 -0
  432. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/FeedView.swift +175 -159
  433. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/MainTabView.swift +54 -0
  434. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Media.swift +271 -0
  435. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Models.swift +101 -4
  436. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/PostCard.swift +245 -0
  437. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileEditorView.swift +208 -0
  438. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileView.swift +183 -0
  439. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/RootView.swift +25 -31
  440. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/SocialStore.swift +285 -0
  441. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/StoryViewer.swift +155 -0
  442. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Theme.swift +82 -0
  443. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +4 -47
  444. package/templates/ios/consumer/apps/ios/project.yml +13 -9
  445. package/templates/local-service/.env.example +7 -0
  446. package/templates/local-service/README.md +26 -2
  447. package/templates/local-service/app/(marketing)/booking-widget.tsx +48 -9
  448. package/templates/local-service/app/(marketing)/page.tsx +66 -42
  449. package/templates/local-service/app/auth-form.tsx +8 -8
  450. package/templates/local-service/app/dashboard/dashboard-client.tsx +119 -92
  451. package/templates/local-service/app/dashboard/page.tsx +14 -14
  452. package/templates/local-service/app/icon.svg +2 -0
  453. package/templates/local-service/app/login/page.tsx +15 -15
  454. package/templates/local-service/functions/seedDemo.ts +49 -0
  455. package/templates/local-service/lib/demo.ts +135 -0
  456. package/templates/local-service/lib/hours.ts +44 -0
  457. package/templates/local-service/lib/site.config.ts +8 -3
  458. package/templates/local-service/lib/slots.ts +3 -0
  459. package/templates/local-service/public/favicon.ico +0 -0
  460. package/templates/local-service/tests/booking.test.ts +104 -0
  461. package/templates/marketplace/README.md +23 -15
  462. package/templates/marketplace/app/globals.css +172 -114
  463. package/templates/marketplace/app/layout.tsx +75 -30
  464. package/templates/marketplace/app/listing/[id]/page.tsx +210 -123
  465. package/templates/marketplace/app/me/page.tsx +4 -4
  466. package/templates/marketplace/app/page.tsx +221 -298
  467. package/templates/marketplace/app/sell/page.tsx +8 -11
  468. package/templates/marketplace/app.ts +32 -7
  469. package/templates/marketplace/client/ActivityFeed.tsx +311 -0
  470. package/templates/marketplace/client/AuthNav.tsx +24 -16
  471. package/templates/marketplace/client/ListingCard.tsx +118 -0
  472. package/templates/marketplace/client/LiveGrid.tsx +92 -0
  473. package/templates/marketplace/client/LiveListingStatus.tsx +32 -24
  474. package/templates/marketplace/client/LoginCard.tsx +114 -136
  475. package/templates/marketplace/client/MarketProvider.tsx +3 -5
  476. package/templates/marketplace/client/MyMarket.tsx +371 -185
  477. package/templates/marketplace/client/OfferPanel.tsx +421 -287
  478. package/templates/marketplace/client/SellForm.tsx +271 -192
  479. package/templates/marketplace/client/ThemeToggle.tsx +1 -0
  480. package/templates/marketplace/client/WatchButton.tsx +16 -16
  481. package/templates/marketplace/client/market.ts +61 -14
  482. package/templates/marketplace/functions/seedMarket.ts +143 -36
  483. package/templates/marketplace/functions/seedOffers.ts +101 -0
  484. package/templates/marketplace/lib/catalog.ts +72 -1
  485. package/templates/marketplace/public/images/listings/ceramic-lamp-640.webp +0 -0
  486. package/templates/marketplace/public/images/listings/ceramic-lamp.webp +0 -0
  487. package/templates/marketplace/public/images/listings/duffel-640.webp +0 -0
  488. package/templates/marketplace/public/images/listings/duffel.webp +0 -0
  489. package/templates/marketplace/public/images/listings/film-camera-640.webp +0 -0
  490. package/templates/marketplace/public/images/listings/film-camera.webp +0 -0
  491. package/templates/marketplace/public/images/listings/lounge-chair-640.webp +0 -0
  492. package/templates/marketplace/public/images/listings/lounge-chair.webp +0 -0
  493. package/templates/marketplace/public/images/listings/road-bike-640.webp +0 -0
  494. package/templates/marketplace/public/images/listings/road-bike.webp +0 -0
  495. package/templates/marketplace/public/images/listings/slipper-chair-640.webp +0 -0
  496. package/templates/marketplace/public/images/listings/slipper-chair.webp +0 -0
  497. package/templates/marketplace/public/images/listings/turntable-640.webp +0 -0
  498. package/templates/marketplace/public/images/listings/turntable.webp +0 -0
  499. package/templates/marketplace/public/images/listings/wardrobe-640.webp +0 -0
  500. package/templates/marketplace/public/images/listings/wardrobe.webp +0 -0
  501. package/templates/marketplace/public/images/listings/watch-640.webp +0 -0
  502. package/templates/marketplace/public/images/listings/watch.webp +0 -0
  503. package/templates/marketplace/public/images/listings/weekender-bag-640.webp +0 -0
  504. package/templates/marketplace/public/images/listings/weekender-bag.webp +0 -0
  505. package/templates/marketplace/tests/example.test.ts +64 -2
  506. package/templates/marketplace/tests/listing-card.test.tsx +61 -0
  507. package/templates/projects/README.md +24 -3
  508. package/templates/projects/app/apple-icon.png +0 -0
  509. package/templates/projects/app/clients/clients-view.tsx +20 -6
  510. package/templates/projects/app/clients/page.tsx +2 -1
  511. package/templates/projects/app/globals.css +29 -14
  512. package/templates/projects/app/icon.svg +1 -0
  513. package/templates/projects/app/layout.tsx +7 -5
  514. package/templates/projects/app/page.tsx +3 -5
  515. package/templates/projects/app/projects/[id]/page.tsx +2 -1
  516. package/templates/projects/app/projects/[id]/project-view.tsx +45 -30
  517. package/templates/projects/app/projects-view.tsx +105 -52
  518. package/templates/projects/app/workspace.tsx +41 -20
  519. package/templates/projects/app.ts +2 -2
  520. package/templates/projects/components/app-shell.tsx +94 -0
  521. package/templates/projects/components/auth-form.tsx +35 -5
  522. package/templates/projects/components/avatar.tsx +5 -1
  523. package/templates/projects/components/board-skeleton.tsx +3 -3
  524. package/templates/projects/components/brand-mark.tsx +25 -0
  525. package/templates/projects/components/command-palette.tsx +17 -17
  526. package/templates/projects/components/data-table.tsx +97 -36
  527. package/templates/projects/components/filter-tabs.tsx +101 -0
  528. package/templates/projects/components/meter.tsx +48 -0
  529. package/templates/projects/components/nav.tsx +14 -0
  530. package/templates/projects/components/page-header.tsx +43 -7
  531. package/templates/projects/components/require-auth.tsx +24 -26
  532. package/templates/projects/components/sidebar.tsx +54 -48
  533. package/templates/projects/components/summary-bar.tsx +49 -0
  534. package/templates/projects/components/task-board.tsx +10 -9
  535. package/templates/projects/components/time-dialog.tsx +29 -5
  536. package/templates/projects/components/ui/button.tsx +5 -4
  537. package/templates/projects/components/ui/dialog.tsx +1 -1
  538. package/templates/projects/components/ui/input.tsx +1 -1
  539. package/templates/projects/components/ui/select.tsx +1 -1
  540. package/templates/projects/components/ui/textarea.tsx +1 -1
  541. package/templates/projects/functions/seedWorkspace.ts +26 -12
  542. package/templates/projects/lib/brand.ts +9 -0
  543. package/templates/projects/lib/demo-team.ts +51 -0
  544. package/templates/projects/lib/search.ts +4 -3
  545. package/templates/projects/lib/seed.ts +35 -1
  546. package/templates/projects/lib/work.ts +63 -0
  547. package/templates/projects/public/favicon.ico +0 -0
  548. package/templates/projects/tests/demo-team.test.ts +18 -0
  549. package/templates/projects/tests/work.test.ts +65 -0
  550. package/templates/restaurant/.env.example +7 -0
  551. package/templates/restaurant/README.md +29 -3
  552. package/templates/restaurant/app/(marketing)/layout.tsx +4 -2
  553. package/templates/restaurant/app/(marketing)/page.tsx +47 -23
  554. package/templates/restaurant/app/(marketing)/reservation-widget.tsx +37 -7
  555. package/templates/restaurant/app/auth-form.tsx +8 -8
  556. package/templates/restaurant/app/dashboard/dashboard-client.tsx +241 -109
  557. package/templates/restaurant/app/dashboard/page.tsx +14 -14
  558. package/templates/restaurant/app/icon.svg +4 -0
  559. package/templates/restaurant/app/login/page.tsx +15 -15
  560. package/templates/restaurant/functions/seedDemo.ts +44 -0
  561. package/templates/restaurant/lib/demo.ts +144 -0
  562. package/templates/restaurant/lib/hours.ts +45 -0
  563. package/templates/restaurant/lib/site.config.ts +3 -3
  564. package/templates/restaurant/public/favicon.ico +0 -0
  565. package/templates/restaurant/tests/reservations.test.ts +67 -0
  566. package/templates/saas/README.md +54 -35
  567. package/templates/saas/app/(auth)/auth-form.tsx +25 -23
  568. package/templates/saas/app/(auth)/forgot-password/page.tsx +1 -1
  569. package/templates/saas/app/(auth)/layout.tsx +27 -33
  570. package/templates/saas/app/(auth)/login/page.tsx +1 -1
  571. package/templates/saas/app/(auth)/reset-password/page.tsx +1 -1
  572. package/templates/saas/app/(auth)/signup/page.tsx +1 -1
  573. package/templates/saas/app/(marketing)/layout.tsx +127 -253
  574. package/templates/saas/app/(marketing)/page.tsx +133 -372
  575. package/templates/saas/app/(marketing)/pricing/page.tsx +21 -19
  576. package/templates/saas/app/(marketing)/products/[slug]/page.tsx +40 -95
  577. package/templates/saas/app/dashboard/board-client.tsx +416 -0
  578. package/templates/saas/app/dashboard/dashboard-client.tsx +30 -615
  579. package/templates/saas/app/dashboard/layout.tsx +27 -13
  580. package/templates/saas/app/dashboard/overview-client.tsx +332 -0
  581. package/templates/saas/app/dashboard/page.tsx +29 -35
  582. package/templates/saas/app/dashboard/projects/[id]/page.tsx +29 -0
  583. package/templates/saas/app/dashboard/projects/page.tsx +11 -7
  584. package/templates/saas/app/dashboard/projects-client.tsx +398 -0
  585. package/templates/saas/app/globals.css +5 -4
  586. package/templates/saas/app/icon.svg +1 -0
  587. package/templates/saas/app/onboarding/onboarding-client.tsx +93 -26
  588. package/templates/saas/app/onboarding/page.tsx +6 -2
  589. package/templates/saas/app.ts +68 -13
  590. package/templates/saas/components/dashboard-shell.tsx +212 -132
  591. package/templates/saas/components/marketing.tsx +137 -274
  592. package/templates/saas/components/pricing-table.tsx +12 -11
  593. package/templates/saas/components/setup-checklist.tsx +31 -29
  594. package/templates/saas/components/task-ui.tsx +199 -0
  595. package/templates/saas/functions/createProject.ts +7 -13
  596. package/templates/saas/functions/createTask.ts +67 -0
  597. package/templates/saas/functions/deleteProject.ts +17 -0
  598. package/templates/saas/functions/deleteTask.ts +14 -0
  599. package/templates/saas/functions/seedWorkspace.ts +46 -0
  600. package/templates/saas/functions/setProjectStatus.ts +41 -0
  601. package/templates/saas/functions/updateProfile.ts +13 -0
  602. package/templates/saas/functions/updateProject.ts +4 -5
  603. package/templates/saas/functions/updateTask.ts +88 -0
  604. package/templates/saas/lib/members.ts +53 -0
  605. package/templates/saas/lib/names.ts +53 -0
  606. package/templates/saas/lib/plans.ts +11 -0
  607. package/templates/saas/lib/sample-workspace.ts +117 -0
  608. package/templates/saas/lib/site.config.ts +200 -463
  609. package/templates/saas/lib/tasks.ts +101 -0
  610. package/templates/saas/lib/use-live.ts +25 -0
  611. package/templates/saas/public/favicon.ico +0 -0
  612. package/templates/saas/public/screenshots/board.png +0 -0
  613. package/templates/saas/public/screenshots/members.png +0 -0
  614. package/templates/saas/public/screenshots/overview.png +0 -0
  615. package/templates/saas/public/screenshots/projects.png +0 -0
  616. package/templates/saas/scripts/capture-screenshots.mjs +156 -0
  617. package/templates/saas/tests/names.test.ts +35 -0
  618. package/templates/saas/tests/plans.test.ts +12 -0
  619. package/templates/saas/tests/sample-workspace.test.ts +46 -0
  620. package/templates/saas/tests/tasks.test.ts +81 -0
  621. package/templates/shop/README.md +25 -13
  622. package/templates/shop/app/(marketing)/layout.tsx +94 -67
  623. package/templates/shop/app/(marketing)/not-found.tsx +13 -12
  624. package/templates/shop/app/(marketing)/page.tsx +120 -59
  625. package/templates/shop/app/(marketing)/shop-client.tsx +364 -237
  626. package/templates/shop/app/(marketing)/success/page.tsx +75 -22
  627. package/templates/shop/app/globals.css +94 -1
  628. package/templates/shop/app/layout.tsx +4 -4
  629. package/templates/shop/app/login/page.tsx +4 -4
  630. package/templates/shop/app.ts +18 -6
  631. package/templates/shop/components/cart-button.tsx +33 -0
  632. package/templates/shop/components/marketing.tsx +17 -87
  633. package/templates/shop/components/shelf-count.tsx +36 -0
  634. package/templates/shop/functions/checkout.ts +17 -1
  635. package/templates/shop/lib/cart-store.ts +57 -0
  636. package/templates/shop/lib/shelf-store.ts +35 -0
  637. package/templates/shop/lib/shop.ts +27 -0
  638. package/templates/shop/lib/site.config.ts +108 -98
  639. package/templates/shop/public/images/hero.jpg +0 -0
  640. package/templates/shop/public/images/products/cedar-smoke.jpg +0 -0
  641. package/templates/shop/public/images/products/fig-leaf.jpg +0 -0
  642. package/templates/shop/public/images/products/linen.jpg +0 -0
  643. package/templates/shop/public/images/products/sea-salt.jpg +0 -0
  644. package/templates/shop/public/images/products/tobacco-oak.jpg +0 -0
  645. package/templates/shop/tests/shop.test.ts +64 -0
  646. package/templates/waitlist/.env.example +7 -0
  647. package/templates/waitlist/README.md +29 -13
  648. package/templates/waitlist/app/(marketing)/page.tsx +9 -7
  649. package/templates/waitlist/app/(marketing)/waitlist-hero.tsx +64 -31
  650. package/templates/waitlist/app/auth-form.tsx +8 -8
  651. package/templates/waitlist/app/dashboard/dashboard-client.tsx +132 -95
  652. package/templates/waitlist/app/dashboard/page.tsx +17 -16
  653. package/templates/waitlist/app/icon.svg +2 -0
  654. package/templates/waitlist/app/login/page.tsx +14 -14
  655. package/templates/waitlist/app.ts +6 -6
  656. package/templates/waitlist/functions/joinWaitlist.ts +6 -18
  657. package/templates/waitlist/functions/seedDemo.ts +32 -0
  658. package/templates/waitlist/lib/demo.ts +94 -0
  659. package/templates/waitlist/lib/launch.ts +19 -0
  660. package/templates/waitlist/lib/site.config.ts +14 -9
  661. package/templates/waitlist/lib/stats.ts +35 -0
  662. package/templates/waitlist/public/favicon.ico +0 -0
  663. package/templates/waitlist/tests/counter.test.ts +107 -0
  664. package/templates/waitlist/tests/live-count.test.tsx +48 -0
  665. package/templates/ai-chat/tests/example.test.ts +0 -12
  666. package/templates/backend/consumer/apps/api/functions/feed.ts +0 -57
  667. package/templates/backend/consumer/apps/api/functions/profilePosts.ts +0 -17
  668. package/templates/backend/consumer/apps/api/functions/toggleLike.ts +0 -48
  669. package/templates/backend/consumer/apps/api/schema.ts +0 -143
  670. package/templates/chat/functions/_keep.ts +0 -13
  671. package/templates/chat/tests/example.test.ts +0 -12
  672. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileSetupView.swift +0 -68
  673. package/templates/marketplace/client/LiveTicker.tsx +0 -116
  674. package/templates/marketplace/client/ScrollToListingsLink.tsx +0 -29
  675. package/templates/marketplace/public/images/marketplace-hero.webp +0 -0
@@ -65,6 +65,39 @@ export interface TimeEntry {
65
65
  spentOn?: string | null;
66
66
  }
67
67
 
68
+ export interface ProjectTab {
69
+ id: string;
70
+ label: string;
71
+ /** Project status the tab shows; undefined shows every project. */
72
+ status?: string;
73
+ }
74
+
75
+ /** The project list's tabs, left to right. */
76
+ export const PROJECT_TABS: ProjectTab[] = [
77
+ { id: "active", label: "Active", status: "active" },
78
+ { id: "paused", label: "Paused", status: "paused" },
79
+ { id: "complete", label: "Complete", status: "complete" },
80
+ { id: "all", label: "All" },
81
+ ];
82
+
83
+ /** The tab the list opens on. The sidebar's Projects count is this tab's count. */
84
+ export const DEFAULT_PROJECT_TAB = "active";
85
+
86
+ export function inProjectTab(tabId: string, project: { status: string }): boolean {
87
+ const tab = PROJECT_TABS.find((t) => t.id === tabId);
88
+ return !tab?.status || project.status === tab.status;
89
+ }
90
+
91
+ /**
92
+ * How many projects each tab lists. The sidebar badge, the page header, and
93
+ * the tab counts all read this, so they always agree.
94
+ */
95
+ export function projectTabCounts(projects: Array<{ status: string }>): Record<string, number> {
96
+ return Object.fromEntries(
97
+ PROJECT_TABS.map((tab) => [tab.id, projects.filter((p) => inProjectTab(tab.id, p)).length]),
98
+ );
99
+ }
100
+
68
101
  export function taskStatusById(id: string): Status | undefined {
69
102
  return TASK_STATUSES.find((s) => s.id === id);
70
103
  }
@@ -231,3 +264,33 @@ export function parseDuration(input: string): number | null {
231
264
 
232
265
  return null;
233
266
  }
267
+
268
+ export interface Portfolio {
269
+ /** Projects with status "active". */
270
+ activeCount: number;
271
+ /** Minutes logged against active projects. */
272
+ loggedMinutes: number;
273
+ /** What that time is worth at each project's rate. */
274
+ billableCents: number;
275
+ /** Active projects past their budget. */
276
+ overBudgetCount: number;
277
+ }
278
+
279
+ /** The numbers above the project list, over active projects only. */
280
+ export function portfolio(projects: Project[], entries: TimeEntry[]): Portfolio {
281
+ const result: Portfolio = {
282
+ activeCount: 0,
283
+ loggedMinutes: 0,
284
+ billableCents: 0,
285
+ overBudgetCount: 0,
286
+ };
287
+ for (const project of projects) {
288
+ if (project.status !== "active") continue;
289
+ const minutes = minutesForProject(project.id, entries);
290
+ result.activeCount += 1;
291
+ result.loggedMinutes += minutes;
292
+ result.billableCents += billableCents(minutes, project.hourlyRateCents);
293
+ if (budgetState(project.budgetMinutes, minutes) === "over") result.overBudgetCount += 1;
294
+ }
295
+ return result;
296
+ }
@@ -0,0 +1,18 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { demoEmail, isDemoRow } from "../lib/demo-team";
3
+
4
+ describe("demo teammates", () => {
5
+ test("the first address is the person's own, then numbered aliases", () => {
6
+ expect(demoEmail("maya.castillo@demo.invalid", 0)).toBe("maya.castillo@demo.invalid");
7
+ expect(demoEmail("maya.castillo@demo.invalid", 1)).toBe("maya.castillo+2@demo.invalid");
8
+ expect(demoEmail("maya.castillo@demo.invalid", 2)).toBe("maya.castillo+3@demo.invalid");
9
+ });
10
+
11
+ test("a registered account is never reused as a demo teammate", () => {
12
+ // Someone who registered a demo address first must not be handed the
13
+ // seeded records.
14
+ expect(isDemoRow({ email: "x@demo.invalid", passwordHash: "argon2id$..." })).toBe(false);
15
+ expect(isDemoRow({ email: "x@demo.invalid", emailVerified: "2026-07-01T00:00:00Z" })).toBe(false);
16
+ expect(isDemoRow({ email: "x@demo.invalid", displayName: "Maya Castillo" })).toBe(true);
17
+ });
18
+ });
@@ -1,10 +1,13 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import {
3
+ DEFAULT_PROJECT_TAB,
4
+ PROJECT_TABS,
3
5
  TASK_STATUSES,
4
6
  billableCents,
5
7
  budgetState,
6
8
  duration,
7
9
  groupByStatus,
10
+ inProjectTab,
8
11
  isOpen,
9
12
  isValidProjectStatus,
10
13
  isValidTaskStatus,
@@ -13,10 +16,13 @@ import {
13
16
  minutesForTask,
14
17
  money,
15
18
  parseDuration,
19
+ portfolio,
16
20
  progress,
21
+ projectTabCounts,
17
22
  type Task,
18
23
  type TimeEntry,
19
24
  } from "../lib/work";
25
+ import { SEED_TEAM, shapeSeed } from "../lib/seed";
20
26
 
21
27
  const task = (over: Partial<Task> = {}): Task => ({
22
28
  id: "t1",
@@ -236,3 +242,62 @@ describe("money", () => {
236
242
  expect(money(null)).toBe("$0.00");
237
243
  });
238
244
  });
245
+
246
+ describe("project tabs", () => {
247
+ const projects = [
248
+ { id: "a", status: "active" },
249
+ { id: "b", status: "active" },
250
+ { id: "c", status: "paused" },
251
+ { id: "d", status: "complete" },
252
+ ];
253
+
254
+ test("the sidebar count is the default tab's row count", () => {
255
+ // The sidebar once counted active projects (2) while the list showed all
256
+ // of them (4). Both now read projectTabCounts for the default tab.
257
+ const counts = projectTabCounts(projects);
258
+ expect(counts[DEFAULT_PROJECT_TAB]).toBe(
259
+ projects.filter((p) => inProjectTab(DEFAULT_PROJECT_TAB, p)).length,
260
+ );
261
+ expect(counts[DEFAULT_PROJECT_TAB]).toBe(2);
262
+ });
263
+
264
+ test("each tab counts what it lists", () => {
265
+ const counts = projectTabCounts(projects);
266
+ for (const tab of PROJECT_TABS) {
267
+ expect(counts[tab.id]).toBe(projects.filter((p) => inProjectTab(tab.id, p)).length);
268
+ }
269
+ expect(counts).toEqual({ active: 2, paused: 1, complete: 1, all: 4 });
270
+ });
271
+ });
272
+
273
+ describe("seed", () => {
274
+ test("tasks are spread across the whole team, with some left unassigned", () => {
275
+ const tasks = shapeSeed(Date.parse("2026-07-27T12:00:00Z")).tasks;
276
+ const owners = new Set(tasks.map((t) => t.assignee));
277
+ for (let i = 0; i <= SEED_TEAM.length; i += 1) expect(owners.has(i)).toBe(true);
278
+ expect(owners.has(null)).toBe(true);
279
+ // Only work that hasn't started is unassigned.
280
+ for (const t of tasks) if (t.assignee === null) expect(t.row.status).toBe("todo");
281
+ });
282
+ });
283
+
284
+ describe("portfolio", () => {
285
+ test("totals active projects only and counts the ones over budget", () => {
286
+ const projects = [
287
+ { id: "a", name: "A", status: "active", budgetMinutes: 60, hourlyRateCents: 10_000 },
288
+ { id: "b", name: "B", status: "active", budgetMinutes: 600, hourlyRateCents: 0 },
289
+ { id: "c", name: "C", status: "complete", budgetMinutes: 60, hourlyRateCents: 10_000 },
290
+ ];
291
+ const entries = [
292
+ { id: "1", taskId: "t", projectId: "a", minutes: 90 },
293
+ { id: "2", taskId: "t", projectId: "b", minutes: 30 },
294
+ { id: "3", taskId: "t", projectId: "c", minutes: 500 },
295
+ ];
296
+ expect(portfolio(projects, entries)).toEqual({
297
+ activeCount: 2,
298
+ loggedMinutes: 120,
299
+ billableCents: 15_000,
300
+ overBudgetCount: 1,
301
+ });
302
+ });
303
+ });
@@ -10,3 +10,10 @@ PYLON_OWNER_EMAIL=you@yourrestaurant.com
10
10
  # ── Site URL (optional) ──────────────────────────────────────────────────────
11
11
  # Used by robots.txt + sitemap.xml. Point it at your real domain in production.
12
12
  # SITE_URL=https://yourrestaurant.com
13
+
14
+ # ── Demo data ────────────────────────────────────────────────────────────────
15
+ # `pylon dev` fills an empty reservation book with fictional tables. Seeding
16
+ # runs only under `pylon dev` (it checks PYLON_DEV_MODE and
17
+ # PYLON_DEV_WATCH_DIR), so `pylon start`, Docker, and Pylon Cloud deploys never
18
+ # seed. Set 0 to start empty in development. Never set 1 on a deploy.
19
+ # PYLON_DEMO_DATA=0
@@ -17,7 +17,7 @@ time in one tab and watch it change to "Full" in another without refreshing.
17
17
 
18
18
  ## How the realtime works
19
19
 
20
- - `app/reservation-widget.tsx` subscribes to the public, PII-free
20
+ - `app/(marketing)/reservation-widget.tsx` subscribes to the public, PII-free
21
21
  `ReservationSlot` markers with `db.useQuery` and COUNTS them per seating, so
22
22
  "tables left" ticks down live across every tab.
23
23
  - `functions/createReservation.ts` is a public **mutation** that re-checks the
@@ -43,25 +43,51 @@ details, notes — with confirm/cancel, plus live covers + counts.
43
43
  Set `PYLON_OWNER_EMAIL` in `.env` (see `.env.example`) to the email you'll sign
44
44
  in with, then create that account at `/login`.
45
45
 
46
+ ## Demo data
47
+
48
+ In `pylon dev`, an empty reservation book gets fictional tables from the past
49
+ week and the next two weeks (`functions/seedDemo.ts`, `lib/demo.ts`). Each one
50
+ writes a `Reservation` and its `ReservationSlot` marker, like a real
51
+ reservation, so the public calendar shows fewer tables left and the dashboard
52
+ lists the same guests. Seeding runs only when `PYLON_DEMO_DATA` is on, or when
53
+ it is unset and the app runs under `pylon dev`, and only while the `Reservation` table
54
+ is empty. `pylon start`, Docker, and Pylon Cloud deploys do not seed, because only `pylon dev` sets `PYLON_DEV_WATCH_DIR`. Set
55
+ `PYLON_DEMO_DATA=0` to start empty in development. Delete both files once you
56
+ take real reservations.
57
+
58
+ ## Map or hours card
59
+
60
+ Set `location.mapEmbedUrl` in `lib/site.config.ts` to a Google Maps embed URL to
61
+ show a map beside the address. Without one, the site shows the week's service
62
+ hours (from `reservations.hours`, the same hours the calendar uses) and a
63
+ directions link. The dashboard reminds you while the URL is empty.
64
+
46
65
  ## Rebrand + reconfigure it
47
66
 
48
67
  Brand, colors, the menu, seating hours, tables per seating, lead time, reviews,
49
68
  location, and FAQ content live in **`lib/site.config.ts`**. Editing or
50
69
  generating that file updates both the site and reservation engine.
51
70
 
71
+ The tab icon is `app/icon.svg` (with `public/favicon.ico` for browsers that
72
+ request `/favicon.ico`). Both carry this brand's letter and colors; replace
73
+ them when you rename the brand.
74
+
52
75
  ## Layout
53
76
 
54
77
  ```
55
78
  app.ts Reservation + ReservationSlot + User + policies
56
79
  lib/site.config.ts ALL copy + brand + menu + seating config
57
80
  lib/slots.ts pure seating math, shared by picker + server
81
+ lib/hours.ts weekly hours rows + directions link
82
+ lib/demo.ts demo reservations + the dev-only gate
83
+ functions/seedDemo.ts dev-only demo reservations (see Demo data)
58
84
  lib/reservation.ts shared reservation-row types
59
85
  lib/owner.ts owner-email gate (PYLON_OWNER_EMAIL)
60
86
  functions/createReservation.ts public mutation: capacity re-check + reserve
61
87
  functions/reservationsForOwner.ts owner-only query: reservations + guest PII
62
88
  functions/{confirm,cancel}Reservation.ts owner-only mutations
63
- app/page.tsx landing (hero + menu + reviews + location)
64
- app/reservation-widget.tsx client island: live table picker + form
89
+ app/(marketing)/page.tsx landing (hero + menu + reviews + location)
90
+ app/(marketing)/reservation-widget.tsx client island: live table picker + form
65
91
  app/dashboard/ owner dashboard (auth-gated, live)
66
92
  ```
67
93
 
@@ -22,7 +22,9 @@ export default function MarketingLayout({ children, auth }: LayoutProps) {
22
22
  const { brand } = siteConfig;
23
23
 
24
24
  return (
25
- <>
25
+ // The ink background sits behind the translucent nav, so the nav reads as
26
+ // dark glass over the page instead of grey over the white body.
27
+ <div className="flex min-h-screen flex-col bg-[var(--ink)]">
26
28
  {/* The nav sits over the hero photo: dark, translucent, cream type. */}
27
29
  <header className="sticky top-0 z-30 border-b border-white/10 bg-[var(--ink)]/80 text-[var(--cream)] backdrop-blur">
28
30
  <div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-6">
@@ -57,7 +59,7 @@ export default function MarketingLayout({ children, auth }: LayoutProps) {
57
59
  <main className="flex-1">{children}</main>
58
60
 
59
61
  <SiteFooter />
60
- </>
62
+ </div>
61
63
  );
62
64
  }
63
65
 
@@ -3,6 +3,7 @@ import { type Metadata } from "@pylonsync/react";
3
3
  import { LiveBadge } from "@/components/marketing";
4
4
  import { ReservationWidget } from "./reservation-widget";
5
5
  import { siteConfig, type DayHours } from "@/lib/site.config";
6
+ import { clock, directionsUrl, hoursRows } from "@/lib/hours";
6
7
 
7
8
  export const metadata: Metadata = {
8
9
  title: siteConfig.seo.title,
@@ -24,7 +25,8 @@ export default function LandingPage() {
24
25
  return (
25
26
  <div className="bg-[var(--ink)] text-[var(--cream)]">
26
27
  {/* HERO: the room, full bleed, the words on top. */}
27
- <section className="relative isolate min-h-[88vh] overflow-hidden">
28
+ {/* -mt-16 runs the photo up under the translucent nav. */}
29
+ <section className="relative isolate -mt-16 min-h-[88vh] overflow-hidden">
28
30
  <img src={hero.image} alt="" className="absolute inset-0 -z-20 size-full object-cover" />
29
31
  <div className="absolute inset-0 -z-10 bg-gradient-to-t from-[var(--ink)] via-[var(--ink)]/60 to-[var(--ink)]/20" />
30
32
  <div className={`${WRAP} flex min-h-[88vh] flex-col justify-end pb-16 pt-32`}>
@@ -155,29 +157,34 @@ export default function LandingPage() {
155
157
 
156
158
  {/* VISIT */}
157
159
  <section id="visit" className="border-t border-white/10">
158
- <div className={`${WRAP} grid gap-10 py-16 lg:grid-cols-2`}>
159
- <div>
160
+ <div className={`${WRAP} grid grid-cols-[minmax(0,1fr)] gap-10 py-16 lg:grid-cols-2`}>
161
+ <div className="min-w-0">
160
162
  <h2 className="font-display text-balance text-[2.25rem] leading-[1.08] sm:text-[3rem]">{location.headline}</h2>
161
- <div className="mt-6 space-y-2 text-[15px] leading-relaxed text-[var(--cream-2)]">
162
- <p className="text-[var(--cream)]">{location.address}</p>
163
- <p>{location.hoursText}</p>
164
- <p>
165
- <a href={`tel:${location.phone}`} className="text-[var(--cream)] underline-offset-4 hover:underline">
166
- {location.phone}
167
- </a>{" "}
168
- · <a href={`mailto:${location.email}`} className="underline-offset-4 hover:underline">{location.email}</a>
169
- </p>
163
+ <p className="font-display mt-6 max-w-sm text-[1.5rem] leading-snug sm:text-[1.75rem]">{location.address}</p>
164
+ <div className="mt-8 flex flex-wrap items-center gap-3">
165
+ <a
166
+ href={directionsUrl(location.address)}
167
+ target="_blank"
168
+ rel="noreferrer"
169
+ className="inline-flex items-center rounded-full bg-brand px-5 py-2.5 text-[14px] font-medium text-white transition-opacity hover:opacity-90"
170
+ >
171
+ Get directions
172
+ </a>
173
+ <a href={`tel:${location.phone}`} className="inline-flex items-center rounded-full border border-white/25 px-5 py-2.5 text-[14px] font-medium transition-colors hover:bg-white/10">
174
+ Call {location.phone}
175
+ </a>
170
176
  </div>
171
- <a href="#reserve" className="mt-8 inline-flex items-center rounded-full border border-white/25 px-5 py-2.5 text-[14px] font-medium transition-colors hover:bg-white/10">
172
- {hero.ctaLabel}
173
- </a>
177
+ <p className="mt-6 text-[14px] text-[var(--cream-2)]">
178
+ Or write to{" "}
179
+ <a href={`mailto:${location.email}`} className="text-[var(--cream)] underline-offset-4 hover:underline">
180
+ {location.email}
181
+ </a>
182
+ </p>
174
183
  </div>
175
184
  {location.mapEmbedUrl ? (
176
- <iframe title="Map" src={location.mapEmbedUrl} className="h-72 w-full rounded-[6px] border border-white/10 grayscale" loading="lazy" />
185
+ <iframe title={`Map to ${siteConfig.brand.name}`} src={location.mapEmbedUrl} className="h-80 w-full rounded-[6px] border border-white/10 grayscale" loading="lazy" />
177
186
  ) : (
178
- <div className="grid h-72 place-items-center rounded-[6px] border border-white/10 bg-[var(--ink-2)] text-[13px] text-[var(--cream-2)]">
179
- Set <code className="mx-1 font-mono">location.mapEmbedUrl</code> to show a map here
180
- </div>
187
+ <HoursCard />
181
188
  )}
182
189
  </div>
183
190
  </section>
@@ -210,8 +217,25 @@ function hoursLabel(h: DayHours): string {
210
217
  return `${clock(h.open)} – ${clock(h.close)}`;
211
218
  }
212
219
 
213
- function clock(t: string): string {
214
- const [hh, mm] = t.split(":").map(Number);
215
- const h12 = ((hh + 11) % 12) + 1;
216
- return `${h12}${mm ? `:${String(mm).padStart(2, "0")}` : ""}${hh < 12 ? "am" : "pm"}`;
220
+ // The week's service hours on a cream card, from the same config the
221
+ // reservation calendar reads. Shown in place of a map when
222
+ // `location.mapEmbedUrl` is empty.
223
+ function HoursCard() {
224
+ const rows = hoursRows(siteConfig.reservations.hours);
225
+ return (
226
+ <div className="rounded-[6px] bg-[var(--cream)] p-6 text-[var(--ink)] sm:p-8">
227
+ <h3 className="font-display text-[1.75rem] italic leading-none">Dinner service</h3>
228
+ <dl className="mt-6 divide-y divide-[var(--ink)]/10">
229
+ {rows.map((r) => (
230
+ <div key={r.days} className="flex items-baseline justify-between gap-4 py-3">
231
+ <dt className="text-[15px] font-medium">{r.days}</dt>
232
+ <dd className={"text-[15px] tabular-nums " + (r.closed ? "text-[var(--ink)]/45" : "")}>{r.time}</dd>
233
+ </div>
234
+ ))}
235
+ </dl>
236
+ <a href="#reserve" className="mt-6 inline-flex items-center text-[14px] font-medium text-brand underline-offset-4 hover:underline">
237
+ {siteConfig.hero.ctaLabel}
238
+ </a>
239
+ </div>
240
+ );
217
241
  }
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import React, { useMemo, useState } from "react";
3
+ import React, { useEffect, useMemo, useRef, useState } from "react";
4
4
  import { db, callFn } from "@pylonsync/react";
5
5
  import { EnsureGuest } from "@pylonsync/client";
6
6
  import { siteConfig } from "@/lib/site.config";
@@ -33,13 +33,38 @@ export function ReservationWidget() {
33
33
  function Picker() {
34
34
  const cfg = siteConfig.reservations;
35
35
  const { data: markers, loading } = db.useQuery<SlotMarker>("ReservationSlot");
36
+
37
+ // In `pylon dev` an empty book gets demo reservations (functions/seedDemo.ts),
38
+ // which show up here as fewer tables left. The call goes out only when no
39
+ // table is reserved, so a live site with reservations never makes it.
40
+ const empty = !loading && markers.length === 0;
41
+ const seedSent = useRef(false);
42
+ useEffect(() => {
43
+ if (!empty || seedSent.current) return;
44
+ seedSent.current = true;
45
+ void callFn("seedDemo", {}).catch(() => {});
46
+ }, [empty]);
36
47
  const [nowMs] = useState(() => Date.now());
37
48
 
38
49
  const openDays = useMemo(() => {
39
50
  const days: string[] = [];
40
51
  for (let i = 0; i < cfg.daysAhead; i++) {
41
52
  const key = localDateKey(i, nowMs);
42
- if (cfg.hours[weekdayOf(key)]) days.push(key);
53
+ const hrs = cfg.hours[weekdayOf(key)];
54
+ if (!hrs) continue;
55
+ // Today drops out once its last seating is too soon to book.
56
+ if (i === 0) {
57
+ const left = seatingsForDay({
58
+ dayISODate: key,
59
+ open: hrs.open,
60
+ close: hrs.close,
61
+ slotMinutes: cfg.slotMinutes,
62
+ leadTimeHours: cfg.leadTimeHours,
63
+ nowMs,
64
+ }).some((s) => !s.past);
65
+ if (!left) continue;
66
+ }
67
+ days.push(key);
43
68
  }
44
69
  return days;
45
70
  }, [cfg.daysAhead, cfg.hours, nowMs]);
@@ -68,10 +93,13 @@ function Picker() {
68
93
  slotMinutes: cfg.slotMinutes,
69
94
  leadTimeHours: cfg.leadTimeHours,
70
95
  nowMs,
71
- }).map((s) => {
72
- const remaining = cfg.tablesPerSlot - (takenByTime.get(s.startsAt) ?? 0);
73
- return { startsAt: s.startsAt, remaining, available: !s.past && remaining > 0 };
74
- });
96
+ })
97
+ // Seatings too soon to book are left out, so "Full" only ever means full.
98
+ .filter((s) => !s.past)
99
+ .map((s) => {
100
+ const remaining = Math.max(0, cfg.tablesPerSlot - (takenByTime.get(s.startsAt) ?? 0));
101
+ return { startsAt: s.startsAt, remaining, available: remaining > 0 };
102
+ });
75
103
  }, [day, cfg, takenByTime, nowMs]);
76
104
 
77
105
  // Clear an in-progress selection if its seating just filled up (live).
@@ -142,8 +170,10 @@ function Picker() {
142
170
  <div className="mt-5 border-t border-zinc-100 pt-5">
143
171
  {loading ? (
144
172
  <SeatingsSkeleton />
173
+ ) : !cfg.hours[weekdayOf(day)] ? (
174
+ <p className="py-6 text-center text-sm text-zinc-500">Closed that day. Pick another.</p>
145
175
  ) : seatings.length === 0 ? (
146
- <p className="py-6 text-center text-sm text-zinc-500">Closed that day — pick another.</p>
176
+ <p className="py-6 text-center text-sm text-zinc-500">No seatings left that day. Pick another.</p>
147
177
  ) : (
148
178
  <div className="grid grid-cols-3 gap-2 sm:grid-cols-4">
149
179
  {seatings.map((seat) => {
@@ -51,7 +51,7 @@ export function AuthForm() {
51
51
  <div className="space-y-5">
52
52
  <form onSubmit={onSubmit} className="space-y-4">
53
53
  <label className="block">
54
- <span className="mb-1.5 block text-[13px] font-medium text-zinc-700">Email</span>
54
+ <span className="mb-1.5 block text-[13px] font-medium text-[var(--cream-2)]">Email</span>
55
55
  <input
56
56
  type="email"
57
57
  value={email}
@@ -59,11 +59,11 @@ export function AuthForm() {
59
59
  required
60
60
  autoComplete="email"
61
61
  placeholder="you@yourbusiness.com"
62
- className="h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 text-sm text-zinc-900 outline-none transition placeholder:text-zinc-400 focus:border-brand focus:ring-2 focus:ring-brand/20"
62
+ className="h-11 w-full rounded-[6px] border border-white/15 bg-[var(--ink)] px-3.5 text-[15px] text-[var(--cream)] outline-none transition placeholder:text-[var(--cream-2)]/60 focus:border-brand"
63
63
  />
64
64
  </label>
65
65
  <label className="block">
66
- <span className="mb-1.5 block text-[13px] font-medium text-zinc-700">Password</span>
66
+ <span className="mb-1.5 block text-[13px] font-medium text-[var(--cream-2)]">Password</span>
67
67
  <input
68
68
  type="password"
69
69
  value={password}
@@ -71,24 +71,24 @@ export function AuthForm() {
71
71
  required
72
72
  autoComplete={mode === "login" ? "current-password" : "new-password"}
73
73
  placeholder={mode === "login" ? "Your password" : "At least 10 characters"}
74
- className="h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 text-sm text-zinc-900 outline-none transition placeholder:text-zinc-400 focus:border-brand focus:ring-2 focus:ring-brand/20"
74
+ className="h-11 w-full rounded-[6px] border border-white/15 bg-[var(--ink)] px-3.5 text-[15px] text-[var(--cream)] outline-none transition placeholder:text-[var(--cream-2)]/60 focus:border-brand"
75
75
  />
76
76
  </label>
77
77
  {error ? (
78
- <p className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-[13px] leading-snug text-red-700">
78
+ <p className="rounded-[6px] border border-brand/40 bg-brand/10 px-3 py-2 text-[13px] leading-snug text-[var(--cream)]">
79
79
  {error}
80
80
  </p>
81
81
  ) : null}
82
82
  <button
83
83
  type="submit"
84
84
  disabled={pending}
85
- className="inline-flex h-10 w-full items-center justify-center rounded-lg bg-zinc-900 text-sm font-medium text-white transition-colors hover:bg-zinc-700 disabled:opacity-60"
85
+ className="inline-flex h-11 w-full items-center justify-center rounded-full bg-brand text-[14px] font-medium text-white transition-opacity hover:opacity-90 disabled:opacity-60"
86
86
  >
87
87
  {pending ? "…" : mode === "login" ? "Sign in" : "Create account"}
88
88
  </button>
89
89
  </form>
90
90
 
91
- <p className="text-center text-[13px] text-zinc-500">
91
+ <p className="text-center text-[13px] text-[var(--cream-2)]">
92
92
  {mode === "login" ? "First time here?" : "Already have an account?"}{" "}
93
93
  <button
94
94
  type="button"
@@ -96,7 +96,7 @@ export function AuthForm() {
96
96
  setMode(mode === "login" ? "signup" : "login");
97
97
  setError(null);
98
98
  }}
99
- className="font-medium text-zinc-900 underline underline-offset-2"
99
+ className="font-medium text-[var(--cream)] underline underline-offset-4"
100
100
  >
101
101
  {mode === "login" ? "Create the owner account" : "Sign in"}
102
102
  </button>