@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
@@ -3,7 +3,9 @@
3
3
  import React, { useEffect, useMemo, useState } from "react";
4
4
  import { db, callFn } from "@pylonsync/react";
5
5
  import { useAuth } from "@pylonsync/client";
6
+ import { ArrowUpRight, Check, Eye, EyeOff, LogOut, Plus, Star, Trash2, X } from "lucide-react";
6
7
  import {
8
+ bookingWindow,
7
9
  money,
8
10
  parseLineItems,
9
11
  lineItemsTotal,
@@ -46,9 +48,11 @@ export function AgencyDashboard({ userEmail }: { userEmail: string }) {
46
48
  setAuthorized(false);
47
49
  return;
48
50
  }
49
- setAuthorized(true);
50
- // Seed demo clients + invoices once, for the owner only (idempotent).
51
- void callFn("seedStudioBackoffice", {}).catch(() => {});
51
+ // In `pylon dev` an empty back-office gets demo leads, clients, and
52
+ // invoices before the tabs load (functions/seedStudioBackoffice.ts). On
53
+ // a deploy the call returns without writing.
54
+ await callFn("seedStudioBackoffice", {}).catch(() => {});
55
+ if (!cancelled) setAuthorized(true);
52
56
  } catch (e) {
53
57
  if (!cancelled) setError(e instanceof Error ? e.message : String(e));
54
58
  }
@@ -72,17 +76,17 @@ export function AgencyDashboard({ userEmail }: { userEmail: string }) {
72
76
  ];
73
77
 
