@aglyn/plugins-ai 1.0.0-beta.226 → 1.0.0-beta.228

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 (203) hide show
  1. package/README.md +3 -2
  2. package/package.json +17 -17
  3. package/src/lib/billing/ai-month-meter.js +5 -3
  4. package/src/lib/billing/ai-month-meter.js.map +1 -1
  5. package/src/lib/billing/ai-overage-ledger.js +3 -1
  6. package/src/lib/billing/ai-overage-ledger.js.map +1 -1
  7. package/src/lib/components/ai-brief-dialog.component.d.ts +6 -2
  8. package/src/lib/components/ai-brief-dialog.component.js +36 -18
  9. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  10. package/src/lib/components/ai-describe-automation.component.js +24 -4
  11. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  12. package/src/lib/components/ai-describe-button.component.js +6 -1
  13. package/src/lib/components/ai-describe-button.component.js.map +1 -1
  14. package/src/lib/components/ai-describe-page.component.d.ts +1 -1
  15. package/src/lib/components/ai-describe-page.component.js +1 -1
  16. package/src/lib/components/ai-describe-page.component.js.map +1 -1
  17. package/src/lib/components/ai-insight-dialog.component.js +24 -2
  18. package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
  19. package/src/lib/components/ai-job-events.d.ts +20 -0
  20. package/src/lib/components/ai-job-events.js +78 -0
  21. package/src/lib/components/ai-job-events.js.map +1 -0
  22. package/src/lib/components/ai-job-follow.component.d.ts +49 -0
  23. package/src/lib/components/ai-job-follow.component.js +210 -0
  24. package/src/lib/components/ai-job-follow.component.js.map +1 -0
  25. package/src/lib/components/ai-job-links.d.ts +42 -0
  26. package/src/lib/components/ai-job-links.js +140 -0
  27. package/src/lib/components/ai-job-links.js.map +1 -0
  28. package/src/lib/components/ai-job-plan.component.d.ts +13 -6
  29. package/src/lib/components/ai-job-plan.component.js +29 -3
  30. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  31. package/src/lib/components/ai-job-requests.d.ts +24 -0
  32. package/src/lib/components/ai-job-requests.js +76 -0
  33. package/src/lib/components/ai-job-requests.js.map +1 -0
  34. package/src/lib/components/ai-job-site.d.ts +22 -0
  35. package/src/lib/components/ai-job-site.js +73 -0
  36. package/src/lib/components/ai-job-site.js.map +1 -0
  37. package/src/lib/components/ai-jobs-indicator.component.d.ts +25 -0
  38. package/src/lib/components/ai-jobs-indicator.component.js +80 -0
  39. package/src/lib/components/ai-jobs-indicator.component.js.map +1 -0
  40. package/src/lib/components/ai-jobs-list-page.component.d.ts +23 -0
  41. package/src/lib/components/ai-jobs-list-page.component.js +303 -0
  42. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -0
  43. package/src/lib/components/ai-jobs-page.component.d.ts +7 -0
  44. package/src/lib/components/ai-jobs-page.component.js +30 -0
  45. package/src/lib/components/ai-jobs-page.component.js.map +1 -0
  46. package/src/lib/components/ai-jobs-store.d.ts +81 -0
  47. package/src/lib/components/ai-jobs-store.js +218 -0
  48. package/src/lib/components/ai-jobs-store.js.map +1 -0
  49. package/src/lib/components/ai-page-brief-dialog.component.d.ts +4 -4
  50. package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -1
  51. package/src/lib/components/ai-site-build-page.component.d.ts +51 -0
  52. package/src/lib/components/ai-site-build-page.component.js +495 -0
  53. package/src/lib/components/ai-site-build-page.component.js.map +1 -0
  54. package/src/lib/components/ai-site-start-card.component.d.ts +9 -1
  55. package/src/lib/components/ai-site-start-card.component.js +533 -132
  56. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  57. package/src/lib/components/ai-site-starter-fallback.component.d.ts +24 -0
  58. package/src/lib/components/ai-site-starter-fallback.component.js +112 -0
  59. package/src/lib/components/ai-site-starter-fallback.component.js.map +1 -0
  60. package/src/lib/components/assist-jobs-drawer.component.d.ts +18 -20
  61. package/src/lib/components/assist-jobs-drawer.component.js +152 -172
  62. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  63. package/src/lib/components/assist-panel.component.js +69 -8
  64. package/src/lib/components/assist-panel.component.js.map +1 -1
  65. package/src/lib/components/staff-ai-credit-return.component.d.ts +55 -0
  66. package/src/lib/components/staff-ai-credit-return.component.js +346 -0
  67. package/src/lib/components/staff-ai-credit-return.component.js.map +1 -0
  68. package/src/lib/components/staff-org-ai-card.component.js +364 -279
  69. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  70. package/src/lib/components/staff-user-ai-usage-card.component.d.ts +4 -0
  71. package/src/lib/components/staff-user-ai-usage-card.component.js +83 -28
  72. package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -1
  73. package/src/lib/components/use-ai-job-run.js +5 -18
  74. package/src/lib/components/use-ai-job-run.js.map +1 -1
  75. package/src/lib/jobs/ai-job-auto-confirm.d.ts +52 -0
  76. package/src/lib/jobs/ai-job-auto-confirm.js +62 -0
  77. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -0
  78. package/src/lib/jobs/ai-job-draft-ids.d.ts +6 -0
  79. package/src/lib/jobs/ai-job-draft-ids.js +9 -0
  80. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  81. package/src/lib/jobs/ai-job-drafts.d.ts +7 -0
  82. package/src/lib/jobs/ai-job-drafts.js +16 -7
  83. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  84. package/src/lib/jobs/ai-job-layout-step.js +2 -1
  85. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  86. package/src/lib/jobs/ai-job-page-sections.d.ts +6 -5
  87. package/src/lib/jobs/ai-job-page-sections.js +3 -3
  88. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  89. package/src/lib/jobs/ai-job-page-step.d.ts +19 -1
  90. package/src/lib/jobs/ai-job-page-step.js +50 -8
  91. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  92. package/src/lib/jobs/ai-job-plan-step.d.ts +33 -2
  93. package/src/lib/jobs/ai-job-plan-step.js +167 -18
  94. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  95. package/src/lib/jobs/ai-job-site-step.d.ts +28 -0
  96. package/src/lib/jobs/ai-job-site-step.js +130 -21
  97. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  98. package/src/lib/jobs/ai-job-text-step.d.ts +14 -1
  99. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  100. package/src/lib/jobs/ai-jobs-notify.d.ts +37 -0
  101. package/src/lib/jobs/ai-jobs-notify.js +51 -0
  102. package/src/lib/jobs/ai-jobs-notify.js.map +1 -0
  103. package/src/lib/jobs/ai-jobs.d.ts +39 -4
  104. package/src/lib/jobs/ai-jobs.js +205 -34
  105. package/src/lib/jobs/ai-jobs.js.map +1 -1
  106. package/src/lib/jobs/ai-site-publish.d.ts +98 -0
  107. package/src/lib/jobs/ai-site-publish.js +372 -0
  108. package/src/lib/jobs/ai-site-publish.js.map +1 -0
  109. package/src/lib/jobs/fixtures/ai-page-briefs.d.ts +7 -6
  110. package/src/lib/jobs/fixtures/ai-page-briefs.js +23 -56
  111. package/src/lib/jobs/fixtures/ai-page-briefs.js.map +1 -1
  112. package/src/lib/model/ai-job-activity.d.ts +112 -0
  113. package/src/lib/model/ai-job-activity.js +144 -0
  114. package/src/lib/model/ai-job-activity.js.map +1 -0
  115. package/src/lib/model/ai-job-failure-copy.d.ts +69 -0
  116. package/src/lib/model/ai-job-failure-copy.js +78 -0
  117. package/src/lib/model/ai-job-failure-copy.js.map +1 -0
  118. package/src/lib/model/ai-job-notice.d.ts +63 -0
  119. package/src/lib/model/ai-job-notice.js +129 -0
  120. package/src/lib/model/ai-job-notice.js.map +1 -0
  121. package/src/lib/model/ai-jobs.types.d.ts +53 -0
  122. package/src/lib/model/ai-jobs.types.js +1 -0
  123. package/src/lib/model/ai-jobs.types.js.map +1 -1
  124. package/src/lib/model/ai-plan-capabilities.d.ts +32 -10
  125. package/src/lib/model/ai-plan-capabilities.js +15 -2
  126. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  127. package/src/lib/model/ai-site-build-progress.d.ts +61 -0
  128. package/src/lib/model/ai-site-build-progress.js +136 -0
  129. package/src/lib/model/ai-site-build-progress.js.map +1 -0
  130. package/src/lib/model/ai-site-inventory.d.ts +9 -0
  131. package/src/lib/model/ai-site-inventory.js +7 -2
  132. package/src/lib/model/ai-site-inventory.js.map +1 -1
  133. package/src/lib/model/ai-site-job.d.ts +93 -2
  134. package/src/lib/model/ai-site-job.js +88 -7
  135. package/src/lib/model/ai-site-job.js.map +1 -1
  136. package/src/lib/model/ai-site-start-seo.d.ts +19 -0
  137. package/src/lib/model/ai-site-start-seo.js +91 -3
  138. package/src/lib/model/ai-site-start-seo.js.map +1 -1
  139. package/src/lib/model/ai-site-start.d.ts +3 -1
  140. package/src/lib/model/ai-site-start.js +6 -4
  141. package/src/lib/model/ai-site-start.js.map +1 -1
  142. package/src/lib/plan-entitlements.d.ts +3 -3
  143. package/src/lib/plan-entitlements.js +3 -3
  144. package/src/lib/plan-entitlements.js.map +1 -1
  145. package/src/lib/plugin.js +38 -2
  146. package/src/lib/plugin.js.map +1 -1
  147. package/src/lib/runtime/ai-abuse-guards.d.ts +9 -6
  148. package/src/lib/runtime/ai-abuse-guards.js +10 -7
  149. package/src/lib/runtime/ai-abuse-guards.js.map +1 -1
  150. package/src/lib/runtime/ai-doctrine-validators.d.ts +16 -12
  151. package/src/lib/runtime/ai-doctrine-validators.js +50 -40
  152. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  153. package/src/lib/runtime/ai-doctrine.js +1 -1
  154. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  155. package/src/lib/runtime/ai-eval-live.d.ts +1 -1
  156. package/src/lib/runtime/ai-eval-live.js +1 -1
  157. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  158. package/src/lib/runtime/ai-gate.d.ts +3 -2
  159. package/src/lib/runtime/ai-gate.js +3 -2
  160. package/src/lib/runtime/ai-gate.js.map +1 -1
  161. package/src/lib/runtime/site-inventory.d.ts +16 -1
  162. package/src/lib/runtime/site-inventory.js +43 -3
  163. package/src/lib/runtime/site-inventory.js.map +1 -1
  164. package/src/lib/server/ai-admin-credits.d.ts +21 -0
  165. package/src/lib/server/ai-admin-credits.js +213 -0
  166. package/src/lib/server/ai-admin-credits.js.map +1 -0
  167. package/src/lib/server/ai-admin-org.js +12 -5
  168. package/src/lib/server/ai-admin-org.js.map +1 -1
  169. package/src/lib/server/ai-admin-signals.js +3 -1
  170. package/src/lib/server/ai-admin-signals.js.map +1 -1
  171. package/src/lib/server/ai-admin-user.js +12 -1
  172. package/src/lib/server/ai-admin-user.js.map +1 -1
  173. package/src/lib/server/ai-allotments.js +3 -2
  174. package/src/lib/server/ai-allotments.js.map +1 -1
  175. package/src/lib/server/ai-jobs-live-sites.d.ts +26 -0
  176. package/src/lib/server/ai-jobs-live-sites.js +58 -0
  177. package/src/lib/server/ai-jobs-live-sites.js.map +1 -0
  178. package/src/lib/server/ai-jobs-route.js +42 -5
  179. package/src/lib/server/ai-jobs-route.js.map +1 -1
  180. package/src/lib/server/assist-chat.js +3 -2
  181. package/src/lib/server/assist-chat.js.map +1 -1
  182. package/src/lib/server/billing-credits.js +4 -3
  183. package/src/lib/server/billing-credits.js.map +1 -1
  184. package/src/lib/server/billing-overage.js +3 -2
  185. package/src/lib/server/billing-overage.js.map +1 -1
  186. package/src/lib/server.js +11 -0
  187. package/src/lib/server.js.map +1 -1
  188. package/src/lib/usage/assist-credit-returns-write.d.ts +75 -0
  189. package/src/lib/usage/assist-credit-returns-write.js +150 -0
  190. package/src/lib/usage/assist-credit-returns-write.js.map +1 -0
  191. package/src/lib/usage/assist-credit-returns.d.ts +68 -0
  192. package/src/lib/usage/assist-credit-returns.js +79 -0
  193. package/src/lib/usage/assist-credit-returns.js.map +1 -0
  194. package/src/lib/usage/assist-free-taste.js +6 -3
  195. package/src/lib/usage/assist-free-taste.js.map +1 -1
  196. package/src/lib/usage/assist-job-refund.d.ts +100 -0
  197. package/src/lib/usage/assist-job-refund.js +109 -0
  198. package/src/lib/usage/assist-job-refund.js.map +1 -0
  199. package/src/lib/usage/assist-usage.js +5 -2
  200. package/src/lib/usage/assist-usage.js.map +1 -1
  201. package/src/lib/usage/staff-org-ai.d.ts +31 -1
  202. package/src/lib/usage/staff-org-ai.js +19 -2
  203. package/src/lib/usage/staff-org-ai.js.map +1 -1