74
78
  return (
75
- <div className="space-y-6">
76
- <nav className="flex gap-1 border-b border-zinc-200">
79
+ <div className="space-y-8">
80
+ <nav className="flex gap-1 overflow-x-auto border-b border-zinc-200">
77
81
  {tabs.map((t) => (
78
82
  <button
79
83
  key={t.id}
80
84
  type="button"
81
85
  onClick={() => setTab(t.id)}
82
86
  className={
83
- "-mb-px border-b-2 px-3.5 py-2.5 text-[13.5px] font-medium transition-colors " +
87
+ "-mb-px shrink-0 border-b-2 px-3.5 py-2.5 text-[14px] font-medium transition-colors " +
84
88
  (tab === t.id
85
- ? "border-brand text-zinc-900"
89
+ ? "border-ink text-ink"
86
90
  : "border-transparent text-zinc-500 hover:text-zinc-800")
87
91
  }
88
92
  >
@@ -148,10 +152,10 @@ function PipelinePanel() {
148
152
 
149
153
  return (
150
154
  <div className="space-y-6">
151
- <PanelHead title="Pipeline" sub="Live — leads land here the moment they're sent. Booking one drops the open-slot count on your site instantly." />
155
+ <PanelHead title="Pipeline" sub="Leads appear here as they are sent. Booking one lowers the open-slot count on your site." live />
152
156
 
153
- <div className="grid gap-4 sm:grid-cols-3">
154
- <Stat label="Open slots" value={String(cap?.openSlots ?? 0)} hint={cap?.label} />
157
+ <div className="grid gap-px overflow-hidden rounded-sm border border-zinc-200 bg-zinc-200 sm:grid-cols-3">
158
+ <Stat label="Open slots" value={String(cap?.openSlots ?? 0)} hint={bookingWindow(cap?.label, Date.now())} />
155
159
  <Stat label="New leads" value={String(newCount)} />
156
160
  <Stat label="Booked" value={String(bookedCount)} />
157
161
  </div>
@@ -160,7 +164,7 @@ function PipelinePanel() {
160
164
 
161
165
  <Card title="Inquiries" count={active.length}>
162
166
  {inquiries.length === 0 ? (
163
- <Empty>No inquiries yet — share your site.</Empty>
167
+ <Empty>No inquiries yet. They appear here as soon as someone sends the contact form.</Empty>
164
168
  ) : (
165
169
  <ul className="divide-y divide-zinc-100">
166
170
  {inquiries.map((i) => (
@@ -187,9 +191,10 @@ function PipelinePanel() {
187
191
  type="button"
188
192
  disabled={busyId === i.id}
189
193
  onClick={() => act(i.id, "bookInquiry")}
190
- className="rounded-md bg-brand px-3 py-1.5 text-[12.5px] font-medium text-white transition-opacity hover:opacity-90 disabled:opacity-50"
194
+ className="inline-flex items-center gap-1.5 rounded-full bg-ink px-3.5 py-1.5 text-[12.5px] font-medium text-white transition-opacity hover:opacity-85 disabled:opacity-50"
191
195
  >
192
- {busyId === i.id ? "…" : "Book"}
196
+ <Check aria-hidden className="size-3.5" />
197
+ Book
193
198
  </button>
194
199
  ) : null}
195
200
  {i.status !== "declined" ? (
@@ -197,7 +202,7 @@ function PipelinePanel() {
197
202
  type="button"
198
203
  disabled={busyId === i.id}
199
204
  onClick={() => act(i.id, "declineInquiry")}
200
- className="rounded-md border border-zinc-300 px-3 py-1.5 text-[12.5px] font-medium text-zinc-600 transition-colors hover:border-red-300 hover:text-red-600 disabled:opacity-50"
205
+ className="rounded-full border border-zinc-300 px-3.5 py-1.5 text-[12.5px] font-medium text-zinc-600 transition-colors hover:border-zinc-500 hover:text-ink disabled:opacity-50"
201
206
  >
202
207
  {i.status === "booked" ? "Release" : "Decline"}
203
208
  </button>
@@ -237,17 +242,20 @@ function CapacityCard({ cap }: { cap?: CapacityRow }) {
237
242
  }
238
243
 
239
244
  return (
240
- <form onSubmit={save} className="rounded-xl border border-zinc-200 bg-white p-4">
241
- <div className="text-sm font-semibold text-zinc-900">Availability</div>
242
- <p className="mt-1 text-[13px] text-zinc-500">Shown live on your site as “N project slots open”.</p>
245
+ <form onSubmit={save} className="rounded-sm border border-zinc-200 bg-white p-5">
246
+ <h2 className="font-display text-[1.5rem] leading-none text-ink">Availability</h2>
247
+ <p className="mt-2 text-[13px] text-zinc-500">
248
+ Your site shows this as “N project slots open”. Leave the window empty to use the next
249
+ quarter a project could start in.
250
+ </p>
243
251
  <div className="mt-4 flex flex-wrap items-end gap-3">
244
252
  <label className="block">
245
253
  <span className="mb-1 block text-[12px] font-medium text-zinc-600">Booking window</span>
246
254
  <input
247
255
  value={label}
248
256
  onChange={(e) => { setLabel(e.target.value); setSaved(false); }}
249
- placeholder="Q3 2026"
250
- className="h-9 w-40 rounded-lg border border-zinc-300 px-3 text-[14px] outline-none focus:border-brand focus:ring-2 focus:ring-brand/20"
257
+ placeholder={bookingWindow("", Date.now())}
258
+ className="h-9 w-44 rounded-sm border border-zinc-300 px-3 text-[14px] outline-none focus:border-brand focus:ring-2 focus:ring-brand/20"
251
259
  />
252
260
  </label>
253
261
  <label className="block">
@@ -257,16 +265,22 @@ function CapacityCard({ cap }: { cap?: CapacityRow }) {
257
265
  min={0}
258
266
  value={slots}
259
267
  onChange={(e) => { setSlots(e.target.value); setSaved(false); }}
260
- className="h-9 w-24 rounded-lg border border-zinc-300 px-3 text-[14px] tabular-nums outline-none focus:border-brand focus:ring-2 focus:ring-brand/20"
268
+ className="h-9 w-24 rounded-sm border border-zinc-300 px-3 text-[14px] tabular-nums outline-none focus:border-brand focus:ring-2 focus:ring-brand/20"
261
269
  />
262
270
  </label>
263
271
  <button
264
272
  type="submit"
265
273
  disabled={saving}
266
- className="h-9 rounded-lg bg-zinc-900 px-4 text-[13px] font-medium text-white transition-colors hover:bg-zinc-700 disabled:opacity-50"
274
+ className="inline-flex h-9 items-center gap-1.5 rounded-full bg-ink px-4 text-[13px] font-medium text-white transition-opacity hover:opacity-85 disabled:opacity-50"
267
275
  >
268
- {saving ? "Saving…" : saved ? "Saved ✓" : "Save"}
276
+ {saving ? "Saving…" : "Save"}
269
277
  </button>
278
+ {saved ? (
279
+ <span role="status" className="inline-flex items-center gap-1 text-[13px] text-green-700">
280
+ <Check aria-hidden className="size-3.5" />
281
+ Saved
282
+ </span>
283
+ ) : null}
270
284
  </div>
271
285
  </form>
272
286
  );
@@ -310,13 +324,13 @@ function WorkPanel() {
310
324
  <div className="space-y-6">
311
325
  <PanelHead
312
326
  title="Work"
313
- sub={`Your portfolio. ${selectedCount} featured on the homepage. Toggle the star to feature, the eye to publish.`}
327
+ sub={`Your portfolio. ${selectedCount} featured on the homepage. The star features a project; the eye publishes it.`}
314
328
  action={<NewButton onClick={() => setEditing("new")}>New project</NewButton>}
315
329
  />
316
330
 
317
331
  <Card title="Projects" count={projects.length}>
318
332
  {ordered.length === 0 ? (
319
- <Empty>No projects yet — add your first case study.</Empty>
333
+ <Empty>No projects yet. Add your first case study.</Empty>
320
334
  ) : (
321
335
  <ul className="divide-y divide-zinc-100">
322
336
  {ordered.map((p) => (
@@ -343,7 +357,7 @@ function WorkPanel() {
343
357
  title={p.selected ? "Featured on homepage" : "Feature on homepage"}
344
358
  onClick={() => flag(p.id, { selected: !p.selected })}
345
359
  >
346
- {p.selected ? "★" : "☆"}
360
+ <Star aria-hidden className="size-4" fill={p.selected ? "currentColor" : "none"} />
347
361
  </IconToggle>
348
362
  <IconToggle
349
363
  on={p.published}
@@ -351,16 +365,17 @@ function WorkPanel() {
351
365
  title={p.published ? "Published" : "Draft (hidden)"}
352
366
  onClick={() => flag(p.id, { published: !p.published })}
353
367
  >
354
- {p.published ? "👁" : "🚫"}
368
+ {p.published ? <Eye aria-hidden className="size-4" /> : <EyeOff aria-hidden className="size-4" />}
355
369
  </IconToggle>
356
370
  <a
357
371
  href={`/work/${p.slug}`}
358
372
  target="_blank"
359
373
  rel="noreferrer"
360
- className="rounded-md px-2 py-1 text-[12px] text-zinc-500 hover:bg-zinc-100 hover:text-zinc-800"
374
+ className="rounded-md p-1.5 text-zinc-500 hover:bg-zinc-100 hover:text-zinc-800"
361
375
  title="View case study"
376
+ aria-label={`View the ${p.title} case study`}
362
377
  >
363
- ↗
378
+ <ArrowUpRight aria-hidden className="size-4" />
364
379
  </a>
365
380
  <button
366
381
  type="button"
@@ -373,10 +388,11 @@ function WorkPanel() {
373
388
  type="button"
374
389
  disabled={busyId === p.id}
375
390
  onClick={() => remove(p)}
376
- className="rounded-md px-2 py-1 text-[12.5px] text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
391
+ className="rounded-md p-1.5 text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
377
392
  title="Delete"
393
+ aria-label="Delete"
378
394
  >
379
- ✕
395
+ <Trash2 aria-hidden className="size-4" />
380
396
  </button>
381
397
  </div>
382
398
  </li>
@@ -445,7 +461,7 @@ function ProjectForm({
445
461
  });
446
462
  onClose();
447
463
  } catch (e) {
448
- setErr(e instanceof Error ? e.message : "Couldn't save — try again.");
464
+ setErr(e instanceof Error ? e.message : "Could not save. Try again.");
449
465
  setSaving(false);
450
466
  }
451
467
  }
@@ -514,7 +530,7 @@ function ClientsPanel() {
514
530
  setNote(null);
515
531
  try {
516
532
  const r = await callFn<{ ok: boolean; invoices: number }>("deleteClient", { id: c.id });
517
- if (!r.ok) setNote(`${c.name} has ${r.invoices} invoice${r.invoices === 1 ? "" : "s"} — delete those first.`);
533
+ if (!r.ok) setNote(`${c.name} has ${r.invoices} invoice${r.invoices === 1 ? "" : "s"}. Delete those first.`);
518
534
  else await load();
519
535
  } finally {
520
536
  setBusyId(null);
@@ -527,7 +543,7 @@ function ClientsPanel() {
527
543
  <div className="space-y-6">
528
544
  <PanelHead
529
545
  title="Clients"
530
- sub="Your CRM — private contact details, never exposed to the public site."
546
+ sub="Contacts and their status. Only you can see this tab; nothing here reaches the public site."
531
547
  action={<NewButton onClick={() => setEditing("new")}>New client</NewButton>}
532
548
  />
533
549
  {note ? (
@@ -536,7 +552,7 @@ function ClientsPanel() {
536
552
 
537
553
  <Card title="Contacts" count={clients.length}>
538
554
  {clients.length === 0 ? (
539
- <Empty>No clients yet — add your first contact.</Empty>
555
+ <Empty>No clients yet. Add your first contact.</Empty>
540
556
  ) : (
541
557
  <ul className="divide-y divide-zinc-100">
542
558
  {clients.map((c) => (
@@ -560,10 +576,11 @@ function ClientsPanel() {
560
576
  type="button"
561
577
  disabled={busyId === c.id}
562
578
  onClick={() => remove(c)}
563
- className="rounded-md px-2 py-1 text-[12.5px] text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
579
+ className="rounded-md p-1.5 text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
564
580
  title="Delete"
581
+ aria-label="Delete"
565
582
  >
566
- ✕
583
+ <Trash2 aria-hidden className="size-4" />
567
584
  </button>
568
585
  </div>
569
586
  </li>
@@ -617,7 +634,7 @@ function ClientForm({
617
634
  await onSaved();
618
635
  onClose();
619
636
  } catch (e) {
620
- setErr(e instanceof Error ? e.message : "Couldn't save — try again.");
637
+ setErr(e instanceof Error ? e.message : "Could not save. Try again.");
621
638
  setSaving(false);
622
639
  }
623
640
  }
@@ -713,11 +730,11 @@ function InvoicesPanel() {
713
730
  <div className="space-y-6">
714
731
  <PanelHead
715
732
  title="Invoices"
716
- sub="Billing — private. View any invoice, export it to PDF, and track what's paid vs outstanding. Never syncs to the public site."
733
+ sub="View an invoice, export it to PDF, and track what is paid and outstanding. Only you can see this tab."
717
734
  action={<NewButton onClick={() => setEditing("new")}>New invoice</NewButton>}
718
735
  />
719
736
 
720
- <div className="grid gap-4 sm:grid-cols-3">
737
+ <div className="grid gap-px overflow-hidden rounded-sm border border-zinc-200 bg-zinc-200 sm:grid-cols-3">
721
738
  <Stat label="Paid" value={money(totals.paid)} />
722
739
  <Stat label="Outstanding" value={money(totals.outstanding)} />
723
740
  <Stat label="Invoices" value={String(invoices.length)} />
@@ -770,10 +787,11 @@ function InvoicesPanel() {
770
787
  type="button"
771
788
  disabled={busyId === inv.id}
772
789
  onClick={() => remove(inv)}
773
- className="rounded-md px-2 py-1 text-[12.5px] text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
790
+ className="rounded-md p-1.5 text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
774
791
  title="Delete"
792
+ aria-label="Delete"
775
793
  >
776
- ✕
794
+ <Trash2 aria-hidden className="size-4" />
777
795
  </button>
778
796
  </div>
779
797
  </li>
@@ -894,7 +912,7 @@ function InvoiceView({
894
912
  >
895
913
  {downloading ? "Preparing…" : "Download PDF"}
896
914
  </button>
897
- <button type="button" onClick={onClose} className="rounded-md px-2 py-1 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700">✕</button>
915
+ <button type="button" onClick={onClose} aria-label="Close" className="rounded-md p-1.5 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700"><X aria-hidden className="size-4" /></button>
898
916
  </div>
899
917
  </div>
900
918
 
@@ -1072,7 +1090,7 @@ function InvoiceForm({
1072
1090
  await onSaved();
1073
1091
  onClose();
1074
1092
  } catch (e) {
1075
- setErr(e instanceof Error ? e.message : "Couldn't save — try again.");
1093
+ setErr(e instanceof Error ? e.message : "Could not save. Try again.");
1076
1094
  setSaving(false);
1077
1095
  }
1078
1096
  }
@@ -1083,7 +1101,7 @@ function InvoiceForm({
1083
1101
  <Modal title={initial ? "Edit invoice" : "New invoice"} onClose={onClose} onSubmit={save} saving={saving} error={err}>
1084
1102
  {noClients ? (
1085
1103
  <p className="rounded-lg bg-amber-50 px-3 py-2 text-[13px] text-amber-800">
1086
- Add a client first — an invoice needs someone to bill.
1104
+ Add a client first. An invoice needs someone to bill.
1087
1105
  </p>
1088
1106
  ) : null}
1089
1107
  <div className="grid gap-3 sm:grid-cols-2">
@@ -1143,8 +1161,9 @@ function InvoiceForm({
1143
1161
  disabled={items.length === 1}
1144
1162
  className="shrink-0 rounded-md px-2 py-1 text-[13px] text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-30"
1145
1163
  title="Remove line"
1164
+ aria-label="Remove line"
1146
1165
  >
1147
- ✕
1166
+ <X aria-hidden className="size-4" />
1148
1167
  </button>
1149
1168
  </div>
1150
1169
  ))}
@@ -1159,7 +1178,7 @@ function InvoiceForm({
1159
1178
  </div>
1160
1179
  </div>
1161
1180
 
1162
- <Field label="Project" hint="Optional — ties the bill to a case study">
1181
+ <Field label="Project" hint="Optional. Links the invoice to a case study">
1163
1182
  <select value={f.projectId} onChange={(e) => set("projectId", e.target.value)} className={inputCls}>
1164
1183
  <option value="">None</option>
1165
1184
  {projects.map((p) => (
@@ -1192,14 +1211,33 @@ function InvoiceForm({
1192
1211
 
1193
1212
  /* ============================== PRIMITIVES ============================= */
1194
1213
 
1195
- function PanelHead({ title, sub, action }: { title: string; sub: string; action?: React.ReactNode }) {
1214
+ function PanelHead({
1215
+ title,
1216
+ sub,
1217
+ action,
1218
+ live,
1219
+ }: {
1220
+ title: string;
1221
+ sub: string;
1222
+ action?: React.ReactNode;
1223
+ live?: boolean;
1224
+ }) {
1196
1225
  return (
1197
- <div className="flex flex-wrap items-start justify-between gap-3">
1226
+ <div className="flex flex-wrap items-end justify-between gap-4">
1198
1227
  <div>
1199
- <h1 className="text-xl font-semibold tracking-tight">{title}</h1>
1200
- <p className="mt-1 max-w-xl text-sm text-zinc-500">{sub}</p>
1228
+ <h1 className="font-display text-[2.75rem] leading-none tracking-[-0.015em] text-ink">{title}</h1>
1229
+ <p className="mt-3 max-w-xl text-[14px] leading-relaxed text-zinc-600">{sub}</p>
1201
1230
  </div>
1202
1231
  {action}
1232
+ {live ? (
1233
+ <span className="inline-flex items-center gap-2 text-[13px] text-zinc-600">
1234
+ <span className="relative flex size-2">
1235
+ <span className="absolute inline-flex size-2 animate-ping rounded-full bg-green-500/60" />
1236
+ <span className="relative inline-flex size-2 rounded-full bg-green-600" />
1237
+ </span>
1238
+ Live
1239
+ </span>
1240
+ ) : null}
1203
1241
  </div>
1204
1242
  );
1205
1243
  }
@@ -1209,18 +1247,20 @@ function NewButton({ onClick, children }: { onClick: () => void; children: React
1209
1247
  <button
1210
1248
  type="button"
1211
1249
  onClick={onClick}
1212
- className="shrink-0 rounded-lg bg-zinc-900 px-3.5 py-2 text-[13px] font-medium text-white transition-colors hover:bg-zinc-700"
1250
+ className="inline-flex shrink-0 items-center gap-1.5 rounded-full bg-ink px-4 py-2 text-[13px] font-medium text-white transition-opacity hover:opacity-85"
1213
1251
  >
1214
- + {children}
1252
+ <Plus aria-hidden className="size-3.5" />
1253
+ {children}
1215
1254
  </button>
1216
1255
  );
1217
1256
  }
1218
1257
 
1219
1258
  function Card({ title, count, children }: { title: string; count: number; children: React.ReactNode }) {
1220
1259
  return (
1221
- <div className="rounded-xl border border-zinc-200 bg-white">
1222
- <div className="border-b border-zinc-100 px-4 py-3 text-sm font-semibold text-zinc-900">
1223
- {title} <span className="font-normal text-zinc-400">({count})</span>
1260
+ <div className="rounded-sm border border-zinc-200 bg-white">
1261
+ <div className="flex items-baseline gap-2 border-b border-zinc-100 px-4 py-3.5">
1262
+ <h2 className="font-display text-[1.375rem] leading-none text-ink">{title}</h2>
1263
+ <span className="text-[13px] tabular-nums text-zinc-400">{count}</span>
1224
1264
  </div>
1225
1265
  {children}
1226
1266
  </div>
@@ -1233,9 +1273,9 @@ function Empty({ children }: { children: React.ReactNode }) {
1233
1273
 
1234
1274
  function Stat({ label, value, hint }: { label: string; value: string; hint?: string }) {
1235
1275
  return (
1236
- <div className="rounded-xl border border-zinc-200 bg-white p-4">
1237
- <div className="text-[11px] font-medium uppercase tracking-wide text-zinc-400">{label}</div>
1238
- <div className="mt-1 text-2xl font-semibold tabular-nums text-zinc-900">{value}</div>
1276
+ <div className="bg-white p-5">
1277
+ <div className="text-[11px] font-medium uppercase tracking-wide text-zinc-500">{label}</div>
1278
+ <div className="font-display mt-2 text-[2.75rem] leading-none tabular-nums text-ink">{value}</div>
1239
1279
  {hint ? <div className="mt-0.5 text-[12px] text-zinc-400">{hint}</div> : null}
1240
1280
  </div>
1241
1281
  );
@@ -1284,10 +1324,12 @@ function IconToggle({
1284
1324
  type="button"
1285
1325
  disabled={disabled}
1286
1326
  title={title}
1327
+ aria-label={title}
1328
+ aria-pressed={on}
1287
1329
  onClick={onClick}
1288
1330
  className={
1289
- "rounded-md px-2 py-1 text-[13px] leading-none transition-colors disabled:opacity-50 " +
1290
- (on ? "text-amber-500 hover:bg-amber-50" : "text-zinc-300 hover:bg-zinc-100 hover:text-zinc-500")
1331
+ "rounded-md p-1.5 leading-none transition-colors disabled:opacity-50 " +
1332
+ (on ? "text-ink hover:bg-zinc-100" : "text-zinc-300 hover:bg-zinc-100 hover:text-zinc-500")
1291
1333
  }
1292
1334
  >
1293
1335
  {children}
@@ -1312,7 +1354,7 @@ function Switch({ label, checked, onChange }: { label: string; checked: boolean;
1312
1354
  }
1313
1355
 
1314
1356
  // A simple centered modal with a sticky footer. Submits on the footer button or
1315
- // Enter; closes on the backdrop, the ✕, or Escape.
1357
+ // Enter; closes on the backdrop, the close button, or Escape.
1316
1358
  function Modal({
1317
1359
  title,
1318
1360
  onClose,
@@ -1343,7 +1385,7 @@ function Modal({
1343
1385
  >
1344
1386
  <div className="flex items-center justify-between border-b border-zinc-100 px-5 py-3.5">
1345
1387
  <h2 className="text-[15px] font-semibold text-zinc-900">{title}</h2>
1346
- <button type="button" onClick={onClose} className="rounded-md px-2 py-1 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700">✕</button>
1388
+ <button type="button" onClick={onClose} aria-label="Close" className="rounded-md p-1.5 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700"><X aria-hidden className="size-4" /></button>
1347
1389
  </div>
1348
1390
  <div className="max-h-[70vh] space-y-3.5 overflow-y-auto px-5 py-4">{children}</div>
1349
1391
  <div className="flex items-center justify-between gap-3 border-t border-zinc-100 px-5 py-3.5">
@@ -1406,7 +1448,10 @@ export function UserMenu({ email }: { email: string }) {
1406
1448
  }
1407
1449
  return (
1408
1450
  <details className="group relative">
1409
- <summary className="flex size-8 cursor-pointer select-none list-none items-center justify-center rounded-full bg-zinc-900 text-[12px] font-semibold text-white marker:hidden [&::-webkit-details-marker]:hidden">
1451
+ <summary
1452
+ aria-label="Account"
1453
+ className="flex size-8 cursor-pointer select-none list-none items-center justify-center rounded-full bg-ink text-[12px] font-semibold text-white marker:hidden [&::-webkit-details-marker]:hidden"
1454
+ >
1410
1455
  {initial}
1411
1456
  </summary>
1412
1457
  <div className="absolute right-0 top-full z-40 mt-2 w-56 overflow-hidden rounded-xl border border-zinc-200 bg-white py-1 shadow-[0_16px_48px_-16px_rgba(0,0,0,0.25)]">
@@ -1416,8 +1461,9 @@ export function UserMenu({ email }: { email: string }) {
1416
1461
  <button
1417
1462
  type="button"
1418
1463
  onClick={onSignOut}
1419
- className="flex w-full items-center px-3 py-2 text-left text-[13px] text-zinc-700 transition-colors hover:bg-zinc-50"
1464
+ className="flex w-full items-center gap-2 px-3 py-2 text-left text-[13px] text-zinc-700 transition-colors hover:bg-zinc-50"
1420
1465
  >
1466
+ <LogOut aria-hidden className="size-3.5" />
1421
1467
  Sign out
1422
1468
  </button>
1423
1469
  </div>
@@ -1,5 +1,6 @@
1
1
  import React, { use } from "react";
2
2
  import { Link, type Metadata, type PageProps } from "@pylonsync/react";
3
+ import { ArrowUpRight } from "lucide-react";
3
4
  import { siteConfig } from "@/lib/site.config";
4
5
  import { UserMenu, AgencyDashboard } from "./dashboard-client";
5
6
 
@@ -35,35 +36,32 @@ export default function DashboardPage({ auth, response, serverData }: PageProps)
35
36
  );
36
37
  }
37
38
 
38
- // Dashboard chrome: a slim top bar with the logo, a link back to the public
39
- // site, and the account menu (a client island for sign-out).
39
+ // Dashboard chrome in the site's own look: white page, the studio name in the
40
+ // display serif, a link back to the site, and the account menu (a client
41
+ // island for sign-out).
40
42
  function Shell({ email, children }: { email: string; children: React.ReactNode }) {
41
43
  const { brand } = siteConfig;
42
44
  return (
43
- <div className="flex min-h-screen flex-col bg-white text-zinc-900">
45
+ <div className="flex min-h-screen flex-col bg-white text-ink">
44
46
  <header className="border-b border-zinc-200">
45
- <div className="mx-auto flex h-14 max-w-4xl items-center justify-between px-6">
46
- <div className="flex items-center gap-2">
47
- <span className="flex size-6 items-center justify-center rounded-[7px] bg-zinc-900 text-[13px] font-bold text-white">
48
- {brand.letter}
49
- </span>
50
- <span className="text-[15px] font-semibold tracking-tight">
51
- {brand.name} <span className="text-zinc-400">/ studio</span>
52
- </span>
53
- </div>
54
- <div className="flex items-center gap-4">
47
+ <div className="mx-auto flex h-16 w-full max-w-5xl items-center justify-between px-6">
48
+ <Link href="/" className="font-display text-[1.5rem] leading-none text-ink">
49
+ {brand.name}
50
+ </Link>
51
+ <div className="flex items-center gap-5">
55
52
  <Link
56
53
  href="/"
57
- className="text-[13px] text-zinc-500 transition-colors hover:text-zinc-900"
54
+ className="hidden items-center gap-1 text-[14px] text-zinc-600 transition-colors hover:text-ink sm:inline-flex"
58
55
  >
59
- View site ↗
56
+ View site
57
+ <ArrowUpRight aria-hidden className="size-3.5" />
60
58
  </Link>
61
59
  <UserMenu email={email} />
62
60
  </div>
63
61
  </div>
64
62
  </header>
65
63
  <main className="flex-1">
66
- <div className="mx-auto max-w-4xl px-6 py-8">{children}</div>
64
+ <div className="mx-auto w-full max-w-5xl px-6 py-8 sm:py-10">{children}</div>
67
65
  </main>
68
66
  </div>
69
67
  );
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="10" fill="#111111"/>
2
+ <path d="M16 14h7v14h18V14h7v36h-7V35H23v15h-7z" fill="#ffffff"/>
3
+ <circle cx="52" cy="52" r="5" fill="#22c55e"/></svg>
@@ -18,21 +18,19 @@ export default function LoginPage({ auth, response }: PageProps) {
18
18
  if (auth.user_id && !auth.user_id.startsWith("guest_")) response.redirect("/dashboard");
19
19
  const { brand } = siteConfig;
20
20
  return (
21
- <div className="flex min-h-screen items-center justify-center bg-white px-6 py-12">
22
- <div className="w-full max-w-[400px] rounded-2xl border border-zinc-200/70 p-8">
23
- <Link href="/" className="inline-flex">
24
- <span className="flex size-9 items-center justify-center rounded-xl bg-zinc-900 text-base font-bold text-white">
25
- {brand.letter}
26
- </span>
21
+ <div className="flex min-h-screen flex-col bg-white text-ink">
22
+ <div className="mx-auto flex h-16 w-full max-w-5xl items-center px-6">
23
+ <Link href="/" className="font-display text-[1.5rem] leading-none">
24
+ {brand.name}
27
25
  </Link>
28
- <h1 className="mt-5 text-[22px] font-semibold tracking-tight text-zinc-900">
29
- {brand.name} dashboard
30
- </h1>
31
- <p className="mt-1 text-[13px] text-zinc-500">
32
- Sign in to manage your inquiries.
33
- </p>
34
- <div className="mt-6">
35
- <AuthForm />
26
+ </div>
27
+ <div className="flex flex-1 items-center justify-center px-6 pb-24">
28
+ <div className="w-full max-w-[380px]">
29
+ <h1 className="font-display text-[2.75rem] leading-none tracking-[-0.015em]">Studio sign in</h1>
30
+ <p className="mt-3 text-[15px] text-zinc-600">Leads, work, clients, and invoices.</p>
31
+ <div className="mt-8">
32
+ <AuthForm />
33
+ </div>
36
34
  </div>
37
35
  </div>
38
36
  </div>
@@ -41,11 +41,16 @@ export function SectionTitle({ title, body }: { title: string; body?: string })
41
41
  // A linked portfolio entry: the 4:3 cover, then the title, client label, and
42
42
  // a one-line summary in small type. Used on the homepage work grid and the
43
43
  // full /work index, so both stay in lockstep.
44
- export function ProjectCard({ p }: { p: ProjectView }) {
44
+ // `wide` spans both grid columns with a wider cover (see isWideCard).
45
+ export function ProjectCard({ p, wide = false }: { p: ProjectView; wide?: boolean }) {
45
46
  return (
46
- <Link href={`/work/${p.slug}`} className="group block">
47
+ <Link href={`/work/${p.slug}`} className={"group block" + (wide ? " sm:col-span-2" : "")}>
47
48
  {/* Case-study cover: public/images/work/<slug>.jpg. */}
48
- <ImagePlaceholder shape="landscape" title={`${p.title} product shot`} src={`/images/work/${p.slug}.jpg`} />
49
+ <ImagePlaceholder
50
+ shape={wide ? "wide" : "landscape"}
51
+ title={`${p.title} product shot`}
52
+ src={`/images/work/${p.slug}.jpg`}
53
+ />
49
54
  <div className="mt-4 flex items-baseline justify-between gap-4">
50
55
  <h3 className="font-display text-[1.5rem] leading-none text-ink transition-colors group-hover:text-brand">
51
56
  {p.title}
@@ -73,7 +78,7 @@ export function ImagePlaceholder({
73
78
  src,
74
79
  className = "",
75
80
  }: {
76
- shape?: "landscape" | "portrait" | "square" | "circle";
81
+ shape?: "landscape" | "wide" | "portrait" | "square" | "circle";
77
82
  title: string;
78
83
  hint?: string;
79
84
  /** A real image. The template ships one under public/images; swap it for yours. */
@@ -85,7 +90,9 @@ export function ImagePlaceholder({
85
90
  ? "aspect-[3/4]"
86
91
  : shape === "square" || shape === "circle"
87
92
  ? "aspect-square"
88
- : "aspect-[4/3]";
93
+ : shape === "wide"
94
+ ? "aspect-[4/3] sm:aspect-[2/1]"
95
+ : "aspect-[4/3]";
89
96
  const radius = shape === "circle" ? "rounded-full" : "rounded-sm";
90
97
  if (src) {
91
98
  return (