@@ -16,36 +16,176 @@
16
16
  */ 'use client';
17
17
  import { _ as _extends } from "@swc/helpers/_/_extends";
18
18
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
19
- import { lockdownRefusalText, parseLockdownRefusal } from "@aglyn/aglyn";
19
+ import { lockdownRefusalText, parseLockdownRefusal, PLATFORM_BRAND_NAME } from "@aglyn/aglyn";
20
+ import { trackEvent } from "@aglyn/aglyn/app-utils/analytics-events";
20
21
  import { ICON_VARIANT_CLOSE } from "@aglyn/shared-data-enums";
22
+ import { mdiCheckCircle, mdiCreation, mdiInformationOutline, mdiPageLayoutHeaderFooter } from "@aglyn/shared-data-mdi";
21
23
  import { MdiIcon } from "@aglyn/shared-ui-jsx";
22
24
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
23
25
  import { useUser } from "@aglyn/tenant-feature-instance";
24
- import { Alert, AppBar, Box, Button, Chip, Container, Dialog, DialogActions, DialogContent, Divider, IconButton, MenuItem, Stack, TextField, Toolbar, Typography } from "@mui/material";
25
- import { useCallback, useEffect, useRef, useState } from "react";
26
- import { AI_SITE_PAGES, AI_SITE_SUBMISSION_CHOICES, aiSiteCreditEstimate } from "../model/ai-site-job.js";
26
+ import { Alert, AppBar, Box, Button, Card, CardActionArea, Chip, CircularProgress, Container, Dialog, DialogActions, DialogContent, IconButton, MenuItem, Stack, Step, StepLabel, Stepper, TextField, Toolbar, Tooltip, Typography } from "@mui/material";
27
+ import { alpha } from "@mui/material/styles";
28
+ import { useRouter } from "next/navigation";
29
+ import { useCallback, useEffect, useId, useRef, useState } from "react";
30
+ import { AI_JOB_AUTO_CONFIRM_INPUT, AI_SITE_FREE_PAGES, AI_SITE_FREE_PAGES_NOTE, AI_SITE_SUBMISSION_CHOICES, aiFreeSiteCreditEstimate, aiSiteCreditEstimate, aiSitePagesBand } from "../model/ai-site-job.js";
31
+ import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
27
32
  import { AI_SITE_START_ANSWERS, AI_SITE_START_EXAMPLES, AI_SITE_START_TYPES, aiSiteStartBrief, aiSiteStartInputs, aiSiteStartRefusal } from "../model/ai-site-start.js";
33
+ import { AiJobFollow } from "./ai-job-follow.component.js";
34
+ import { aiSiteBuildHref } from "./ai-job-links.js";
35
+ import { publishAiJob } from "./ai-jobs-store.js";
28
36
  /** Labels the dialog for a reader, from its own heading. */ const TITLE_ID = 'ai-site-start-title';
29
- export function AiSiteStartCard({ hostId, orgId, startBlank }) {
37
+ /**
38
+ * The takeover's surface, for its paper, its bar and its footer alike: the
39
+ * console page's own background (AGL-3596). The paper sits at elevation 0,
40
+ * which is what keeps MUI's dark-mode elevation overlay off it.
41
+ */ export const AI_SITE_START_SURFACE_SX = {
42
+ bgcolor: 'background.default'
43
+ };
44
+ /** The two steps, as the indicator under the header names them. */ const STEP_LABELS = [
45
+ 'Choose',
46
+ 'Describe'
47
+ ];
48
+ /** The soft round badge an icon sits in, in its accent's tint. */ function IconBadge({ accent, children }) {
49
+ return /*#__PURE__*/ _jsx(Box, {
50
+ "aria-hidden": true,
51
+ sx: (theme)=>({
52
+ width: theme.spacing(9),
53
+ height: theme.spacing(9),
54
+ borderRadius: '50%',
55
+ display: 'flex',
56
+ alignItems: 'center',
57
+ justifyContent: 'center',
58
+ bgcolor: alpha(theme.palette[accent].main, theme.palette.mode === 'dark' ? 0.24 : 0.12),
59
+ color: `${accent}.main`,
60
+ fontSize: theme.spacing(5.5)
61
+ }),
62
+ children: children
63
+ });
64
+ }
65
+ /**
66
+ * One way to start, as a card the whole of which is the button. Its accessible
67
+ * name is its title; its description describes it.
68
+ */ function ChoiceCard({ accent, icon, iconId, title, description, busy = false, onChoose, children }) {
69
+ const id = useId();
70
+ return /*#__PURE__*/ _jsx(Card, {
71
+ variant: "outlined",
72
+ sx: (theme)=>({
73
+ borderRadius: 2,
74
+ height: '100%',
75
+ transition: theme.transitions.create([
76
+ 'border-color',
77
+ 'box-shadow',
78
+ 'transform'
79
+ ], {
80
+ duration: theme.transitions.duration.shorter
81
+ }),
82
+ '&:hover, &:focus-within': {
83
+ borderColor: `${accent}.main`,
84
+ boxShadow: theme.shadows[4],
85
+ transform: `translateY(-${theme.spacing(0.25)})`
86
+ },
87
+ '@media (prefers-reduced-motion: reduce)': {
88
+ transition: 'none',
89
+ '&:hover, &:focus-within': {
90
+ transform: 'none'
91
+ }
92
+ }
93
+ }),
94
+ children: /*#__PURE__*/ _jsx(CardActionArea, {
95
+ onClick: onChoose,
96
+ disabled: busy,
97
+ "aria-labelledby": `${id}-title`,
98
+ "aria-describedby": `${id}-description`,
99
+ "aria-busy": busy || undefined,
100
+ sx: {
101
+ height: '100%',
102
+ p: {
103
+ xs: 3,
104
+ sm: 4
105
+ }
106
+ },
107
+ children: /*#__PURE__*/ _jsxs(Stack, {
108
+ spacing: 2,
109
+ sx: {
110
+ alignItems: 'center',
111
+ textAlign: 'center'
112
+ },
113
+ children: [
114
+ /*#__PURE__*/ _jsx(IconBadge, {
115
+ accent: accent,
116
+ children: busy ? /*#__PURE__*/ _jsx(CircularProgress, {
117
+ size: "1em",
118
+ color: accent,
119
+ "aria-label": `${title}…`
120
+ }) : /*#__PURE__*/ _jsx(MdiIcon, {
121
+ path: icon,
122
+ "data-icon": iconId,
123
+ "aria-hidden": true
124
+ })
125
+ }),
126
+ /*#__PURE__*/ _jsx(Typography, {
127
+ id: `${id}-title`,
128
+ variant: "h6",
129
+ component: "h2",
130
+ children: title
131
+ }),
132
+ /*#__PURE__*/ _jsx(Typography, {
133
+ id: `${id}-description`,
134
+ variant: "body2",
135
+ color: "text.secondary",
136
+ children: description
137
+ }),
138
+ children
139
+ ]
140
+ })
141
+ })
142
+ });
143
+ }
144
+ /** A small label above a group of questions. */ function SectionLabel({ children }) {
145
+ return /*#__PURE__*/ _jsx(Typography, {
146
+ variant: "overline",
147
+ color: "text.secondary",
148
+ component: "h2",
149
+ sx: {
150
+ display: 'block'
151
+ },
152
+ children: children
153
+ });
154
+ }
155
+ export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leave }) {
30
156
  var _ref;
31
157
  const { data: user } = useUser();
158
+ const router = useRouter();
32
159
  // Held in a ref so a request reads WHO is signed in, never the identity of
33
160
  // the object that says so.
34
161
  const userRef = useRef(user);
35
162
  userRef.current = user;
36
163
  const uid = (_ref = user == null ? void 0 : user.uid) != null ? _ref : null;
37
164
  const [verdict, setVerdict] = useState('checking');
165
+ const [step, setStep] = useState('choose');
38
166
  const [answers, setAnswers] = useState(AI_SITE_START_ANSWERS);
39
167
  const [busy, setBusy] = useState(false);
168
+ // The starter card's own busy state while `startBlank` runs (AGL-3594).
169
+ const [startingStarter, setStartingStarter] = useState(false);
40
170
  const [notice, setNotice] = useState(null);
41
- const [started, setStarted] = useState(false);
171
+ /** The site job "Plan my site" started, as the create door answered with it. */ const [started, setStarted] = useState(null);
172
+ // The Free taste's page band (AGL-3594), read off the verdict request.
173
+ const [freeTaste, setFreeTaste] = useState(false);
42
174
  useEffect(()=>{
43
175
  if (!orgId || !uid) return;
44
176
  let active = true;
45
177
  void (async ()=>{
46
178
  try {
47
179
  const response = await authorizedFetch(userRef.current, `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1`);
180
+ const payload = response.ok ? await response.json().catch(()=>null) : null;
48
181
  if (active) {
182
+ if ((payload == null ? void 0 : payload.freeTaste) === true) {
183
+ setFreeTaste(true);
184
+ setAnswers((current)=>_extends({}, current, {
185
+ pages: AI_SITE_FREE_PAGES.max,
186
+ welcomeEmail: false
187
+ }));
188
+ }
49
189
  setVerdict(response.status === 404 || response.status === 403 ? 'hidden' : 'ready');
50
190
  }
51
191
  } catch (unused) {
@@ -60,12 +200,18 @@ export function AiSiteStartCard({ hostId, orgId, startBlank }) {
60
200
  uid
61
201
  ]);
62
202
  const answer = useCallback((patch)=>setAnswers((current)=>_extends({}, current, patch)), []);
63
- const refusal = aiSiteStartRefusal(answers);
203
+ const refusal = aiSiteStartRefusal(answers, {
204
+ freeTaste
205
+ });
206
+ const band = aiSitePagesBand(freeTaste);
64
207
  const plan = useCallback(async ()=>{
65
- if (!orgId || aiSiteStartRefusal(answers)) return;
208
+ if (!orgId || aiSiteStartRefusal(answers, {
209
+ freeTaste
210
+ })) return;
66
211
  setBusy(true);
67
212
  setNotice(null);
68
213
  try {
214
+ var _ref;
69
215
  const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {
70
216
  method: 'POST',
71
217
  headers: {
@@ -76,17 +222,38 @@ export function AiSiteStartCard({ hostId, orgId, startBlank }) {
76
222
  hostId,
77
223
  kind: 'site',
78
224
  brief: aiSiteStartBrief(answers),
79
- inputs: aiSiteStartInputs(answers)
225
+ // The guided start confirms its own plan (AGL-3594): the build
226
+ // follows the plan with no approval to make.
227
+ inputs: _extends({}, aiSiteStartInputs(answers), {
228
+ [AI_JOB_AUTO_CONFIRM_INPUT]: true
229
+ })
80
230
  })
81
231
  });
82
232
  const payload = await response.json().catch(()=>null);
83
233
  if (!response.ok) {
84
- var _ref;
234
+ var _ref1;
85
235
  const locked = parseLockdownRefusal(response.status, payload);
86
- setNotice(locked ? lockdownRefusalText(locked) : String((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'The site could not be started. Try again.'));
236
+ setNotice(locked ? lockdownRefusalText(locked) : String((_ref1 = payload == null ? void 0 : payload.error) != null ? _ref1 : 'The site could not be started. Try again.'));
237
+ return;
238
+ }
239
+ const created = (_ref = payload == null ? void 0 : payload.job) != null ? _ref : null;
240
+ if (!created) {
241
+ setNotice('The site could not be started. Try again.');
87
242
  return;
88
243
  }
89
- setStarted(true);
244
+ // The indicator and the launcher count it from the moment it exists.
245
+ publishAiJob(created);
246
+ // Where the next step is (AGL-3594): the site's "Building your site"
247
+ // page for this job. The zone closes without the starter first, so the
248
+ // setup page does not ask again behind it.
249
+ const href = aiSiteBuildHref(orgSlug, host, created.id);
250
+ if (href) {
251
+ ;
252
+ (leave != null ? leave : ()=>undefined)();
253
+ router.push(href);
254
+ return;
255
+ }
256
+ setStarted(created);
90
257
  } catch (unused) {
91
258
  setNotice('The site could not be started. Try again.');
92
259
  } finally{
@@ -95,28 +262,73 @@ export function AiSiteStartCard({ hostId, orgId, startBlank }) {
95
262
  }, [
96
263
  orgId,
97
264
  hostId,
98
- answers
265
+ answers,
266
+ freeTaste,
267
+ orgSlug,
268
+ host,
269
+ leave,
270
+ router
271
+ ]);
272
+ const chooseStarter = useCallback(()=>{
273
+ setStartingStarter(true);
274
+ trackEvent('site_start_choice', {
275
+ choice: 'starter'
276
+ });
277
+ startBlank();
278
+ }, [
279
+ startBlank
99
280
  ]);
281
+ const chooseAi = useCallback(()=>{
282
+ trackEvent('site_start_choice', {
283
+ choice: 'ai'
284
+ });
285
+ setStep('describe');
286
+ }, []);
100
287
  if (verdict !== 'ready') return null;
101
- const estimate = aiSiteCreditEstimate(answers.pages, {
288
+ // Before a job is started every way out is the blank site, which writes the
289
+ // starter (AGL-3594); after, it only closes — the job builds the site.
290
+ const exit = started ? leave != null ? leave : startBlank : startBlank;
291
+ const estimate = freeTaste ? aiFreeSiteCreditEstimate(answers.pages) : aiSiteCreditEstimate(answers.pages, {
102
292
  welcomeEmail: answers.welcomeEmail
103
293
  });
294
+ const estimateText = freeTaste ? `Up to about ${estimate.toLocaleString('en-US')} of the ${FREE_AI_TASTE_CREDITS_PER_MONTH} AI credits your Free workspace has each month` : `About ${estimate.toLocaleString('en-US')} credits, estimated`;
295
+ const choosing = step === 'choose' && !started;
104
296
  return /*#__PURE__*/ _jsxs(Dialog, {
105
297
  open: true,
106
298
  fullScreen: true,
107
- onClose: startBlank,
299
+ onClose: exit,
108
300
  "aria-labelledby": TITLE_ID,
301
+ // The console's own page surface, in both modes. A Dialog's paper sits
302
+ // at elevation 24 by default, and in dark mode MUI lightens an elevated
303
+ // paper with a white overlay, which turned the whole takeover a
304
+ // washed-out gray; the overlay is for floating surfaces, and this one
305
+ // fills the screen, so its paper is flat.
306
+ slotProps: {
307
+ paper: {
308
+ elevation: 0,
309
+ sx: AI_SITE_START_SURFACE_SX
310
+ }
311
+ },
109
312
  children: [
110
313
  /*#__PURE__*/ _jsx(AppBar, {
111
314
  position: "relative",
112
- color: "surface",
315
+ color: "inherit",
316
+ elevation: 0,
113
317
  enableColorOnDark: true,
318
+ sx: _extends({}, AI_SITE_START_SURFACE_SX, {
319
+ borderBottom: 1,
320
+ borderColor: 'divider'
321
+ }),
114
322
  children: /*#__PURE__*/ _jsxs(Toolbar, {
323
+ variant: "dense",
324
+ sx: {
325
+ gap: 1
326
+ },
115
327
  children: [
116
328
  /*#__PURE__*/ _jsx(IconButton, {
117
329
  edge: "start",
118
330
  color: "inherit",
119
- onClick: startBlank,
331
+ onClick: exit,
120
332
  "aria-label": "Close the guided start",
121
333
  children: /*#__PURE__*/ _jsx(MdiIcon, {
122
334
  path: ICON_VARIANT_CLOSE.path
@@ -124,190 +336,336 @@ export function AiSiteStartCard({ hostId, orgId, startBlank }) {
124
336
  }),
125
337
  /*#__PURE__*/ _jsx(Typography, {
126
338
  id: TITLE_ID,
127
- variant: "h6",
339
+ variant: "subtitle1",
128
340
  component: "div",
129
341
  noWrap: true,
130
342
  sx: {
131
343
  textOverflow: 'ellipsis',
132
- ml: 2,
133
344
  flex: 1
134
345
  },
135
- children: 'Start this site with AI'
136
- }),
137
- /*#__PURE__*/ _jsx(Divider, {
138
- sx: {
139
- height: 28,
140
- m: 0.5
141
- },
142
- orientation: "vertical"
346
+ children: 'Start your site'
143
347
  }),
144
348
  /*#__PURE__*/ _jsx(Button, {
145
349
  color: "inherit",
146
- onClick: startBlank,
147
- children: started ? 'Close' : 'Skip and start blank'
350
+ onClick: exit,
351
+ "aria-label": started ? 'Close' : 'Skip and start blank',
352
+ children: started ? 'Close' : 'Skip'
148
353
  })
149
354
  ]
150
355
  })
151
356
  }),
152
357
  /*#__PURE__*/ _jsx(DialogContent, {
358
+ sx: {
359
+ px: {
360
+ xs: 2,
361
+ sm: 3
362
+ }
363
+ },
153
364
  children: /*#__PURE__*/ _jsx(Container, {
154
- maxWidth: "md",
365
+ maxWidth: choosing ? 'md' : 'sm',
155
366
  disableGutters: true,
156
367
  sx: {
157
- py: 2
368
+ py: {
369
+ xs: 3,
370
+ sm: 5
371
+ }
158
372
  },
159
373
  children: /*#__PURE__*/ _jsxs(Stack, {
160
- spacing: 3,
374
+ spacing: 4,
161
375
  children: [
162
- /*#__PURE__*/ _jsx(Typography, {
163
- variant: "body2",
164
- color: "text.secondary",
165
- children: 'Answer a few questions and AI plans the whole site — its pages, its ' + 'navigation, its layout and a contact form. Nothing is built until you ' + 'confirm the plan, and everything it builds is an unpublished draft.'
376
+ /*#__PURE__*/ _jsx(Stepper, {
377
+ activeStep: choosing ? 0 : 1,
378
+ "aria-label": choosing ? 'Step 1 of 2' : 'Step 2 of 2',
379
+ sx: {
380
+ maxWidth: (theme)=>theme.spacing(40),
381
+ width: '100%',
382
+ mx: 'auto'
383
+ },
384
+ children: STEP_LABELS.map((label)=>/*#__PURE__*/ _jsx(Step, {
385
+ children: /*#__PURE__*/ _jsx(StepLabel, {
386
+ children: label
387
+ })
388
+ }, label))
389
+ }),
390
+ /*#__PURE__*/ _jsxs(Stack, {
391
+ spacing: 1,
392
+ sx: {
393
+ textAlign: choosing ? 'center' : 'left'
394
+ },
395
+ children: [
396
+ /*#__PURE__*/ _jsx(Typography, {
397
+ variant: "h4",
398
+ component: "h1",
399
+ children: choosing ? 'How do you want to start?' : 'Tell us about your site'
400
+ }),
401
+ /*#__PURE__*/ _jsx(Typography, {
402
+ variant: "body1",
403
+ color: "text.secondary",
404
+ children: choosing ? 'You can change everything later.' : `A few answers and ${PLATFORM_BRAND_NAME} AI plans and writes your pages, then publishes your site.`
405
+ })
406
+ ]
166
407
  }),
167
408
  notice && /*#__PURE__*/ _jsx(Alert, {
168
409
  severity: "info",
169
410
  children: notice
170
411
  }),
171
- started ? /*#__PURE__*/ _jsx(Alert, {
172
- severity: "success",
173
- children: 'Your site is being planned. Open AI jobs in the Assist panel to read the plan ' + 'and confirm it — nothing is built, and nothing is published, until you do.'
412
+ choosing ? /*#__PURE__*/ _jsxs(Box, {
413
+ sx: {
414
+ display: 'grid',
415
+ gridTemplateColumns: {
416
+ xs: '1fr',
417
+ sm: '1fr 1fr'
418
+ },
419
+ gap: 3
420
+ },
421
+ children: [
422
+ /*#__PURE__*/ _jsx(ChoiceCard, {
423
+ accent: "secondary",
424
+ icon: mdiPageLayoutHeaderFooter.path,
425
+ iconId: "page-layout-header-footer",
426
+ title: "Start from the starter site",
427
+ description: "A ready-made home page with a header, footer and contact form you can edit.",
428
+ busy: startingStarter,
429
+ onChoose: chooseStarter
430
+ }),
431
+ /*#__PURE__*/ _jsxs(ChoiceCard, {
432
+ accent: "primary",
433
+ icon: mdiCreation.path,
434
+ iconId: "creation",
435
+ title: "Start with AI",
436
+ description: `Answer a few questions and ${PLATFORM_BRAND_NAME} AI plans your pages.`,
437
+ onChoose: chooseAi,
438
+ children: [
439
+ /*#__PURE__*/ _jsx(Chip, {
440
+ size: "small",
441
+ color: "primary",
442
+ label: "Recommended"
443
+ }),
444
+ freeTaste && /*#__PURE__*/ _jsx(Typography, {
445
+ variant: "caption",
446
+ color: "text.secondary",
447
+ children: `Up to ${AI_SITE_FREE_PAGES.max} pages on the Free plan`
448
+ })
449
+ ]
450
+ })
451
+ ]
452
+ }) : started && orgId ? /*#__PURE__*/ _jsx(Card, {
453
+ variant: "outlined",
454
+ sx: {
455
+ borderRadius: 2,
456
+ p: {
457
+ xs: 2,
458
+ sm: 3
459
+ }
460
+ },
461
+ children: /*#__PURE__*/ _jsx(AiJobFollow, {
462
+ job: started,
463
+ orgId: orgId,
464
+ orgSlug: orgSlug,
465
+ user: user,
466
+ intro: 'Your site is being planned. Review the plan here or in AI jobs, and confirm ' + 'it to build — nothing is built, and nothing is published, until you do.',
467
+ // Full screen: AI jobs opens in the panel this dialog covers.
468
+ onOpenJobs: exit
469
+ })
174
470
  }) : /*#__PURE__*/ _jsxs(_Fragment, {
175
471
  children: [
176
- /*#__PURE__*/ _jsxs(Box, {
472
+ /*#__PURE__*/ _jsxs(Stack, {
473
+ spacing: 2,
474
+ component: "section",
475
+ "aria-label": "Your business",
177
476
  children: [
477
+ /*#__PURE__*/ _jsx(SectionLabel, {
478
+ children: 'Your business'
479
+ }),
178
480
  /*#__PURE__*/ _jsx(TextField, {
179
481
  fullWidth: true,
482
+ required: true,
180
483
  label: "What kind of site are you creating?",
181
484
  placeholder: "a neighborhood dog groomer that takes bookings",
182
485
  value: answers.siteType,
183
486
  onChange: (event)=>answer({
184
487
  siteType: event.target.value
185
- })
488
+ }),
489
+ helperText: "A few words is enough, or pick one below."
186
490
  }),
187
- /*#__PURE__*/ _jsx(Stack, {
188
- direction: "row",
189
- spacing: 1,
491
+ /*#__PURE__*/ _jsx(Box, {
190
492
  sx: {
493
+ display: 'flex',
191
494
  flexWrap: 'wrap',
192
- mt: 1,
193
- rowGap: 1
495
+ gap: 1
194
496
  },
195
497
  children: AI_SITE_START_TYPES.map((type)=>/*#__PURE__*/ _jsx(Chip, {
196
498
  label: type,
197
499
  size: "small",
500
+ color: answers.siteType === type ? 'primary' : 'default',
198
501
  variant: answers.siteType === type ? 'filled' : 'outlined',
199
502
  onClick: ()=>answer({
200
503
  siteType: type
201
504
  })
202
505
  }, type))
506
+ }),
507
+ /*#__PURE__*/ _jsx(TextField, {
508
+ fullWidth: true,
509
+ label: "Who is it for?",
510
+ placeholder: "local dog owners who want a regular groom booked online",
511
+ value: answers.audience,
512
+ onChange: (event)=>answer({
513
+ audience: event.target.value
514
+ }),
515
+ helperText: "Optional. It narrows who the pages are written for."
203
516
  })
204
517
  ]
205
518
  }),
206
- /*#__PURE__*/ _jsx(TextField, {
207
- fullWidth: true,
208
- label: "Who is it for?",
209
- placeholder: "local dog owners who want a regular groom booked online",
210
- value: answers.audience,
211
- onChange: (event)=>answer({
212
- audience: event.target.value
213
- }),
214
- helperText: "Optional. It narrows who the pages are written for."
215
- }),
216
- /*#__PURE__*/ _jsxs(Box, {
519
+ /*#__PURE__*/ _jsxs(Stack, {
520
+ spacing: 2,
521
+ component: "section",
522
+ "aria-label": "Look & layout",
217
523
  children: [
218
- /*#__PURE__*/ _jsx(Typography, {
219
- variant: "subtitle2",
220
- gutterBottom: true,
221
- children: 'Which of these do you like?'
524
+ /*#__PURE__*/ _jsx(SectionLabel, {
525
+ children: 'Look & layout'
222
526
  }),
223
527
  /*#__PURE__*/ _jsx(Typography, {
224
528
  variant: "body2",
225
529
  color: "text.secondary",
226
- gutterBottom: true,
227
530
  children: 'Optional. Picking one steers the shape of the site, not its words.'
228
531
  }),
229
- /*#__PURE__*/ _jsx(Stack, {
230
- direction: "row",
231
- spacing: 1,
532
+ /*#__PURE__*/ _jsx(Box, {
232
533
  sx: {
233
- flexWrap: 'wrap',
234
- rowGap: 1
534
+ display: 'grid',
535
+ gridTemplateColumns: {
536
+ xs: '1fr',
537
+ sm: '1fr 1fr'
538
+ },
539
+ gap: 2
235
540
  },
236
- children: AI_SITE_START_EXAMPLES.map((example)=>/*#__PURE__*/ _jsx(Chip, {
237
- label: `${example.label} — ${example.blurb}`,
238
- variant: answers.example === example.id ? 'filled' : 'outlined',
239
- onClick: ()=>answer({
240
- example: answers.example === example.id ? null : example.id
541
+ children: AI_SITE_START_EXAMPLES.map((example)=>{
542
+ const selected = answers.example === example.id;
543
+ return /*#__PURE__*/ _jsx(Card, {
544
+ variant: "outlined",
545
+ sx: (theme)=>({
546
+ borderRadius: 2,
547
+ borderColor: selected ? 'primary.main' : 'divider',
548
+ bgcolor: selected ? alpha(theme.palette.primary.main, 0.08) : 'transparent'
549
+ }),
550
+ children: /*#__PURE__*/ _jsx(CardActionArea, {
551
+ "aria-pressed": selected,
552
+ "aria-label": `${example.label} — ${example.blurb}`,
553
+ onClick: ()=>answer({
554
+ example: selected ? null : example.id
555
+ }),
556
+ sx: {
557
+ p: 2,
558
+ height: '100%'
559
+ },
560
+ children: /*#__PURE__*/ _jsxs(Stack, {
561
+ direction: "row",
562
+ spacing: 1.5,
563
+ sx: {
564
+ alignItems: 'flex-start'
565
+ },
566
+ children: [
567
+ /*#__PURE__*/ _jsxs(Box, {
568
+ sx: {
569
+ flex: 1,
570
+ minWidth: 0
571
+ },
572
+ children: [
573
+ /*#__PURE__*/ _jsx(Typography, {
574
+ variant: "subtitle2",
575
+ children: example.label
576
+ }),
577
+ /*#__PURE__*/ _jsx(Typography, {
578
+ variant: "body2",
579
+ color: "text.secondary",
580
+ children: example.blurb
581
+ })
582
+ ]
583
+ }),
584
+ selected && /*#__PURE__*/ _jsx(Box, {
585
+ sx: {
586
+ color: 'primary.main',
587
+ display: 'flex'
588
+ },
589
+ "aria-hidden": true,
590
+ children: /*#__PURE__*/ _jsx(MdiIcon, {
591
+ path: mdiCheckCircle.path
592
+ })
593
+ })
594
+ ]
241
595
  })
242
- }, example.id))
596
+ })
597
+ }, example.id);
598
+ })
243
599
  })
244
600
  ]
245
601
  }),
246
- /*#__PURE__*/ _jsx(TextField, {
247
- select: true,
248
- fullWidth: true,
249
- label: "Where do form submissions go?",
250
- value: answers.submissions,
251
- onChange: (event)=>answer({
252
- submissions: event.target.value
253
- }),
254
- helperText: "You can change this on the form itself afterwards.",
255
- children: AI_SITE_SUBMISSION_CHOICES.map((option)=>/*#__PURE__*/ _jsx(MenuItem, {
256
- value: option.id,
257
- children: `${option.label} — ${option.blurb}`
258
- }, option.id))
259
- }),
260
602
  /*#__PURE__*/ _jsxs(Stack, {
261
- direction: {
262
- xs: 'column',
263
- sm: 'row'
264
- },
265
603
  spacing: 2,
604
+ component: "section",
605
+ "aria-label": "Details",
266
606
  children: [
607
+ /*#__PURE__*/ _jsx(SectionLabel, {
608
+ children: 'Details'
609
+ }),
267
610
  /*#__PURE__*/ _jsx(TextField, {
268
611
  select: true,
269
- label: "Pages",
270
- value: answers.pages,
612
+ fullWidth: true,
613
+ label: "Where do form submissions go?",
614
+ value: answers.submissions,
271
615
  onChange: (event)=>answer({
272
- pages: Number(event.target.value)
616
+ submissions: event.target.value
273
617
  }),
274
- sx: {
275
- minWidth: 160
276
- },
277
- children: Array.from({
278
- length: AI_SITE_PAGES.max - AI_SITE_PAGES.min + 1
279
- }, (_, index)=>AI_SITE_PAGES.min + index).map((count)=>/*#__PURE__*/ _jsx(MenuItem, {
280
- value: count,
281
- children: count
282
- }, count))
618
+ helperText: "You can change this on the form itself afterwards.",
619
+ children: AI_SITE_SUBMISSION_CHOICES.map((option)=>/*#__PURE__*/ _jsx(MenuItem, {
620
+ value: option.id,
621
+ children: `${option.label} — ${option.blurb}`
622
+ }, option.id))
283
623
  }),
284
- /*#__PURE__*/ _jsxs(TextField, {
285
- select: true,
286
- label: "Welcome email",
287
- value: answers.welcomeEmail ? 'yes' : 'no',
288
- onChange: (event)=>answer({
289
- welcomeEmail: event.target.value === 'yes'
290
- }),
291
- sx: {
292
- minWidth: 160
624
+ /*#__PURE__*/ _jsxs(Stack, {
625
+ direction: {
626
+ xs: 'column',
627
+ sm: 'row'
293
628
  },
629
+ spacing: 2,
294
630
  children: [
295
- /*#__PURE__*/ _jsx(MenuItem, {
296
- value: "yes",
297
- children: 'Draft one'
631
+ /*#__PURE__*/ _jsx(TextField, {
632
+ select: true,
633
+ fullWidth: true,
634
+ label: "Pages",
635
+ value: answers.pages,
636
+ onChange: (event)=>answer({
637
+ pages: Number(event.target.value)
638
+ }),
639
+ helperText: freeTaste ? AI_SITE_FREE_PAGES_NOTE : undefined,
640
+ children: Array.from({
641
+ length: band.max - band.min + 1
642
+ }, (_, index)=>band.min + index).map((count)=>/*#__PURE__*/ _jsx(MenuItem, {
643
+ value: count,
644
+ children: count
645
+ }, count))
298
646
  }),
299
- /*#__PURE__*/ _jsx(MenuItem, {
300
- value: "no",
301
- children: 'No'
647
+ !freeTaste && /*#__PURE__*/ _jsxs(TextField, {
648
+ select: true,
649
+ fullWidth: true,
650
+ label: "Welcome email",
651
+ value: answers.welcomeEmail ? 'yes' : 'no',
652
+ onChange: (event)=>answer({
653
+ welcomeEmail: event.target.value === 'yes'
654
+ }),
655
+ children: [
656
+ /*#__PURE__*/ _jsx(MenuItem, {
657
+ value: "yes",
658
+ children: 'Draft one'
659
+ }),
660
+ /*#__PURE__*/ _jsx(MenuItem, {
661
+ value: "no",
662
+ children: 'No'
663
+ })
664
+ ]
302
665
  })
303
666
  ]
304
667
  })
305
668
  ]
306
- }),
307
- /*#__PURE__*/ _jsx(Typography, {
308
- variant: "body2",
309
- color: "text.secondary",
310
- children: `About ${estimate.toLocaleString('en-US')} credits, estimated. What it really ` + 'costs is what each step spends, and you can watch that add up while it runs.'
311
669
  })
312
670
  ]
313
671
  })
@@ -315,27 +673,70 @@ export function AiSiteStartCard({ hostId, orgId, startBlank }) {
315
673
  })
316
674
  })
317
675
  }),
318
- !started && /* Wraps, because the reason and the button together are wider than a
319
- phone: on one line the reason would push the button off the edge. */ /*#__PURE__*/ _jsxs(DialogActions, {
320
- sx: {
321
- px: 3,
676
+ step === 'describe' && !started && /* The footer stays put while the questions scroll. It wraps, because
677
+ the estimate, the reason and the buttons are wider than a phone. */ /*#__PURE__*/ _jsxs(DialogActions, {
678
+ sx: _extends({
679
+ px: {
680
+ xs: 2,
681
+ sm: 3
682
+ },
322
683
  py: 2,
323
684
  flexWrap: 'wrap',
324
- rowGap: 1
325
- },
685
+ rowGap: 1,
686
+ columnGap: 2,
687
+ borderTop: 1,
688
+ borderColor: 'divider'
689
+ }, AI_SITE_START_SURFACE_SX),
326
690
  children: [
691
+ /*#__PURE__*/ _jsxs(Stack, {
692
+ direction: "row",
693
+ spacing: 0.5,
694
+ sx: {
695
+ alignItems: 'center',
696
+ mr: 'auto',
697
+ minWidth: 0
698
+ },
699
+ children: [
700
+ /*#__PURE__*/ _jsx(Typography, {
701
+ variant: "body2",
702
+ color: "text.secondary",
703
+ children: `${estimateText}.`
704
+ }),
705
+ /*#__PURE__*/ _jsx(Tooltip, {
706
+ title: "What it really costs is what each step spends, and you can watch that add up while it runs.",
707
+ children: /*#__PURE__*/ _jsx(Box, {
708
+ component: "span",
709
+ tabIndex: 0,
710
+ "aria-label": "How the estimate works",
711
+ sx: {
712
+ display: 'inline-flex',
713
+ color: 'text.secondary'
714
+ },
715
+ children: /*#__PURE__*/ _jsx(MdiIcon, {
716
+ path: mdiInformationOutline.path,
717
+ fontSize: "small"
718
+ })
719
+ })
720
+ })
721
+ ]
722
+ }),
327
723
  refusal && /*#__PURE__*/ _jsx(Typography, {
328
724
  variant: "body2",
329
725
  color: "text.secondary",
330
- sx: {
331
- mr: 'auto'
332
- },
333
726
  children: refusal
334
727
  }),
728
+ /*#__PURE__*/ _jsx(Button, {
729
+ onClick: ()=>setStep('choose'),
730
+ children: 'Back'
731
+ }),
335
732
  /*#__PURE__*/ _jsx(Button, {
336
733
  variant: "contained",
734
+ size: "large",
337
735
  disabled: busy || Boolean(refusal),
338
736
  onClick: plan,
737
+ startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
738
+ path: mdiCreation.path
739
+ }),
339
740
  children: busy ? 'Starting…' : 'Plan my site'
340
741
  })
341
742
  ]