@evaro/consultation-sdk 0.1.0 → 0.2.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -20,6 +20,21 @@ var host = (hosts, environment) => {
20
20
  };
21
21
  var apiHost = (environment) => host(API_HOSTS, environment);
22
22
  var nodeApiHost = (environment) => host(NODE_API_HOSTS, environment);
23
+ var isLocalHost = (hostname) => hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "[::1]" || /^127\./.test(hostname);
24
+ var parseUrl = (value) => {
25
+ try {
26
+ return new URL(value);
27
+ } catch {
28
+ throw new Error(`nodeApiUrl is not a valid URL: ${value}`);
29
+ }
30
+ };
31
+ /** The NodeAPI base for target calls: `nodeApiUrl` when the host sets one, otherwise the environment's host. */
32
+ function nodeApiBase(environment, nodeApiUrl) {
33
+ if (!nodeApiUrl) return nodeApiHost(environment);
34
+ const url = parseUrl(nodeApiUrl);
35
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLocalHost(url.hostname))) throw new Error(`nodeApiUrl must use https, except for localhost: ${nodeApiUrl}`);
36
+ return `${url.origin}${url.pathname}`.replace(/\/+$/, "");
37
+ }
23
38
  var RequestError = class extends Error {
24
39
  status;
25
40
  constructor(message, status) {
@@ -28,11 +43,26 @@ var RequestError = class extends Error {
28
43
  this.name = "RequestError";
29
44
  }
30
45
  };
46
+ /** The server refused a write because the answers changed in another place. */
47
+ var isConflict = (error) => error instanceof RequestError && error.status === 409;
48
+ var REQUEST_TIMEOUT_MS = 3e4;
31
49
  async function requestJson(url, init = {}) {
32
- const response = await fetch(url, init);
33
- const body = await response.json().catch(() => null);
34
- if (!response.ok) throw new RequestError(body?.message || `Request failed with status ${response.status}`, response.status);
35
- return body;
50
+ const controller = new AbortController();
51
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
52
+ try {
53
+ const response = await fetch(url, {
54
+ ...init,
55
+ signal: controller.signal
56
+ });
57
+ const body = await response.json().catch(() => null);
58
+ if (!response.ok) throw new RequestError(body?.message || `Request failed with status ${response.status}`, response.status);
59
+ return body;
60
+ } catch (error) {
61
+ if (controller.signal.aborted) throw new RequestError("The request timed out", 0);
62
+ throw error;
63
+ } finally {
64
+ clearTimeout(timer);
65
+ }
36
66
  }
37
67
  /** The consultation as Connect loads it. `preview` reads the unpublished version, which needs no token. */
38
68
  async function fetchVendorConsultation(environment, hash, vendor, preview) {
@@ -49,6 +79,50 @@ function fetchVendorTheme(environment, vendor) {
49
79
  }
50
80
  return themes.get(key);
51
81
  }
82
+ // sdk/src/draftQueue.ts
83
+ /**
84
+ * Runs one target's drafts and submits one after another, so an older write never lands after a newer one, and
85
+ * a reload can wait for the writes that are still open.
86
+ */
87
+ var DraftQueue = class {
88
+ onIdle;
89
+ tail = Promise.resolve();
90
+ open = 0;
91
+ constructor(onIdle = () => {}) {
92
+ this.onIdle = onIdle;
93
+ }
94
+ /** Runs the task after the open ones. A failed task does not stop the queue. */
95
+ run(task) {
96
+ const result = this.tail.then(task);
97
+ this.open++;
98
+ this.tail = result.catch(() => void 0).finally(() => {
99
+ this.open--;
100
+ if (this.open === 0) this.onIdle();
101
+ });
102
+ return result;
103
+ }
104
+ /** Settles when every task queued so far has settled. */
105
+ idle() {
106
+ return this.tail;
107
+ }
108
+ };
109
+ var queues = /* @__PURE__ */ new Map();
110
+ /** Runs a write on the target's queue. Only a write creates a queue, and the queue goes once it is idle. */
111
+ function runOnDraftQueue(key, task) {
112
+ let queue = queues.get(key);
113
+ if (!queue) {
114
+ const created = new DraftQueue(() => {
115
+ if (queues.get(key) === created) queues.delete(key);
116
+ });
117
+ queue = created;
118
+ queues.set(key, queue);
119
+ }
120
+ return queue.run(task);
121
+ }
122
+ /** Settles when the target's open writes have settled. A target with no open writes settles at once. */
123
+ function draftQueueIdle(key) {
124
+ return queues.get(key)?.idle() ?? Promise.resolve();
125
+ }
52
126
  // sdk/src/flow/validations.ts
53
127
  var questionValidations = { blood_pressure: {
54
128
  validate: (value) => /^\d{1,3}\/\d{1,3}$/.test(value),
@@ -199,7 +273,7 @@ function isFreeTextTooShort(type, answer) {
199
273
  return FREE_TEXT_TYPES.includes(type) && (answer ?? "").trim().length < 3;
200
274
  }
201
275
  /** Whether a question holds an answer the patient could move on from. */
202
- function isAnswered(question, answer) {
276
+ function isAnswered$1(question, answer) {
203
277
  if (!question) return false;
204
278
  if (Array.isArray(answer)) return answer.length > 0;
205
279
  if (!answer) return false;
@@ -217,7 +291,7 @@ function areFollowUpsAnswered(followUpHashes, questions, answers, optionHashes,
217
291
  seen.add(hash);
218
292
  const question = questions?.find((q) => q.hash === hash);
219
293
  const answer = answers?.[hash];
220
- if (!isAnswered(question, answer)) return false;
294
+ if (!isAnswered$1(question, answer)) return false;
221
295
  return areFollowUpsAnswered(getFollowUpHashes(getMatchedOptionLogics(answer, optionHashes?.[hash], question.options)), questions, answers, optionHashes, seen);
222
296
  });
223
297
  }
@@ -442,6 +516,13 @@ function resumeHistory(walk) {
442
516
  return walkFrom(visibleQuestions[0]?.hash, walk, (hash) => areFollowUpsAnswered([hash], allQuestions, answers, optionHashes));
443
517
  }
444
518
  /**
519
+ * The one-page form's main questions: the flow from the first question, where only an answer takes a jump, so an
520
+ * unanswered question goes on to its default next one. It stops at an answer with an exit.
521
+ */
522
+ function formPath(walk) {
523
+ return walkFrom(walk.visibleQuestions[0]?.hash, walk, () => true);
524
+ }
525
+ /**
445
526
  * The answers a draft keeps: the path behind the user, and ahead of them the questions the current answers
446
527
  * still lead to. After Back, answers further on survive while the user edits, even when an edit leaves the
447
528
  * current question unfinished. Answers of a branch the user left are dropped.
@@ -711,15 +792,21 @@ function useConsultationFlow({ consultation, consultationType, initialAnswers, i
711
792
  };
712
793
  }
713
794
  // sdk/src/targets/diagnosisRequest.ts
795
+ var versionOf = (body) => {
796
+ if (typeof body?.version !== "number") throw new Error("NodeAPI sent the assessment without a version");
797
+ return body.version;
798
+ };
714
799
  var toSaved = (body) => ({
715
800
  answers: body?.answers ?? {},
716
801
  status: body?.status ?? "empty",
717
802
  consultationVersion: body?.consultation_version ?? null,
718
- submittedAt: body?.submitted_at ?? null
803
+ submittedAt: body?.submitted_at ?? null,
804
+ version: versionOf(body)
719
805
  });
720
806
  /** Assessment answers stored on a diagnosis request, through NodeAPI with the Prescriber App user's token. */
721
- function diagnosisRequestTarget(environment, id, getToken) {
722
- const base = () => `${nodeApiHost(environment)}/prescriber/diagnosis-requests/${encodeURIComponent(id)}/assessment`;
807
+ function diagnosisRequestTarget(environment, id, getToken, nodeApiUrl) {
808
+ const host = () => nodeApiBase(environment, nodeApiUrl);
809
+ const base = () => `${host()}/prescriber/diagnosis-requests/${encodeURIComponent(id)}/assessment`;
723
810
  const headers = async () => {
724
811
  const token = await getToken?.();
725
812
  if (!token) throw new Error("No token: the user must be signed in to load or save answers");
@@ -728,32 +815,235 @@ function diagnosisRequestTarget(environment, id, getToken) {
728
815
  "Content-Type": "application/json"
729
816
  };
730
817
  };
818
+ let version = null;
819
+ let loads = 0;
731
820
  return {
732
- load: async () => toSaved(await requestJson(base(), { headers: await headers() })),
821
+ get version() {
822
+ return version;
823
+ },
824
+ load: async () => {
825
+ const load = ++loads;
826
+ const saved = toSaved(await requestJson(base(), { headers: await headers() }));
827
+ if (load === loads) version = saved.version;
828
+ return saved;
829
+ },
733
830
  saveDraft: async (answers) => {
734
- await requestJson(`${base()}/draft`, {
831
+ version = versionOf(await requestJson(`${base()}/draft`, {
735
832
  method: "PUT",
736
833
  headers: await headers(),
737
- body: JSON.stringify({ answers })
738
- });
834
+ body: JSON.stringify({
835
+ answers,
836
+ version
837
+ })
838
+ }));
739
839
  },
740
840
  submit: async (answers) => {
741
- await requestJson(`${base()}/submit`, {
841
+ version = versionOf(await requestJson(`${base()}/submit`, {
742
842
  method: "POST",
743
843
  headers: await headers(),
744
- body: JSON.stringify({ answers })
745
- });
844
+ body: JSON.stringify({
845
+ answers,
846
+ version
847
+ })
848
+ }));
746
849
  }
747
850
  };
748
851
  }
749
852
  // sdk/src/targets/index.ts
750
853
  /** One adapter for each target type. To save answers in a new place, add a type here, not code in the host. */
751
- function createTargetAdapter(target, environment, getToken) {
854
+ function createTargetAdapter(target, environment, getToken, nodeApiUrl) {
752
855
  switch (target.type) {
753
- case "diagnosis-request": return diagnosisRequestTarget(environment, target.id, getToken);
856
+ case "diagnosis-request": return diagnosisRequestTarget(environment, target.id, getToken, nodeApiUrl);
754
857
  default: throw new Error(`Unknown target type: ${target.type}`);
755
858
  }
756
859
  }
860
+ var errorMessage = (error) => error instanceof Error ? error.message : "Something went wrong";
861
+ function useTargetSave({ adapter, queueKey, revision, latestDraft, onDone, onSubmitted, onConflict, onSaved }) {
862
+ const [saveState, setSaveState] = useState("idle");
863
+ const [submitState, setSubmitState] = useState("idle");
864
+ const [submitError, setSubmitError] = useState(null);
865
+ const latestDraftRef = useRef(latestDraft);
866
+ latestDraftRef.current = latestDraft;
867
+ const onDoneRef = useRef(onDone);
868
+ onDoneRef.current = onDone;
869
+ const onSubmittedRef = useRef(onSubmitted);
870
+ onSubmittedRef.current = onSubmitted;
871
+ const onConflictRef = useRef(onConflict);
872
+ onConflictRef.current = onConflict;
873
+ const onSavedRef = useRef(onSaved);
874
+ onSavedRef.current = onSaved;
875
+ const draftTimer = useRef(null);
876
+ const latestRevision = useRef(revision);
877
+ latestRevision.current = revision;
878
+ const savedRevision = useRef(revision);
879
+ const mounted = useRef(true);
880
+ const closed = useRef(false);
881
+ const draftSeq = useRef(0);
882
+ const conflicted = useRef(false);
883
+ const stopForConflict = useCallback(() => {
884
+ if (conflicted.current) return;
885
+ conflicted.current = true;
886
+ closed.current = true;
887
+ savedRevision.current = latestRevision.current;
888
+ clearTimeout(draftTimer.current);
889
+ onConflictRef.current();
890
+ }, []);
891
+ const saveDraft = useCallback(() => {
892
+ if (!adapter) return;
893
+ clearTimeout(draftTimer.current);
894
+ const seq = ++draftSeq.current;
895
+ const isLatest = () => seq === draftSeq.current;
896
+ setSaveState("saving");
897
+ runOnDraftQueue(queueKey, async () => {
898
+ if (closed.current) return false;
899
+ const revision = latestRevision.current;
900
+ await adapter.saveDraft(latestDraftRef.current());
901
+ savedRevision.current = Math.max(savedRevision.current, revision);
902
+ return true;
903
+ }).then((sent) => {
904
+ if (sent && mounted.current) onSavedRef.current();
905
+ if (!isLatest() || !mounted.current) return;
906
+ setSaveState((state) => sent ? "saved" : state === "saving" ? "idle" : state);
907
+ }, (error) => {
908
+ if (isConflict(error)) {
909
+ if (mounted.current) stopForConflict();
910
+ return;
911
+ }
912
+ if (isLatest() && mounted.current) setSaveState("error");
913
+ });
914
+ }, [
915
+ adapter,
916
+ queueKey,
917
+ stopForConflict
918
+ ]);
919
+ useEffect(() => {
920
+ if (!adapter || revision === 0 || closed.current) return;
921
+ draftTimer.current = setTimeout(saveDraft, 600);
922
+ return () => clearTimeout(draftTimer.current);
923
+ }, [
924
+ adapter,
925
+ revision,
926
+ saveDraft
927
+ ]);
928
+ useEffect(() => {
929
+ mounted.current = true;
930
+ return () => {
931
+ mounted.current = false;
932
+ if (!adapter || latestRevision.current <= savedRevision.current || closed.current) return;
933
+ const answers = latestDraftRef.current();
934
+ runOnDraftQueue(queueKey, () => adapter.saveDraft(answers)).catch((error) => {
935
+ if (isConflict(error)) onConflictRef.current();
936
+ });
937
+ };
938
+ }, []);
939
+ /** Shows why the answers cannot be submitted yet. */
940
+ const rejectSubmit = (message) => {
941
+ setSubmitState("error");
942
+ setSubmitError(message);
943
+ };
944
+ const submit = (answers) => {
945
+ if (!adapter) return;
946
+ closed.current = true;
947
+ clearTimeout(draftTimer.current);
948
+ setSubmitState("submitting");
949
+ setSubmitError(null);
950
+ runOnDraftQueue(queueKey, async () => {
951
+ if (conflicted.current) return {
952
+ submitted: false,
953
+ conflict: true,
954
+ draftSaved: false,
955
+ error: null
956
+ };
957
+ try {
958
+ await adapter.submit(answers);
959
+ return {
960
+ submitted: true,
961
+ conflict: false,
962
+ draftSaved: true,
963
+ error: null
964
+ };
965
+ } catch (error) {
966
+ const versionBefore = adapter.version;
967
+ const saved = await adapter.load().catch(() => null);
968
+ if (saved?.status === "submitted") return {
969
+ submitted: true,
970
+ conflict: false,
971
+ draftSaved: true,
972
+ error: null
973
+ };
974
+ if (saved && saved.version !== versionBefore) return {
975
+ submitted: false,
976
+ conflict: true,
977
+ draftSaved: false,
978
+ error
979
+ };
980
+ const revision = latestRevision.current;
981
+ let conflict = false;
982
+ const draftSaved = await adapter.saveDraft(latestDraftRef.current()).then(() => true, (draftError) => {
983
+ conflict = isConflict(draftError);
984
+ return false;
985
+ });
986
+ if (conflict) {
987
+ if ((await adapter.load().catch(() => null))?.status === "submitted") return {
988
+ submitted: true,
989
+ conflict: false,
990
+ draftSaved: true,
991
+ error: null
992
+ };
993
+ }
994
+ if (draftSaved) savedRevision.current = Math.max(savedRevision.current, revision);
995
+ return {
996
+ submitted: false,
997
+ conflict,
998
+ draftSaved,
999
+ error
1000
+ };
1001
+ }
1002
+ }).then(({ submitted, conflict, draftSaved, error }) => {
1003
+ if (submitted) {
1004
+ if (mounted.current) {
1005
+ setSubmitState("submitted");
1006
+ setSaveState("idle");
1007
+ }
1008
+ onSavedRef.current();
1009
+ onSubmittedRef.current?.();
1010
+ onDoneRef.current?.();
1011
+ return;
1012
+ }
1013
+ if (!mounted.current) return;
1014
+ if (conflict) {
1015
+ stopForConflict();
1016
+ return;
1017
+ }
1018
+ closed.current = false;
1019
+ setSubmitState("error");
1020
+ setSubmitError(`The answers could not be submitted. ${errorMessage(error)}`);
1021
+ setSaveState(draftSaved ? "saved" : "error");
1022
+ });
1023
+ };
1024
+ return {
1025
+ saveState,
1026
+ submitState,
1027
+ submitError,
1028
+ saveDraft,
1029
+ submit,
1030
+ rejectSubmit
1031
+ };
1032
+ }
1033
+ // node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
1034
+ function r(e) {
1035
+ var t, f, n = "";
1036
+ if ("string" == typeof e || "number" == typeof e) n += e;
1037
+ else if ("object" == typeof e) if (Array.isArray(e)) {
1038
+ var o = e.length;
1039
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
1040
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
1041
+ return n;
1042
+ }
1043
+ function clsx() {
1044
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
1045
+ return n;
1046
+ }
757
1047
  // node_modules/.pnpm/dompurify@3.4.16/node_modules/dompurify/dist/purify.es.mjs
758
1048
  /*! @license DOMPurify 3.4.16 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.16/LICENSE */
759
1049
  function _OverloadYield(e, d) {
@@ -3275,20 +3565,6 @@ var sanitize$1 = (html) => {
3275
3565
  ALLOWED_ATTR
3276
3566
  });
3277
3567
  };
3278
- // node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
3279
- function r(e) {
3280
- var t, f, n = "";
3281
- if ("string" == typeof e || "number" == typeof e) n += e;
3282
- else if ("object" == typeof e) if (Array.isArray(e)) {
3283
- var o = e.length;
3284
- for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
3285
- } else for (f in e) e[f] && (n && (n += " "), n += f);
3286
- return n;
3287
- }
3288
- function clsx() {
3289
- for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
3290
- return n;
3291
- }
3292
3568
  // sdk/src/ui/primitives.tsx
3293
3569
  /**
3294
3570
  * Connect's UI primitives (`UI/Button`, `UI/TextField`, `UI/DatePicker`, `UI/Accordion`, `UI/CircularProgress`)
@@ -3521,32 +3797,194 @@ var Accordion = ({ title, description }) => {
3521
3797
  })]
3522
3798
  });
3523
3799
  };
3524
- // sdk/src/ui/Knockout.tsx
3525
- /** Connect's exit page (`ExitConsultation.tsx`) as a screen inside the component. */
3526
- var Knockout = ({ title, content, onReview, onClose }) => /* @__PURE__ */ jsxs("div", {
3527
- className: "flex flex-col items-start py-8 max-w-2xl",
3528
- children: [
3529
- /* @__PURE__ */ jsx("h1", {
3530
- className: "text-xl md:text-3xl font-bold mb-4",
3531
- children: title || "No suitable treatments available"
3532
- }),
3533
- /* @__PURE__ */ jsx("div", {
3534
- className: "text-sm sm:text-base mb-8 [&_a]:underline [&_ul]:list-disc [&_ul]:ml-4 [&_li]:ml-4 [&_b]:font-bold [&_strong]:font-bold [&_p]:mb-3",
3535
- dangerouslySetInnerHTML: { __html: sanitize$1(content ?? "You are not eligible for this treatment. Please exit the consultation or review your answers.") }
3536
- }),
3537
- /* @__PURE__ */ jsx(Button, {
3538
- className: "w-full mb-4 text-md font-bold normal-case bg-primary-700 rounded-3xl",
3539
- onClick: onReview,
3540
- children: "Review answer"
3541
- }),
3542
- onClose && /* @__PURE__ */ jsx(Button, {
3543
- variant: "text",
3544
- className: "w-full text-sm normal-case font-bold text-gray-900 rounded-3xl",
3545
- onClick: onClose,
3546
- children: "Exit Consultation"
3547
- })
3548
- ]
3800
+ // sdk/src/ui/ErrorNotice.tsx
3801
+ var ErrorNotice = ({ message, onRetry }) => /* @__PURE__ */ jsxs("div", {
3802
+ role: "alert",
3803
+ className: "my-4 flex flex-col gap-3 rounded-lg border border-error-300 bg-error-100 p-4 text-sm text-gray-900",
3804
+ children: [/* @__PURE__ */ jsx("p", { children: message }), /* @__PURE__ */ jsx(Button, {
3805
+ variant: "outlined",
3806
+ fullWidth: false,
3807
+ className: "self-start !py-2",
3808
+ onClick: onRetry,
3809
+ children: "Try again"
3810
+ })]
3549
3811
  });
3812
+ // sdk/src/flow/form.ts
3813
+ var plain = (html) => (html ?? "").replace(/<[^>]*>/g, "").trim().toLowerCase();
3814
+ /** A radio question whose only options are Yes and No. The form draws it as a Yes / No pair. */
3815
+ function isYesNo(question) {
3816
+ if (question?.type !== "radio" || question.options?.length !== 2) return false;
3817
+ const labels = question.options.map((option) => plain(option.label)).sort();
3818
+ return labels[0] === "no" && labels[1] === "yes";
3819
+ }
3820
+ var shownFrom = (hashes, walk) => {
3821
+ const shown = /* @__PURE__ */ new Set();
3822
+ for (const hash of hashes) withFollowUps(hash, walk.allQuestions, walk.answers, walk.optionHashes, shown);
3823
+ return shown;
3824
+ };
3825
+ /** The first answer with an exit, on a main question or one of its open follow-ups. The page stops there. */
3826
+ function pathAndExit(walk) {
3827
+ const path = formPath(walk);
3828
+ for (let index = 0; index < path.length; index++) for (const hash of shownFrom([path[index]], walk)) {
3829
+ const question = walk.allQuestions.find((q) => q.hash === hash);
3830
+ const logic = getMatchedOptionLogics(walk.answers[hash], walk.optionHashes[hash], question?.options ?? []).find(hasExitLogic);
3831
+ if (logic) return {
3832
+ path: path.slice(0, index + 1),
3833
+ exit: {
3834
+ questionHash: hash,
3835
+ title: logic.exit_title ?? null,
3836
+ content: logic.exit ?? null
3837
+ }
3838
+ };
3839
+ }
3840
+ return {
3841
+ path,
3842
+ exit: null
3843
+ };
3844
+ }
3845
+ /**
3846
+ * A table section reads its main questions row by row, in consultation order: one question for each heading
3847
+ * after the first. A row a jump or an exit hides as a whole is left out. When the questions do not fill whole
3848
+ * rows, or an answer hides part of a row, the section shows as a list, so a cell can never move under the wrong
3849
+ * row label.
3850
+ */
3851
+ function sectionTable(section, questions, shown) {
3852
+ const columns = section.columns ?? [];
3853
+ const width = columns.length - 1;
3854
+ if (!isTableLayout(section) || !questions.length || questions.length % width) return null;
3855
+ const rows = [];
3856
+ for (let start = 0; start < questions.length; start += width) {
3857
+ const row = questions.slice(start, start + width);
3858
+ const showing = row.filter((hash) => shown.has(hash)).length;
3859
+ if (showing === row.length) rows.push(row);
3860
+ else if (showing) return null;
3861
+ }
3862
+ return rows.length ? {
3863
+ columns,
3864
+ rows
3865
+ } : null;
3866
+ }
3867
+ /** A section set to table with a heading for the row labels and at least one more. */
3868
+ var isTableLayout = (section) => section.layout === "table" && (section.columns?.length ?? 0) > 1;
3869
+ /** Main questions in flow order: a run with no section, or a section's questions at the place of its first one. */
3870
+ function draftBlocks(path, sectionOf) {
3871
+ const drafts = [];
3872
+ const bySection = /* @__PURE__ */ new Map();
3873
+ for (const hash of path) {
3874
+ const section = sectionOf(hash);
3875
+ const last = drafts[drafts.length - 1];
3876
+ if (!section) {
3877
+ if (last?.kind === "questions") last.questions.push(hash);
3878
+ else drafts.push({
3879
+ kind: "questions",
3880
+ questions: [hash]
3881
+ });
3882
+ } else if (bySection.has(section.id)) bySection.get(section.id)?.questions.push(hash);
3883
+ else {
3884
+ const created = {
3885
+ kind: "section",
3886
+ source: section,
3887
+ questions: [hash]
3888
+ };
3889
+ bySection.set(section.id, created);
3890
+ drafts.push(created);
3891
+ }
3892
+ }
3893
+ return drafts;
3894
+ }
3895
+ /**
3896
+ * What the page shows: the flow from the first question. A section's questions show together, where its first
3897
+ * question comes in the flow, and questions with no section show where they come. Sections next to each other
3898
+ * with the same group show as tabs. Follow-ups show under their parent, so only main questions are grouped.
3899
+ */
3900
+ function buildForm(walk, consultation) {
3901
+ const { path, exit } = pathAndExit(walk);
3902
+ const sections = new Map((consultation.sections ?? []).map((section) => [section.id, section]));
3903
+ const byHash = new Map(walk.allQuestions.map((question) => [question.hash, question]));
3904
+ const sectionOf = (hash) => sections.get(byHash.get(hash)?.consultation_section?.[consultation.hash] ?? "") ?? null;
3905
+ const onPath = new Set(path);
3906
+ const sectionQuestionsOf = /* @__PURE__ */ new Map();
3907
+ for (const question of walk.visibleQuestions) {
3908
+ const id = sectionOf(question.hash)?.id;
3909
+ if (id) sectionQuestionsOf.set(id, [...sectionQuestionsOf.get(id) ?? [], question.hash]);
3910
+ }
3911
+ const optional = /* @__PURE__ */ new Set();
3912
+ const blocks = [];
3913
+ for (const draft of draftBlocks(path, sectionOf)) {
3914
+ if (draft.kind === "questions") {
3915
+ blocks.push(draft);
3916
+ continue;
3917
+ }
3918
+ const { source } = draft;
3919
+ const table = sectionTable(source, sectionQuestionsOf.get(source.id) ?? [], onPath);
3920
+ if (isTableLayout(source)) {
3921
+ for (const hash of draft.questions) if (!isYesNo(byHash.get(hash))) optional.add(hash);
3922
+ }
3923
+ const section = {
3924
+ id: source.id,
3925
+ name: source.name,
3926
+ description: source.description ?? "",
3927
+ table,
3928
+ questions: table ? table.rows.flat() : draft.questions
3929
+ };
3930
+ const group = source.group?.trim();
3931
+ const previous = blocks[blocks.length - 1];
3932
+ if (group && previous?.kind === "tabs" && previous.name === group) previous.sections.push(section);
3933
+ else if (group) blocks.push({
3934
+ kind: "tabs",
3935
+ name: group,
3936
+ sections: [section]
3937
+ });
3938
+ else blocks.push({
3939
+ kind: "section",
3940
+ section
3941
+ });
3942
+ }
3943
+ const shown = [...shownFrom(pageOrder(blocks), walk)];
3944
+ for (const hash of shown) {
3945
+ const question = byHash.get(hash);
3946
+ if (question?.type === "checkbox" && question.options?.length === 1) optional.add(hash);
3947
+ }
3948
+ return {
3949
+ path,
3950
+ shown,
3951
+ blocks,
3952
+ optional,
3953
+ exit
3954
+ };
3955
+ }
3956
+ /** The main questions in the order the page draws them. */
3957
+ function pageOrder(blocks) {
3958
+ return blocks.flatMap((block) => {
3959
+ if (block.kind === "questions") return block.questions;
3960
+ if (block.kind === "section") return block.section.questions;
3961
+ return block.sections.flatMap((section) => section.questions);
3962
+ });
3963
+ }
3964
+ /** The copied rule for one answer, without its follow-ups: those are checked as questions of their own. */
3965
+ var isAnswered = (question, answers) => areFollowUpsAnswered([question.hash], [{
3966
+ ...question,
3967
+ options: question.options?.map((option) => option.logic?.follow_ups ? {
3968
+ ...option,
3969
+ logic: {
3970
+ ...option.logic,
3971
+ follow_ups: []
3972
+ }
3973
+ } : option)
3974
+ }], answers, {});
3975
+ /** Questions on the page that still need an answer, in page order. Optional questions are never missing. */
3976
+ function missingAnswers(form, walk) {
3977
+ const byHash = new Map(walk.allQuestions.map((question) => [question.hash, question]));
3978
+ return form.shown.filter((hash) => {
3979
+ if (form.optional.has(hash)) return false;
3980
+ const question = byHash.get(hash);
3981
+ return !question || !isAnswered(question, walk.answers);
3982
+ });
3983
+ }
3984
+ /** The answers on the page: what a draft saves and a submit sends. Answers of a branch a jump leaves are dropped. */
3985
+ function formAnswers(form, walk) {
3986
+ return answersOnPath(form.path, walk.allQuestions, walk.answers, walk.optionHashes);
3987
+ }
3550
3988
  // node_modules/.pnpm/framer-motion@14.9.1_react-dom@19.3.0_react@19.3.0__react@19.3.0/node_modules/framer-motion/dist/es/context/LayoutGroupContext.mjs
3551
3989
  var LayoutGroupContext = /* @__PURE__ */ createContext({});
3552
3990
  // node_modules/.pnpm/framer-motion@14.9.1_react-dom@19.3.0_react@19.3.0__react@19.3.0/node_modules/framer-motion/dist/es/utils/use-constant.mjs
@@ -19504,6 +19942,725 @@ var Question = ({ hash, consultationHash, questions, answers, optionHashes, onAn
19504
19942
  }, hash)
19505
19943
  });
19506
19944
  };
19945
+ // sdk/src/ui/form/inputs.tsx
19946
+ /** Inputs of the one-page form, in the Prescriber App style: 14px text, 16px rounded fields, a Yes / No pair. */
19947
+ var fieldClass = "w-full rounded-2xl border border-gray-300 bg-white px-4 text-sm font-normal text-gray-900 placeholder:text-gray-400 focus:border-primary-700 focus:ring-primary-700 disabled:opacity-60";
19948
+ var plainText = (html) => An(html ?? "").result.trim();
19949
+ /** Options as a joined pair of buttons, such as Yes / No. They are radio inputs, so the keyboard works as usual. */
19950
+ var Segmented = ({ question, answers, optionHashes, onAnswer, disabled, label }) => /* @__PURE__ */ jsx("fieldset", {
19951
+ "aria-label": label ?? plainText(question.title),
19952
+ className: "inline-flex shrink-0",
19953
+ children: question.options.map((option, index) => {
19954
+ const selected = isOptionSelected(option, answers[question.hash], optionHashes[question.hash]);
19955
+ return /* @__PURE__ */ jsxs("label", {
19956
+ className: clsx("flex h-[39px] min-w-[51px] cursor-pointer items-center justify-center border px-3.5 text-sm font-normal transition-colors has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary-300", index === 0 ? "rounded-l-xl" : "-ml-px", index === question.options.length - 1 && "rounded-r-xl", disabled && "pointer-events-none opacity-60", selected ? "relative z-10 border-primary-700 bg-primary-700 text-white" : "border-gray-300 bg-white text-gray-900 hover:bg-gray-50"),
19957
+ children: [/* @__PURE__ */ jsx("input", {
19958
+ type: "radio",
19959
+ className: "sr-only",
19960
+ name: `${question.hash}-choice`,
19961
+ checked: selected,
19962
+ disabled,
19963
+ onChange: () => onAnswer(question, option.label, option.logic)
19964
+ }), plainText(option.label)]
19965
+ }, `${option.label}-${index}`);
19966
+ })
19967
+ });
19968
+ /** A radio question as a dropdown. The native list opens outside the shadow root's styles but works everywhere. */
19969
+ var Select = ({ question, answers, optionHashes, onAnswer, disabled, label }) => {
19970
+ const selected = question.options.findIndex((option) => isOptionSelected(option, answers[question.hash], optionHashes[question.hash]));
19971
+ return /* @__PURE__ */ jsxs("select", {
19972
+ "aria-label": label ?? plainText(question.title),
19973
+ disabled,
19974
+ value: selected < 0 ? "" : String(selected),
19975
+ onChange: (event) => {
19976
+ const option = question.options[Number(event.target.value)];
19977
+ if (option) onAnswer(question, option.label, option.logic);
19978
+ },
19979
+ className: clsx(fieldClass, "h-12 pr-10", selected < 0 && "text-gray-400"),
19980
+ children: [/* @__PURE__ */ jsx("option", {
19981
+ value: "",
19982
+ disabled: true,
19983
+ children: "Select an option"
19984
+ }), question.options.map((option, index) => /* @__PURE__ */ jsx("option", {
19985
+ value: String(index),
19986
+ className: "text-gray-900",
19987
+ children: plainText(option.label)
19988
+ }, `${option.label}-${index}`))]
19989
+ });
19990
+ };
19991
+ /** Free text, a phone number or a date. `multiline` draws the tall box used for notes. */
19992
+ var TextInput = ({ question, answers, onAnswer, disabled, label, multiline, placeholder }) => {
19993
+ const value = answers[question.hash] ?? "";
19994
+ const validation = question.validation ? questionValidations[question.validation] : null;
19995
+ const invalid = !!validation && !!value && !validation.validate(value);
19996
+ const aria = {
19997
+ "aria-label": label ?? plainText(question.title),
19998
+ "aria-invalid": invalid || void 0
19999
+ };
20000
+ const change = (text) => onAnswer(question, question.type === "mobile" ? text.replace(/[^0-9]/g, "") : text, void 0);
20001
+ return /* @__PURE__ */ jsxs("div", {
20002
+ className: "flex w-full flex-col gap-1",
20003
+ children: [question.type === "date" ? /* @__PURE__ */ jsx(DatePicker, {
20004
+ ...aria,
20005
+ placeholder: "DD-MM-YYYY",
20006
+ value,
20007
+ onChange: change,
20008
+ disabled,
20009
+ className: clsx(fieldClass, "h-12")
20010
+ }) : multiline ? /* @__PURE__ */ jsx("textarea", {
20011
+ ...aria,
20012
+ value,
20013
+ disabled,
20014
+ placeholder: validation?.placeholder ?? placeholder,
20015
+ onChange: (event) => change(event.target.value),
20016
+ className: clsx(fieldClass, "min-h-[121px] resize-y py-4 leading-5")
20017
+ }) : /* @__PURE__ */ jsx("input", {
20018
+ ...aria,
20019
+ type: question.type === "mobile" ? "tel" : "text",
20020
+ value,
20021
+ disabled,
20022
+ placeholder: validation?.placeholder ?? placeholder,
20023
+ onChange: (event) => change(event.target.value),
20024
+ className: clsx(fieldClass, "h-12")
20025
+ }), invalid && /* @__PURE__ */ jsx("p", {
20026
+ className: "text-xs text-error-900",
20027
+ children: validation.errorMessage
20028
+ })]
20029
+ });
20030
+ };
20031
+ /** Checkbox options as small ticks. A question with one option draws as one tick with its label. */
20032
+ var Checks = ({ question, answers, optionHashes, onAnswer, disabled }) => /* @__PURE__ */ jsx("div", {
20033
+ className: "flex flex-col gap-2",
20034
+ children: question.options.map((option, index) => /* @__PURE__ */ jsxs("label", {
20035
+ className: "flex cursor-pointer items-center gap-3",
20036
+ children: [/* @__PURE__ */ jsx("input", {
20037
+ type: "checkbox",
20038
+ className: "h-[18px] w-[18px] cursor-pointer rounded border-gray-300 text-[#1a2f91] focus:ring-primary-700",
20039
+ checked: isOptionSelected(option, answers[question.hash], optionHashes[question.hash]),
20040
+ disabled,
20041
+ onChange: () => onAnswer(question, option.label, option.logic)
20042
+ }), /* @__PURE__ */ jsx("span", {
20043
+ className: "text-sm font-normal text-gray-700",
20044
+ dangerouslySetInnerHTML: { __html: sanitize$1(option.label) }
20045
+ })]
20046
+ }, `${option.label}-${index}`))
20047
+ });
20048
+ /** Radio options that are not Yes / No, as a list of round buttons. */
20049
+ var Radios = ({ question, answers, optionHashes, onAnswer, disabled }) => /* @__PURE__ */ jsx("div", {
20050
+ role: "radiogroup",
20051
+ "aria-label": plainText(question.title),
20052
+ className: "flex flex-col gap-2",
20053
+ children: question.options.map((option, index) => {
20054
+ const selected = isOptionSelected(option, answers[question.hash], optionHashes[question.hash]);
20055
+ return /* @__PURE__ */ jsxs("label", {
20056
+ className: "flex cursor-pointer items-center gap-3",
20057
+ children: [/* @__PURE__ */ jsx("input", {
20058
+ type: "radio",
20059
+ name: question.hash,
20060
+ className: "h-[18px] w-[18px] cursor-pointer border-gray-300 text-primary-700 focus:ring-primary-700",
20061
+ checked: selected,
20062
+ disabled,
20063
+ onChange: () => onAnswer(question, option.label, option.logic)
20064
+ }), /* @__PURE__ */ jsxs("span", {
20065
+ className: "flex flex-col",
20066
+ children: [/* @__PURE__ */ jsx("span", {
20067
+ className: "text-sm font-normal text-gray-900",
20068
+ dangerouslySetInnerHTML: { __html: sanitize$1(option.label) }
20069
+ }), option.helper && /* @__PURE__ */ jsx("span", {
20070
+ className: "text-xs font-normal text-gray-700",
20071
+ children: option.helper
20072
+ })]
20073
+ })]
20074
+ }, `${option.label}-${index}`);
20075
+ })
20076
+ });
20077
+ // sdk/src/ui/form/FormField.tsx
20078
+ /** One question of the one-page form with its open follow-ups, in the Prescriber App style. */
20079
+ var MissingNote = ({ show }) => show ? /* @__PURE__ */ jsx("p", {
20080
+ role: "alert",
20081
+ className: "text-xs text-error-900",
20082
+ children: "Answer this question."
20083
+ }) : null;
20084
+ /** The question's description as the grey hint under its label. */
20085
+ var Hint = ({ html }) => plainText(html) ? /* @__PURE__ */ jsx("div", {
20086
+ className: "text-xs font-normal tracking-[-0.072px] text-gray-700 [&_a]:underline [&_p]:m-0",
20087
+ dangerouslySetInnerHTML: { __html: sanitize$1(html) }
20088
+ }) : null;
20089
+ var getOpenFollowUps = (hash, context) => {
20090
+ const question = context.questions.find((q) => q.hash === hash);
20091
+ return getFollowUpHashes(getMatchedOptionLogics(context.answers[hash], context.optionHashes[hash], question?.options ?? []));
20092
+ };
20093
+ /** The open follow-ups of a question, indented under it. */
20094
+ var FollowUps = ({ hash, context }) => {
20095
+ const followUps = getOpenFollowUps(hash, context);
20096
+ if (!followUps.length) return null;
20097
+ return /* @__PURE__ */ jsx("div", {
20098
+ className: "mt-3 flex flex-col gap-4 border-l-2 border-primary-100 pl-4",
20099
+ children: followUps.map((followUp) => /* @__PURE__ */ jsx(FormField, {
20100
+ hash: followUp,
20101
+ context
20102
+ }, followUp))
20103
+ });
20104
+ };
20105
+ /** `spaced` adds the gap a field needs inside a section, where Yes / No rows sit with no gap. */
20106
+ var FormField = ({ hash, context, spaced }) => {
20107
+ const question = context.questions.find((q) => q.hash === hash);
20108
+ if (!question) return null;
20109
+ const input = {
20110
+ question,
20111
+ answers: context.answers,
20112
+ optionHashes: context.optionHashes,
20113
+ onAnswer: context.onAnswer
20114
+ };
20115
+ const disabled = context.disabled;
20116
+ const missing = context.missing.has(hash);
20117
+ if (isYesNo(question)) return /* @__PURE__ */ jsxs("div", {
20118
+ id: hash,
20119
+ className: "scroll-m-9 border-b border-gray-300 px-4 py-2",
20120
+ children: [
20121
+ /* @__PURE__ */ jsxs("div", {
20122
+ className: "flex items-center gap-6",
20123
+ children: [/* @__PURE__ */ jsxs("div", {
20124
+ className: "flex min-w-0 flex-1 flex-col",
20125
+ children: [/* @__PURE__ */ jsx("p", {
20126
+ className: "text-sm font-normal tracking-[-0.084px] text-gray-900",
20127
+ children: question.title
20128
+ }), /* @__PURE__ */ jsx(Hint, { html: question.description })]
20129
+ }), /* @__PURE__ */ jsx(Segmented, {
20130
+ ...input,
20131
+ disabled
20132
+ })]
20133
+ }),
20134
+ /* @__PURE__ */ jsx(MissingNote, { show: missing }),
20135
+ /* @__PURE__ */ jsx(FollowUps, {
20136
+ hash,
20137
+ context
20138
+ })
20139
+ ]
20140
+ });
20141
+ const supported = FILL_TYPES.includes(question.type);
20142
+ const singleCheck = question.type === "checkbox" && question.options.length === 1;
20143
+ return /* @__PURE__ */ jsxs("div", {
20144
+ id: hash,
20145
+ className: clsx("flex scroll-m-9 flex-col gap-1", spaced && "py-3"),
20146
+ children: [
20147
+ !singleCheck && /* @__PURE__ */ jsxs("div", {
20148
+ className: clsx("flex flex-col gap-px", [
20149
+ "radio",
20150
+ "checkbox",
20151
+ "scale"
20152
+ ].includes(question.type) && "mb-1"),
20153
+ children: [/* @__PURE__ */ jsx("p", {
20154
+ className: "text-sm font-bold leading-5 text-gray-900",
20155
+ children: question.title
20156
+ }), /* @__PURE__ */ jsx(Hint, { html: question.description })]
20157
+ }),
20158
+ !supported && /* @__PURE__ */ jsxs("p", {
20159
+ className: "rounded-2xl border border-gray-300 bg-gray-50 p-4 text-sm font-normal text-gray-700",
20160
+ children: [
20161
+ "This question type (",
20162
+ question.type,
20163
+ ") is shown in Connect only."
20164
+ ]
20165
+ }),
20166
+ [
20167
+ "text",
20168
+ "mobile",
20169
+ "date"
20170
+ ].includes(question.type) && /* @__PURE__ */ jsx(TextInput, {
20171
+ ...input,
20172
+ disabled
20173
+ }),
20174
+ question.type === "textarea" && /* @__PURE__ */ jsx(TextInput, {
20175
+ ...input,
20176
+ disabled,
20177
+ multiline: true
20178
+ }),
20179
+ question.type === "checkbox" && /* @__PURE__ */ jsx(Checks, {
20180
+ ...input,
20181
+ disabled
20182
+ }),
20183
+ question.type === "scale" && /* @__PURE__ */ jsx(Segmented, {
20184
+ ...input,
20185
+ disabled
20186
+ }),
20187
+ ["radio", "radio_image"].includes(question.type) && /* @__PURE__ */ jsx(Radios, {
20188
+ ...input,
20189
+ disabled
20190
+ }),
20191
+ /* @__PURE__ */ jsx(MissingNote, { show: missing }),
20192
+ /* @__PURE__ */ jsx(FollowUps, {
20193
+ hash,
20194
+ context
20195
+ })
20196
+ ]
20197
+ });
20198
+ };
20199
+ /** A question as a table cell, with no label of its own. */
20200
+ var Cell = ({ hash, column, context }) => {
20201
+ const question = context.questions.find((q) => q.hash === hash);
20202
+ if (!question) return null;
20203
+ const input = {
20204
+ question,
20205
+ answers: context.answers,
20206
+ optionHashes: context.optionHashes,
20207
+ onAnswer: context.onAnswer,
20208
+ disabled: context.disabled,
20209
+ label: `${plainText(question.title)}: ${column}`
20210
+ };
20211
+ const placeholder = `Write ${column.toLowerCase()}`;
20212
+ let control = /* @__PURE__ */ jsx("span", {
20213
+ className: "text-sm text-gray-400",
20214
+ children: question.type
20215
+ });
20216
+ if (isYesNo(question) || question.type === "scale") control = /* @__PURE__ */ jsx(Segmented, { ...input });
20217
+ else if (["radio", "radio_image"].includes(question.type)) control = /* @__PURE__ */ jsx(Select, { ...input });
20218
+ else if ([
20219
+ "text",
20220
+ "textarea",
20221
+ "mobile",
20222
+ "date"
20223
+ ].includes(question.type)) control = /* @__PURE__ */ jsx(TextInput, {
20224
+ ...input,
20225
+ placeholder
20226
+ });
20227
+ else if (question.type === "checkbox") control = /* @__PURE__ */ jsx(Checks, { ...input });
20228
+ const missing = context.missing.has(hash);
20229
+ const pair = isYesNo(question) || question.type === "scale";
20230
+ return /* @__PURE__ */ jsxs("div", {
20231
+ id: hash,
20232
+ className: clsx("scroll-m-9 rounded-xl", pair ? "inline-flex" : "flex w-full", missing && "ring-2 ring-error-900 ring-offset-1"),
20233
+ children: [control, missing && /* @__PURE__ */ jsx("span", {
20234
+ role: "alert",
20235
+ className: "sr-only",
20236
+ children: "Answer this question."
20237
+ })]
20238
+ });
20239
+ };
20240
+ // sdk/src/ui/form/Blocks.tsx
20241
+ /** Sections, tables and tabs of the one-page form, drawn as in the Prescriber App Assessment design. */
20242
+ var Heading = ({ name, description }) => /* @__PURE__ */ jsxs("div", {
20243
+ className: "flex flex-col gap-px",
20244
+ children: [/* @__PURE__ */ jsx("p", {
20245
+ className: "text-sm font-bold leading-5 tracking-[-0.084px] text-gray-900",
20246
+ children: name
20247
+ }), plainText(description) && /* @__PURE__ */ jsx("div", {
20248
+ className: "text-xs font-normal tracking-[-0.072px] text-gray-700 [&_p]:m-0",
20249
+ dangerouslySetInnerHTML: { __html: sanitize$1(description) }
20250
+ })]
20251
+ });
20252
+ /** "A1 Attention to detail" draws its code in bold, as the design does. */
20253
+ var RowLabel = ({ title }) => {
20254
+ const match = /^([A-Z]{1,3}\d+[a-z]?)\s+(.+)$/.exec(title.trim());
20255
+ return /* @__PURE__ */ jsx("p", {
20256
+ className: "text-sm font-normal leading-5 tracking-[-0.084px] text-gray-900",
20257
+ children: match ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
20258
+ /* @__PURE__ */ jsx("span", {
20259
+ className: "font-extrabold",
20260
+ children: match[1]
20261
+ }),
20262
+ " ",
20263
+ match[2]
20264
+ ] }) : title
20265
+ });
20266
+ };
20267
+ /** A Yes / No pair sits in the middle of its column, under a centred heading. */
20268
+ var isPair = (hash, questions) => {
20269
+ const question = questions.find((q) => q.hash === hash);
20270
+ return isYesNo(question) || question?.type === "scale";
20271
+ };
20272
+ /** Column widths from the first row: a pair takes its own width, a dropdown a fixed one, text the space left. */
20273
+ var columnClass = (hash, questions) => {
20274
+ const question = questions.find((q) => q.hash === hash);
20275
+ if (isPair(hash, questions)) return "w-px whitespace-nowrap text-center";
20276
+ if (question?.type === "radio" || question?.type === "radio_image") return "w-[248px] min-w-[220px]";
20277
+ return "min-w-[240px]";
20278
+ };
20279
+ var Table = ({ table, context }) => {
20280
+ const cell = "py-2 pr-6 last:pr-0 lg:pr-12";
20281
+ return /* @__PURE__ */ jsx("div", {
20282
+ className: "w-full overflow-x-auto rounded-3xl border border-gray-200 bg-gray-50 p-4",
20283
+ children: /* @__PURE__ */ jsxs("table", {
20284
+ className: "w-full min-w-[1000px] border-collapse text-left",
20285
+ children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", {
20286
+ className: "border-b border-gray-300",
20287
+ children: table.columns.map((column, index) => /* @__PURE__ */ jsx("th", {
20288
+ scope: "col",
20289
+ className: clsx("py-4 pr-6 text-base font-bold leading-5 tracking-[-0.15px] text-[#364153] last:pr-0 lg:pr-12", index === 0 ? "w-[300px] min-w-[220px]" : columnClass(table.rows[0][index - 1], context.questions)),
20290
+ children: column
20291
+ }, `${column}-${index}`))
20292
+ }) }), /* @__PURE__ */ jsx("tbody", { children: table.rows.map((cells) => {
20293
+ const label = context.questions.find((q) => q.hash === cells[0])?.title ?? "";
20294
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("tr", {
20295
+ className: "border-b border-gray-300",
20296
+ children: [/* @__PURE__ */ jsx("th", {
20297
+ scope: "row",
20298
+ className: clsx(cell, "font-normal"),
20299
+ children: /* @__PURE__ */ jsx(RowLabel, { title: label })
20300
+ }), cells.map((hash, index) => /* @__PURE__ */ jsx("td", {
20301
+ className: clsx(cell, "align-middle"),
20302
+ children: /* @__PURE__ */ jsx("div", {
20303
+ className: clsx(isPair(hash, context.questions) && "flex justify-center"),
20304
+ children: /* @__PURE__ */ jsx(Cell, {
20305
+ hash,
20306
+ column: table.columns[index + 1],
20307
+ context
20308
+ })
20309
+ })
20310
+ }, hash))]
20311
+ }), cells.some((hash) => getOpenFollowUps(hash, context).length) && /* @__PURE__ */ jsx("tr", {
20312
+ className: "border-b border-gray-300",
20313
+ children: /* @__PURE__ */ jsx("td", {
20314
+ colSpan: table.columns.length,
20315
+ className: "pb-3",
20316
+ children: cells.map((hash) => /* @__PURE__ */ jsx(FollowUps, {
20317
+ hash,
20318
+ context
20319
+ }, hash))
20320
+ })
20321
+ })] }, cells[0]);
20322
+ }) })]
20323
+ })
20324
+ });
20325
+ };
20326
+ var SectionBody = ({ section, context }) => section.table ? /* @__PURE__ */ jsx(Table, {
20327
+ table: section.table,
20328
+ context
20329
+ }) : /* @__PURE__ */ jsx("div", {
20330
+ className: "flex flex-col",
20331
+ children: section.questions.map((hash) => /* @__PURE__ */ jsx(FormField, {
20332
+ hash,
20333
+ context,
20334
+ spaced: true
20335
+ }, hash))
20336
+ });
20337
+ /** A section's questions with their open follow-ups, walking nested ones. */
20338
+ var sectionHashes = (section, context) => {
20339
+ const hashes = /* @__PURE__ */ new Set();
20340
+ for (const hash of section.questions) withFollowUps(hash, context.questions, context.answers, context.optionHashes, hashes);
20341
+ return hashes;
20342
+ };
20343
+ var Tabs = ({ name, sections, context }) => {
20344
+ const [active, setActive] = useState(sections[0].id);
20345
+ const current = sections.find((section) => section.id === active) ?? sections[0];
20346
+ const needsAnswer = (section) => [...sectionHashes(section, context)].some((hash) => context.missing.has(hash));
20347
+ const focus = context.focus;
20348
+ const handledTick = useRef(focus?.tick ?? 0);
20349
+ useEffect(() => {
20350
+ if (!focus || focus.tick === handledTick.current) return;
20351
+ handledTick.current = focus.tick;
20352
+ const holder = sections.find((section) => sectionHashes(section, context).has(focus.hash));
20353
+ if (holder) setActive(holder.id);
20354
+ }, [focus?.tick]);
20355
+ return /* @__PURE__ */ jsxs("div", {
20356
+ className: "flex flex-col gap-6 rounded-3xl border border-gray-200 p-4",
20357
+ children: [/* @__PURE__ */ jsxs("div", {
20358
+ className: "flex flex-col gap-4",
20359
+ children: [/* @__PURE__ */ jsx("div", {
20360
+ className: "py-4",
20361
+ children: /* @__PURE__ */ jsx(Heading, {
20362
+ name,
20363
+ description: current.description
20364
+ })
20365
+ }), /* @__PURE__ */ jsx("div", {
20366
+ role: "tablist",
20367
+ "aria-label": name,
20368
+ className: "flex gap-7 overflow-x-auto border-b border-gray-200",
20369
+ children: sections.map((section) => {
20370
+ const count = section.table?.rows.length ?? section.questions.length;
20371
+ const selected = section === current;
20372
+ return /* @__PURE__ */ jsxs("button", {
20373
+ type: "button",
20374
+ role: "tab",
20375
+ "aria-selected": selected,
20376
+ onClick: () => setActive(section.id),
20377
+ className: clsx("relative h-12 whitespace-nowrap px-2 text-base font-normal tracking-[-0.31px]", selected ? "text-primary-700" : "text-gray-500 hover:text-gray-900"),
20378
+ children: [
20379
+ section.name,
20380
+ " (",
20381
+ count,
20382
+ ")",
20383
+ needsAnswer(section) && /* @__PURE__ */ jsx("span", {
20384
+ className: "ml-1 text-error-900",
20385
+ children: "•"
20386
+ }),
20387
+ selected && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-primary-700" })
20388
+ ]
20389
+ }, section.id);
20390
+ })
20391
+ })]
20392
+ }), /* @__PURE__ */ jsx("div", {
20393
+ role: "tabpanel",
20394
+ children: /* @__PURE__ */ jsx(SectionBody, {
20395
+ section: current,
20396
+ context
20397
+ })
20398
+ })]
20399
+ });
20400
+ };
20401
+ var Block = ({ block, context }) => {
20402
+ if (block.kind === "questions") return /* @__PURE__ */ jsx(Fragment$1, { children: block.questions.map((hash) => /* @__PURE__ */ jsx(FormField, {
20403
+ hash,
20404
+ context
20405
+ }, hash)) });
20406
+ if (block.kind === "tabs") return /* @__PURE__ */ jsx(Tabs, {
20407
+ name: block.name,
20408
+ sections: block.sections,
20409
+ context
20410
+ });
20411
+ const { section } = block;
20412
+ return /* @__PURE__ */ jsxs("div", {
20413
+ className: "flex flex-col gap-1 rounded-[32px] border border-gray-200 bg-white p-6",
20414
+ children: [/* @__PURE__ */ jsx(Heading, {
20415
+ name: section.name,
20416
+ description: section.description
20417
+ }), /* @__PURE__ */ jsx("div", {
20418
+ className: "mt-2",
20419
+ children: /* @__PURE__ */ jsx(SectionBody, {
20420
+ section,
20421
+ context
20422
+ })
20423
+ })]
20424
+ });
20425
+ };
20426
+ // sdk/src/ui/FormFlow.tsx
20427
+ /** The one-page form layout: every question the flow reaches, in the Prescriber App Assessment design. */
20428
+ /**
20429
+ * Keys that stay with a block when answers add or hide blocks or sections. A tab group keeps its key when its
20430
+ * first section changes, so the selected tab does not reset; a second group with the same name gets its own.
20431
+ */
20432
+ var blockKeys = (blocks) => {
20433
+ const groups = /* @__PURE__ */ new Map();
20434
+ return blocks.map((block) => {
20435
+ if (block.kind === "questions") return `questions-${block.questions[0]}`;
20436
+ if (block.kind === "section") return `section-${block.section.id}`;
20437
+ const seen = groups.get(block.name) ?? 0;
20438
+ groups.set(block.name, seen + 1);
20439
+ return `tabs-${block.name}-${seen}`;
20440
+ });
20441
+ };
20442
+ var FormFlow = ({ loaded, adapter, consultationType, submitLabel = "Submit", onDone, onEvent, queueKey, onConflict, onSaved }) => {
20443
+ const { consultation, saved } = loaded;
20444
+ const allQuestions = useMemo(() => withReachableJumps(consultation.questions ?? []), [consultation.questions]);
20445
+ const visibleQuestions = useMemo(() => filterVisibleQuestions(allQuestions, {}), [allQuestions]);
20446
+ const [answers, setAnswers] = useState(saved.answers ?? {});
20447
+ const [optionHashes, setOptionHashes] = useState({});
20448
+ const [revision, setRevision] = useState(0);
20449
+ const [checked, setChecked] = useState(false);
20450
+ const [focus, setFocus] = useState(null);
20451
+ const container = useRef(null);
20452
+ const walk = {
20453
+ allQuestions,
20454
+ visibleQuestions,
20455
+ consultationHash: consultation.hash,
20456
+ answers,
20457
+ optionHashes
20458
+ };
20459
+ const form = useMemo(() => buildForm(walk, consultation), [
20460
+ allQuestions,
20461
+ visibleQuestions,
20462
+ consultation,
20463
+ answers,
20464
+ optionHashes
20465
+ ]);
20466
+ const missing = useMemo(() => new Set(checked ? missingAnswers(form, walk) : []), [checked, form]);
20467
+ const events = useMemo(() => consultationEvents({
20468
+ consultationHash: consultation.hash,
20469
+ experimentOf: consultation.experiment_of,
20470
+ consultationType: consultationType ?? consultation.type ?? "treatment"
20471
+ }), [
20472
+ consultation.hash,
20473
+ consultation.experiment_of,
20474
+ consultation.type,
20475
+ consultationType
20476
+ ]);
20477
+ const emitRef = useRef(onEvent);
20478
+ emitRef.current = onEvent;
20479
+ const submittedShown = useRef([]);
20480
+ const save = useTargetSave({
20481
+ adapter,
20482
+ queueKey,
20483
+ revision,
20484
+ latestDraft: () => formAnswers(form, walk),
20485
+ onDone,
20486
+ onConflict,
20487
+ onSaved,
20488
+ onSubmitted: () => {
20489
+ for (const hash of submittedShown.current) emitRef.current?.(events.questionAnswered(hash));
20490
+ emitRef.current?.(events.completed());
20491
+ }
20492
+ });
20493
+ const busy = save.submitState === "submitting" || save.submitState === "submitted";
20494
+ const started = useRef(false);
20495
+ useEffect(() => {
20496
+ if (started.current || Object.keys(saved.answers ?? {}).length || !visibleQuestions.length) return;
20497
+ started.current = true;
20498
+ emitRef.current?.(events.started());
20499
+ }, []);
20500
+ const viewed = useRef(/* @__PURE__ */ new Set());
20501
+ useEffect(() => {
20502
+ for (const hash of form.shown) {
20503
+ if (viewed.current.has(hash)) continue;
20504
+ viewed.current.add(hash);
20505
+ emitRef.current?.(events.questionViewed(hash));
20506
+ }
20507
+ }, [form.shown, events]);
20508
+ useEffect(() => {
20509
+ if (!focus) return;
20510
+ const frame = requestAnimationFrame(() => container.current?.querySelector(`[id="${CSS.escape(focus.hash)}"]`)?.scrollIntoView?.({ behavior: "smooth" }));
20511
+ return () => cancelAnimationFrame(frame);
20512
+ }, [focus]);
20513
+ const exitHash = form.exit?.questionHash ?? null;
20514
+ useEffect(() => {
20515
+ if (!exitHash) return;
20516
+ emitRef.current?.(events.questionAnswered(exitHash));
20517
+ emitRef.current?.(events.exited());
20518
+ }, [exitHash, events]);
20519
+ const answer = (question, value, logic) => {
20520
+ if (busy) return;
20521
+ const result = applyAnswer({
20522
+ question,
20523
+ state: {
20524
+ answers,
20525
+ optionHashes
20526
+ },
20527
+ value,
20528
+ logic,
20529
+ ownsProgression: false
20530
+ });
20531
+ if (!result) return;
20532
+ setAnswers(result.answers);
20533
+ setOptionHashes(result.optionHashes);
20534
+ setRevision((value) => value + 1);
20535
+ };
20536
+ const submit = () => {
20537
+ if (!adapter) return;
20538
+ setChecked(true);
20539
+ if (form.exit) {
20540
+ save.rejectSubmit("One answer ends the consultation. Change it before you submit.");
20541
+ return;
20542
+ }
20543
+ const open = missingAnswers(form, walk);
20544
+ if (open.length) {
20545
+ save.rejectSubmit(`${open.length} ${open.length === 1 ? "question has" : "questions have"} no answer. Answer them before you submit.`);
20546
+ setFocus((previous) => ({
20547
+ hash: open[0],
20548
+ tick: (previous?.tick ?? 0) + 1
20549
+ }));
20550
+ return;
20551
+ }
20552
+ submittedShown.current = form.shown;
20553
+ save.submit(formAnswers(form, walk));
20554
+ };
20555
+ if (!visibleQuestions.length) return /* @__PURE__ */ jsx("p", {
20556
+ className: "py-6 text-sm text-gray-600",
20557
+ children: "This consultation has no questions."
20558
+ });
20559
+ const context = {
20560
+ consultationHash: consultation.hash,
20561
+ questions: allQuestions,
20562
+ answers,
20563
+ optionHashes,
20564
+ onAnswer: answer,
20565
+ disabled: busy,
20566
+ missing,
20567
+ focus
20568
+ };
20569
+ return /* @__PURE__ */ jsxs("div", {
20570
+ ref: container,
20571
+ className: "flex flex-col gap-[26px] rounded-[32px] border border-gray-200 bg-white p-4 font-normal text-gray-900 sm:p-6",
20572
+ children: [
20573
+ /* @__PURE__ */ jsxs("div", {
20574
+ className: "flex flex-wrap items-center justify-between gap-3",
20575
+ children: [/* @__PURE__ */ jsx("h2", {
20576
+ className: "text-xl font-bold leading-[30px] text-[#111]",
20577
+ children: consultation.title
20578
+ }), adapter && save.submitState !== "submitted" && /* @__PURE__ */ jsxs("div", {
20579
+ className: "flex items-center gap-2",
20580
+ children: [
20581
+ /* @__PURE__ */ jsx("span", {
20582
+ className: "text-xs text-gray-500",
20583
+ "aria-live": "polite",
20584
+ children: save.saveState === "saving" ? "Saving…" : save.saveState === "saved" ? "Draft saved" : ""
20585
+ }),
20586
+ /* @__PURE__ */ jsx(Button, {
20587
+ variant: "outlined",
20588
+ fullWidth: false,
20589
+ disabled: busy,
20590
+ onClick: save.saveDraft,
20591
+ className: "!h-10 !rounded-2xl !border-primary-700 !px-4 !py-0 text-sm !font-bold normal-case",
20592
+ children: "Save draft"
20593
+ }),
20594
+ /* @__PURE__ */ jsx(Button, {
20595
+ fullWidth: false,
20596
+ loading: save.submitState === "submitting",
20597
+ disabled: busy,
20598
+ onClick: submit,
20599
+ className: "!h-10 !rounded-2xl !px-4 !py-0 text-sm !font-bold normal-case !shadow-none",
20600
+ children: submitLabel
20601
+ })
20602
+ ]
20603
+ })]
20604
+ }),
20605
+ save.submitState === "submitted" && /* @__PURE__ */ jsx("p", {
20606
+ className: "text-base font-bold",
20607
+ children: "The answers are submitted."
20608
+ }),
20609
+ save.submitError && /* @__PURE__ */ jsx("p", {
20610
+ className: "text-sm text-error-900",
20611
+ children: save.submitError
20612
+ }),
20613
+ save.saveState === "error" && /* @__PURE__ */ jsx(ErrorNotice, {
20614
+ message: "Your last answer could not be saved.",
20615
+ onRetry: save.saveDraft
20616
+ }),
20617
+ (() => {
20618
+ const keys = blockKeys(form.blocks);
20619
+ return form.blocks.map((block, index) => /* @__PURE__ */ jsx(Block, {
20620
+ block,
20621
+ context
20622
+ }, keys[index]));
20623
+ })(),
20624
+ form.exit && /* @__PURE__ */ jsxs("div", {
20625
+ role: "alert",
20626
+ className: "rounded-2xl border border-warning-300 bg-warning-100 p-4",
20627
+ children: [/* @__PURE__ */ jsx("p", {
20628
+ className: "mb-1 text-sm font-bold",
20629
+ children: form.exit.title || "No suitable treatments available"
20630
+ }), /* @__PURE__ */ jsx("div", {
20631
+ className: "text-sm [&_a]:underline [&_p]:mb-2 [&_ul]:ml-4 [&_ul]:list-disc",
20632
+ dangerouslySetInnerHTML: { __html: sanitize$1(form.exit.content ?? "This answer ends the consultation. Change it to go on.") }
20633
+ })]
20634
+ })
20635
+ ]
20636
+ });
20637
+ };
20638
+ // sdk/src/ui/Knockout.tsx
20639
+ /** Connect's exit page (`ExitConsultation.tsx`) as a screen inside the component. */
20640
+ var Knockout = ({ title, content, onReview, onClose }) => /* @__PURE__ */ jsxs("div", {
20641
+ className: "flex flex-col items-start py-8 max-w-2xl",
20642
+ children: [
20643
+ /* @__PURE__ */ jsx("h1", {
20644
+ className: "text-xl md:text-3xl font-bold mb-4",
20645
+ children: title || "No suitable treatments available"
20646
+ }),
20647
+ /* @__PURE__ */ jsx("div", {
20648
+ className: "text-sm sm:text-base mb-8 [&_a]:underline [&_ul]:list-disc [&_ul]:ml-4 [&_li]:ml-4 [&_b]:font-bold [&_strong]:font-bold [&_p]:mb-3",
20649
+ dangerouslySetInnerHTML: { __html: sanitize$1(content ?? "You are not eligible for this treatment. Please exit the consultation or review your answers.") }
20650
+ }),
20651
+ /* @__PURE__ */ jsx(Button, {
20652
+ className: "w-full mb-4 text-md font-bold normal-case bg-primary-700 rounded-3xl",
20653
+ onClick: onReview,
20654
+ children: "Review answer"
20655
+ }),
20656
+ onClose && /* @__PURE__ */ jsx(Button, {
20657
+ variant: "text",
20658
+ className: "w-full text-sm normal-case font-bold text-gray-900 rounded-3xl",
20659
+ onClick: onClose,
20660
+ children: "Exit Consultation"
20661
+ })
20662
+ ]
20663
+ });
19507
20664
  // sdk/src/ui/ReadOnlyAnswers.tsx
19508
20665
  var isHttpsUrl = (value) => {
19509
20666
  try {
@@ -19571,7 +20728,7 @@ var ReadOnlyAnswers = ({ questions, answers, submittedAt }) => {
19571
20728
  });
19572
20729
  };
19573
20730
  // sdk/src/styles.css?inline
19574
- var styles_default = "*, ::before, ::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}/*\n! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com\n*//*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #E9EAED; /* 2 */\n}\n\n::before,\n::after {\n --tw-content: '';\n}\n\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\n\nhtml,\n:host {\n line-height: 1.5; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n -moz-tab-size: 4; /* 3 */\n -o-tab-size: 4;\n tab-size: 4; /* 3 */\n font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */\n font-feature-settings: normal; /* 5 */\n font-variation-settings: normal; /* 6 */\n -webkit-tap-highlight-color: transparent; /* 7 */\n}\n\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\n\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\n\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; /* 1 */\n font-feature-settings: normal; /* 2 */\n font-variation-settings: normal; /* 3 */\n font-size: 1em; /* 4 */\n}\n\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\n\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-feature-settings: inherit; /* 1 */\n font-variation-settings: inherit; /* 1 */\n font-size: 100%; /* 1 */\n font-weight: inherit; /* 1 */\n line-height: inherit; /* 1 */\n letter-spacing: inherit; /* 1 */\n color: inherit; /* 1 */\n margin: 0; /* 2 */\n padding: 0; /* 3 */\n}\n\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\n\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n -webkit-appearance: button; /* 1 */\n background-color: transparent; /* 2 */\n background-image: none; /* 2 */\n}\n\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n outline: auto;\n}\n\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nlegend {\n padding: 0;\n}\n\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n resize: vertical;\n}\n\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #A0A3A6; /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #A0A3A6; /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\n\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n This can trigger a poorly considered lint error in some tools but is included by design.\n*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden]:where(:not([hidden=\"until-found\"])) {\n display: none;\n}\n\ninput:where([type='text']),input:where(:not([type])),input:where([type='email']),input:where([type='url']),input:where([type='password']),input:where([type='number']),input:where([type='date']),input:where([type='datetime-local']),input:where([type='month']),input:where([type='search']),input:where([type='tel']),input:where([type='time']),input:where([type='week']),select:where([multiple]),textarea,select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: #fff;\n border-color: #6D7684;\n border-width: 1px;\n border-radius: 0px;\n padding-top: 0.5rem;\n padding-right: 0.75rem;\n padding-bottom: 0.5rem;\n padding-left: 0.75rem;\n font-size: 1rem;\n line-height: 1.5rem;\n --tw-shadow: 0 0 #0000;\n}\n\ninput:where([type='text']):focus, input:where(:not([type])):focus, input:where([type='email']):focus, input:where([type='url']):focus, input:where([type='password']):focus, input:where([type='number']):focus, input:where([type='date']):focus, input:where([type='datetime-local']):focus, input:where([type='month']):focus, input:where([type='search']):focus, input:where([type='tel']):focus, input:where([type='time']):focus, input:where([type='week']):focus, select:where([multiple]):focus, textarea:focus, select:focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: #2563eb;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n border-color: #2563eb;\n}\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n color: #6D7684;\n opacity: 1;\n}\n\ninput::placeholder,textarea::placeholder {\n color: #6D7684;\n opacity: 1;\n}\n\n::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n}\n\n::-webkit-date-and-time-value {\n min-height: 1.5em;\n text-align: inherit;\n}\n\n::-webkit-datetime-edit {\n display: inline-flex;\n}\n\n::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {\n padding-top: 0;\n padding-bottom: 0;\n}\n\nselect {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236D7684' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e\");\n background-position: right 0.5rem center;\n background-repeat: no-repeat;\n background-size: 1.5em 1.5em;\n padding-right: 2.5rem;\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n}\n\nselect:where([multiple]),select:where([size]:not([size=\"1\"])) {\n background-image: initial;\n background-position: initial;\n background-repeat: unset;\n background-size: initial;\n padding-right: 0.75rem;\n -webkit-print-color-adjust: unset;\n print-color-adjust: unset;\n}\n\ninput:where([type='checkbox']),input:where([type='radio']) {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n padding: 0;\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n display: inline-block;\n vertical-align: middle;\n background-origin: border-box;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n flex-shrink: 0;\n height: 1rem;\n width: 1rem;\n color: #2563eb;\n background-color: #fff;\n border-color: #6D7684;\n border-width: 1px;\n --tw-shadow: 0 0 #0000;\n}\n\ninput:where([type='checkbox']) {\n border-radius: 0px;\n}\n\ninput:where([type='radio']) {\n border-radius: 100%;\n}\n\ninput:where([type='checkbox']):focus,input:where([type='radio']):focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 2px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: #2563eb;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n\ninput:where([type='checkbox']):checked,input:where([type='radio']):checked {\n border-color: transparent;\n background-color: currentColor;\n background-size: 100% 100%;\n background-position: center;\n background-repeat: no-repeat;\n}\n\ninput:where([type='checkbox']):checked {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e\");\n}\n\n@media (forced-colors: active) {\n\n input:where([type='checkbox']):checked {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='radio']):checked {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e\");\n}\n\n@media (forced-colors: active) {\n\n input:where([type='radio']):checked {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='checkbox']):checked:hover,input:where([type='checkbox']):checked:focus,input:where([type='radio']):checked:hover,input:where([type='radio']):checked:focus {\n border-color: transparent;\n background-color: currentColor;\n}\n\ninput:where([type='checkbox']):indeterminate {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e\");\n border-color: transparent;\n background-color: currentColor;\n background-size: 100% 100%;\n background-position: center;\n background-repeat: no-repeat;\n}\n\n@media (forced-colors: active) {\n\n input:where([type='checkbox']):indeterminate {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='checkbox']):indeterminate:hover,input:where([type='checkbox']):indeterminate:focus {\n border-color: transparent;\n background-color: currentColor;\n}\n\ninput:where([type='file']) {\n background: unset;\n border-color: inherit;\n border-width: 0;\n border-radius: 0;\n padding: 0;\n font-size: unset;\n line-height: inherit;\n}\n\ninput:where([type='file']):focus {\n outline: 1px solid ButtonText;\n outline: 1px auto -webkit-focus-ring-color;\n}\n.container {\n width: 100%;\n}\n@media (min-width: 600px) {\n\n .container {\n max-width: 600px;\n }\n}\n@media (min-width: 768px) {\n\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 900px) {\n\n .container {\n max-width: 900px;\n }\n}\n@media (min-width: 1000px) {\n\n .container {\n max-width: 1000px;\n }\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.visible {\n visibility: visible;\n}\n.absolute {\n position: absolute;\n}\n.relative {\n position: relative;\n}\n.inset-0 {\n inset: 0px;\n}\n.m-\\[1px\\] {\n margin: 1px;\n}\n.mx-auto {\n margin-left: auto;\n margin-right: auto;\n}\n.my-3 {\n margin-top: 0.75rem;\n margin-bottom: 0.75rem;\n}\n.my-4 {\n margin-top: 1rem;\n margin-bottom: 1rem;\n}\n.mb-1 {\n margin-bottom: 0.25rem;\n}\n.mb-10 {\n margin-bottom: 2.5rem;\n}\n.mb-2 {\n margin-bottom: 0.5rem;\n}\n.mb-3 {\n margin-bottom: 0.75rem;\n}\n.mb-4 {\n margin-bottom: 1rem;\n}\n.mb-8 {\n margin-bottom: 2rem;\n}\n.ml-2 {\n margin-left: 0.5rem;\n}\n.mr-auto {\n margin-right: auto;\n}\n.mt-1 {\n margin-top: 0.25rem;\n}\n.mt-4 {\n margin-top: 1rem;\n}\n.mt-6 {\n margin-top: 1.5rem;\n}\n.mt-8 {\n margin-top: 2rem;\n}\n.mt-px {\n margin-top: 1px;\n}\n.block {\n display: block;\n}\n.inline {\n display: inline;\n}\n.flex {\n display: flex;\n}\n.table {\n display: table;\n}\n.hidden {\n display: none;\n}\n.h-1\\.5 {\n height: 0.375rem;\n}\n.h-10 {\n height: 2.5rem;\n}\n.h-24 {\n height: 6rem;\n}\n.h-full {\n height: 100%;\n}\n.min-h-6 {\n min-height: 1.5rem;\n}\n.w-10 {\n width: 2.5rem;\n}\n.w-24 {\n width: 6rem;\n}\n.w-\\[calc\\(50\\%-8px\\)\\] {\n width: calc(50% - 8px);\n}\n.w-full {\n width: 100%;\n}\n.min-w-6 {\n min-width: 1.5rem;\n}\n.max-w-2xl {\n max-width: 42rem;\n}\n.max-w-\\[600px\\] {\n max-width: 600px;\n}\n.flex-1 {\n flex: 1 1 0%;\n}\n.flex-shrink-0 {\n flex-shrink: 0;\n}\n.shrink-0 {\n flex-shrink: 0;\n}\n.rotate-180 {\n --tw-rotate: 180deg;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n@keyframes spin {\n\n to {\n transform: rotate(360deg);\n }\n}\n.animate-spin {\n animation: spin 1s linear infinite;\n}\n.cursor-not-allowed {\n cursor: not-allowed;\n}\n.cursor-pointer {\n cursor: pointer;\n}\n.resize {\n resize: both;\n}\n.scroll-m-9 {\n scroll-margin: 2.25rem;\n}\n.flex-col {\n flex-direction: column;\n}\n.flex-wrap {\n flex-wrap: wrap;\n}\n.items-start {\n align-items: flex-start;\n}\n.\\!items-end {\n align-items: flex-end !important;\n}\n.items-center {\n align-items: center;\n}\n.justify-center {\n justify-content: center;\n}\n.gap-0\\.5 {\n gap: 0.125rem;\n}\n.gap-1 {\n gap: 0.25rem;\n}\n.gap-2 {\n gap: 0.5rem;\n}\n.gap-3 {\n gap: 0.75rem;\n}\n.gap-4 {\n gap: 1rem;\n}\n.gap-5 {\n gap: 1.25rem;\n}\n.self-start {\n align-self: flex-start;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.whitespace-pre-wrap {\n white-space: pre-wrap;\n}\n.break-words {\n overflow-wrap: break-word;\n}\n.rounded-3xl {\n border-radius: 1.5rem;\n}\n.rounded-full {\n border-radius: 9999px;\n}\n.rounded-lg {\n border-radius: 0.5rem;\n}\n.rounded-md {\n border-radius: 0.375rem;\n}\n.border {\n border-width: 1px;\n}\n.border-b {\n border-bottom-width: 1px;\n}\n.border-solid {\n border-style: solid;\n}\n.border-none {\n border-style: none;\n}\n.\\!border-gray-100 {\n --tw-border-opacity: 1 !important;\n border-color: rgb(240 241 243 / var(--tw-border-opacity, 1)) !important;\n}\n.border-error-300 {\n --tw-border-opacity: 1;\n border-color: rgb(255 196 178 / var(--tw-border-opacity, 1));\n}\n.border-gray-200 {\n --tw-border-opacity: 1;\n border-color: rgb(233 234 237 / var(--tw-border-opacity, 1));\n}\n.border-gray-300 {\n --tw-border-opacity: 1;\n border-color: rgb(210 213 217 / var(--tw-border-opacity, 1));\n}\n.border-primary {\n border-color: var(--color-primary-700, #153CB7);\n}\n.border-primary-900 {\n border-color: var(--color-primary-900, #022DB7);\n}\n.border-success {\n --tw-border-opacity: 1;\n border-color: rgb(73 183 21 / var(--tw-border-opacity, 1));\n}\n.\\!bg-primary-100 {\n background-color: var(--color-primary-100, #EBEFFA) !important;\n}\n.bg-error-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(255 224 214 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(240 241 243 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(233 234 237 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(250 250 251 / var(--tw-bg-opacity, 1));\n}\n.bg-primary {\n background-color: var(--color-primary-700, #153CB7);\n}\n.bg-primary-100 {\n background-color: var(--color-primary-100, #EBEFFA);\n}\n.bg-primary-700 {\n background-color: var(--color-primary-700, #153CB7);\n}\n.bg-transparent {\n background-color: transparent;\n}\n.object-cover {\n -o-object-fit: cover;\n object-fit: cover;\n}\n.p-2 {\n padding: 0.5rem;\n}\n.p-4 {\n padding: 1rem;\n}\n.\\!py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n.px-6 {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n}\n.px-\\[10px\\] {\n padding-left: 10px;\n padding-right: 10px;\n}\n.py-16 {\n padding-top: 4rem;\n padding-bottom: 4rem;\n}\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.py-3\\.5 {\n padding-top: 0.875rem;\n padding-bottom: 0.875rem;\n}\n.py-4 {\n padding-top: 1rem;\n padding-bottom: 1rem;\n}\n.py-5 {\n padding-top: 1.25rem;\n padding-bottom: 1.25rem;\n}\n.py-6 {\n padding-top: 1.5rem;\n padding-bottom: 1.5rem;\n}\n.py-8 {\n padding-top: 2rem;\n padding-bottom: 2rem;\n}\n.py-\\[10px\\] {\n padding-top: 10px;\n padding-bottom: 10px;\n}\n.pb-4 {\n padding-bottom: 1rem;\n}\n.pb-\\[calc\\(50\\%-8px\\)\\] {\n padding-bottom: calc(50% - 8px);\n}\n.pr-2 {\n padding-right: 0.5rem;\n}\n.text-left {\n text-align: left;\n}\n.font-primary {\n font-family: var(--font-primary), serif;\n}\n.text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n.text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n.text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.text-xl {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n.text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.font-bold {\n font-weight: 500;\n}\n.font-extrabold {\n font-weight: 600;\n}\n.font-medium {\n font-weight: 300;\n}\n.font-normal {\n font-weight: 400;\n}\n.uppercase {\n text-transform: uppercase;\n}\n.normal-case {\n text-transform: none;\n}\n.tracking-\\[-0\\.176px\\] {\n letter-spacing: -0.176px;\n}\n.tracking-\\[-0\\.27px\\] {\n letter-spacing: -0.27px;\n}\n.\\!text-error {\n --tw-text-opacity: 1 !important;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1)) !important;\n}\n.\\!text-primary-300 {\n color: var(--color-primary-300, #ABBBED) !important;\n}\n.text-black {\n --tw-text-opacity: 1;\n color: rgb(0 0 0 / var(--tw-text-opacity, 1));\n}\n.text-error {\n --tw-text-opacity: 1;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1));\n}\n.text-error-900 {\n --tw-text-opacity: 1;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1));\n}\n.text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(109 118 132 / var(--tw-text-opacity, 1));\n}\n.text-gray-600 {\n --tw-text-opacity: 1;\n color: rgb(98 106 119 / var(--tw-text-opacity, 1));\n}\n.text-gray-700 {\n --tw-text-opacity: 1;\n color: rgb(78 84 94 / var(--tw-text-opacity, 1));\n}\n.text-gray-800 {\n --tw-text-opacity: 1;\n color: rgb(57 62 69 / var(--tw-text-opacity, 1));\n}\n.text-gray-900 {\n --tw-text-opacity: 1;\n color: rgb(36 39 43 / var(--tw-text-opacity, 1));\n}\n.text-primary {\n color: var(--color-primary-700, #153CB7);\n}\n.text-primary-700 {\n color: var(--color-primary-700, #153CB7);\n}\n.text-primary-900 {\n color: var(--color-primary-900, #022DB7);\n}\n.text-secondary {\n color: var(--color-secondary-700, #080F30);\n}\n.text-success {\n --tw-text-opacity: 1;\n color: rgb(73 183 21 / var(--tw-text-opacity, 1));\n}\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n.underline {\n text-decoration-line: underline;\n}\n.no-underline {\n text-decoration-line: none;\n}\n.opacity-25 {\n opacity: 0.25;\n}\n.opacity-60 {\n opacity: 0.6;\n}\n.opacity-75 {\n opacity: 0.75;\n}\n.\\!shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) !important;\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important;\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;\n}\n.\\!shadow-none {\n --tw-shadow: 0 0 #0000 !important;\n --tw-shadow-colored: 0 0 #0000 !important;\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;\n}\n.shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.shadow-md {\n --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.outline-none {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.outline {\n outline-style: solid;\n}\n.outline-1 {\n outline-width: 1px;\n}\n.outline-offset-0 {\n outline-offset: 0px;\n}\n.\\!outline-error {\n outline-color: #FF4D1A !important;\n}\n.outline-gray-300 {\n outline-color: #D2D5D9;\n}\n.\\!ring-0 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color) !important;\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color) !important;\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) !important;\n}\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.transition {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.duration-300 {\n transition-duration: 300ms;\n}\n\n/* Preflight styles html and body, which a shadow root does not have, so the base type goes on the host. */\n:host {\n display: block;\n --font-primary:\n \"Poppins\", system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial,\n sans-serif;\n --font-heading: \"Fraunces\", Georgia, serif;\n font-family: var(--font-primary);\n line-height: 1.5;\n text-align: left;\n color: #24272b;\n -webkit-font-smoothing: antialiased;\n}\n.placeholder\\:text-gray-400::-moz-placeholder {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.placeholder\\:text-gray-400::placeholder {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.focus-within\\:outline-2:focus-within {\n outline-width: 2px;\n}\n.focus-within\\:outline-primary:focus-within {\n outline-color: var(--color-primary-700, #153CB7);\n}\n.hover\\:border-primary-900:hover {\n border-color: var(--color-primary-900, #022DB7);\n}\n.hover\\:bg-gray-200:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(233 234 237 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-gray-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(250 250 251 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-primary-100:hover {\n background-color: var(--color-primary-100, #EBEFFA);\n}\n.hover\\:bg-primary-900:hover {\n background-color: var(--color-primary-900, #022DB7);\n}\n.hover\\:text-primary-900:hover {\n color: var(--color-primary-900, #022DB7);\n}\n.hover\\:outline-gray-700:hover {\n outline-color: #4E545E;\n}\n@media (min-width: 600px) {\n\n .sm\\:mt-14 {\n margin-top: 3.5rem;\n }\n\n .sm\\:text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n }\n\n .sm\\:text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n }\n\n .sm\\:text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n }\n\n .sm\\:text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n }\n}\n@media (min-width: 768px) {\n\n .md\\:mb-10 {\n margin-bottom: 2.5rem;\n }\n\n .md\\:mt-10 {\n margin-top: 2.5rem;\n }\n\n .md\\:mt-12 {\n margin-top: 3rem;\n }\n\n .md\\:text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n }\n\n .md\\:text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n }\n\n .md\\:leading-\\[22px\\] {\n line-height: 22px;\n }\n}\n.\\[\\&_a\\]\\:underline a {\n text-decoration-line: underline;\n}\n.\\[\\&_b\\]\\:font-bold b {\n font-weight: 500;\n}\n.\\[\\&_h3\\]\\:mb-2 h3 {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_h3\\]\\:mt-4 h3 {\n margin-top: 1rem;\n}\n.\\[\\&_h3\\]\\:text-base h3 {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n.\\[\\&_h3\\]\\:font-bold h3 {\n font-weight: 500;\n}\n@media (min-width: 600px) {\n\n .sm\\:\\[\\&_h3\\]\\:text-lg h3 {\n font-size: 1.125rem;\n line-height: 1.75rem;\n }\n}\n.\\[\\&_h4\\]\\:mb-1 h4 {\n margin-bottom: 0.25rem;\n}\n.\\[\\&_h4\\]\\:mt-4 h4 {\n margin-top: 1rem;\n}\n.\\[\\&_h4\\]\\:font-bold h4 {\n font-weight: 500;\n}\n.\\[\\&_img\\]\\:mx-auto img {\n margin-left: auto;\n margin-right: auto;\n}\n.\\[\\&_img\\]\\:my-1 img {\n margin-top: 0.25rem;\n margin-bottom: 0.25rem;\n}\n.\\[\\&_img\\]\\:max-w-full img {\n max-width: 100%;\n}\n.\\[\\&_li\\]\\:ml-4 li {\n margin-left: 1rem;\n}\n.\\[\\&_p\\:not\\(\\:last-child\\)\\]\\:mb-2 p:not(:last-child) {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_p\\]\\:mb-3 p {\n margin-bottom: 0.75rem;\n}\n.\\[\\&_strong\\]\\:font-bold strong {\n font-weight: 500;\n}\n.\\[\\&_ul\\]\\:mb-2 ul {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_ul\\]\\:ml-4 ul {\n margin-left: 1rem;\n}\n.\\[\\&_ul\\]\\:list-disc ul {\n list-style-type: disc;\n}\n";
20731
+ var styles_default = "*, ::before, ::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}/*\n! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com\n*//*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #E9EAED; /* 2 */\n}\n\n::before,\n::after {\n --tw-content: '';\n}\n\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\n\nhtml,\n:host {\n line-height: 1.5; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n -moz-tab-size: 4; /* 3 */\n -o-tab-size: 4;\n tab-size: 4; /* 3 */\n font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */\n font-feature-settings: normal; /* 5 */\n font-variation-settings: normal; /* 6 */\n -webkit-tap-highlight-color: transparent; /* 7 */\n}\n\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\n\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\n\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; /* 1 */\n font-feature-settings: normal; /* 2 */\n font-variation-settings: normal; /* 3 */\n font-size: 1em; /* 4 */\n}\n\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\n\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-feature-settings: inherit; /* 1 */\n font-variation-settings: inherit; /* 1 */\n font-size: 100%; /* 1 */\n font-weight: inherit; /* 1 */\n line-height: inherit; /* 1 */\n letter-spacing: inherit; /* 1 */\n color: inherit; /* 1 */\n margin: 0; /* 2 */\n padding: 0; /* 3 */\n}\n\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\n\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n -webkit-appearance: button; /* 1 */\n background-color: transparent; /* 2 */\n background-image: none; /* 2 */\n}\n\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n outline: auto;\n}\n\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nlegend {\n padding: 0;\n}\n\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n resize: vertical;\n}\n\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #A0A3A6; /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #A0A3A6; /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\n\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n This can trigger a poorly considered lint error in some tools but is included by design.\n*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden]:where(:not([hidden=\"until-found\"])) {\n display: none;\n}\n\ninput:where([type='text']),input:where(:not([type])),input:where([type='email']),input:where([type='url']),input:where([type='password']),input:where([type='number']),input:where([type='date']),input:where([type='datetime-local']),input:where([type='month']),input:where([type='search']),input:where([type='tel']),input:where([type='time']),input:where([type='week']),select:where([multiple]),textarea,select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: #fff;\n border-color: #6D7684;\n border-width: 1px;\n border-radius: 0px;\n padding-top: 0.5rem;\n padding-right: 0.75rem;\n padding-bottom: 0.5rem;\n padding-left: 0.75rem;\n font-size: 1rem;\n line-height: 1.5rem;\n --tw-shadow: 0 0 #0000;\n}\n\ninput:where([type='text']):focus, input:where(:not([type])):focus, input:where([type='email']):focus, input:where([type='url']):focus, input:where([type='password']):focus, input:where([type='number']):focus, input:where([type='date']):focus, input:where([type='datetime-local']):focus, input:where([type='month']):focus, input:where([type='search']):focus, input:where([type='tel']):focus, input:where([type='time']):focus, input:where([type='week']):focus, select:where([multiple]):focus, textarea:focus, select:focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: #2563eb;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n border-color: #2563eb;\n}\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n color: #6D7684;\n opacity: 1;\n}\n\ninput::placeholder,textarea::placeholder {\n color: #6D7684;\n opacity: 1;\n}\n\n::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n}\n\n::-webkit-date-and-time-value {\n min-height: 1.5em;\n text-align: inherit;\n}\n\n::-webkit-datetime-edit {\n display: inline-flex;\n}\n\n::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {\n padding-top: 0;\n padding-bottom: 0;\n}\n\nselect {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236D7684' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e\");\n background-position: right 0.5rem center;\n background-repeat: no-repeat;\n background-size: 1.5em 1.5em;\n padding-right: 2.5rem;\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n}\n\nselect:where([multiple]),select:where([size]:not([size=\"1\"])) {\n background-image: initial;\n background-position: initial;\n background-repeat: unset;\n background-size: initial;\n padding-right: 0.75rem;\n -webkit-print-color-adjust: unset;\n print-color-adjust: unset;\n}\n\ninput:where([type='checkbox']),input:where([type='radio']) {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n padding: 0;\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n display: inline-block;\n vertical-align: middle;\n background-origin: border-box;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n flex-shrink: 0;\n height: 1rem;\n width: 1rem;\n color: #2563eb;\n background-color: #fff;\n border-color: #6D7684;\n border-width: 1px;\n --tw-shadow: 0 0 #0000;\n}\n\ninput:where([type='checkbox']) {\n border-radius: 0px;\n}\n\ninput:where([type='radio']) {\n border-radius: 100%;\n}\n\ninput:where([type='checkbox']):focus,input:where([type='radio']):focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 2px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: #2563eb;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n\ninput:where([type='checkbox']):checked,input:where([type='radio']):checked {\n border-color: transparent;\n background-color: currentColor;\n background-size: 100% 100%;\n background-position: center;\n background-repeat: no-repeat;\n}\n\ninput:where([type='checkbox']):checked {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e\");\n}\n\n@media (forced-colors: active) {\n\n input:where([type='checkbox']):checked {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='radio']):checked {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e\");\n}\n\n@media (forced-colors: active) {\n\n input:where([type='radio']):checked {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='checkbox']):checked:hover,input:where([type='checkbox']):checked:focus,input:where([type='radio']):checked:hover,input:where([type='radio']):checked:focus {\n border-color: transparent;\n background-color: currentColor;\n}\n\ninput:where([type='checkbox']):indeterminate {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e\");\n border-color: transparent;\n background-color: currentColor;\n background-size: 100% 100%;\n background-position: center;\n background-repeat: no-repeat;\n}\n\n@media (forced-colors: active) {\n\n input:where([type='checkbox']):indeterminate {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='checkbox']):indeterminate:hover,input:where([type='checkbox']):indeterminate:focus {\n border-color: transparent;\n background-color: currentColor;\n}\n\ninput:where([type='file']) {\n background: unset;\n border-color: inherit;\n border-width: 0;\n border-radius: 0;\n padding: 0;\n font-size: unset;\n line-height: inherit;\n}\n\ninput:where([type='file']):focus {\n outline: 1px solid ButtonText;\n outline: 1px auto -webkit-focus-ring-color;\n}\n.container {\n width: 100%;\n}\n@media (min-width: 600px) {\n\n .container {\n max-width: 600px;\n }\n}\n@media (min-width: 768px) {\n\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 900px) {\n\n .container {\n max-width: 900px;\n }\n}\n@media (min-width: 1000px) {\n\n .container {\n max-width: 1000px;\n }\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.visible {\n visibility: visible;\n}\n.fixed {\n position: fixed;\n}\n.absolute {\n position: absolute;\n}\n.relative {\n position: relative;\n}\n.inset-0 {\n inset: 0px;\n}\n.inset-x-0 {\n left: 0px;\n right: 0px;\n}\n.bottom-0 {\n bottom: 0px;\n}\n.z-10 {\n z-index: 10;\n}\n.m-\\[1px\\] {\n margin: 1px;\n}\n.mx-auto {\n margin-left: auto;\n margin-right: auto;\n}\n.my-3 {\n margin-top: 0.75rem;\n margin-bottom: 0.75rem;\n}\n.my-4 {\n margin-top: 1rem;\n margin-bottom: 1rem;\n}\n.-ml-px {\n margin-left: -1px;\n}\n.mb-1 {\n margin-bottom: 0.25rem;\n}\n.mb-10 {\n margin-bottom: 2.5rem;\n}\n.mb-2 {\n margin-bottom: 0.5rem;\n}\n.mb-3 {\n margin-bottom: 0.75rem;\n}\n.mb-4 {\n margin-bottom: 1rem;\n}\n.mb-8 {\n margin-bottom: 2rem;\n}\n.ml-1 {\n margin-left: 0.25rem;\n}\n.ml-2 {\n margin-left: 0.5rem;\n}\n.mr-auto {\n margin-right: auto;\n}\n.mt-1 {\n margin-top: 0.25rem;\n}\n.mt-2 {\n margin-top: 0.5rem;\n}\n.mt-3 {\n margin-top: 0.75rem;\n}\n.mt-4 {\n margin-top: 1rem;\n}\n.mt-6 {\n margin-top: 1.5rem;\n}\n.mt-8 {\n margin-top: 2rem;\n}\n.mt-px {\n margin-top: 1px;\n}\n.block {\n display: block;\n}\n.inline {\n display: inline;\n}\n.flex {\n display: flex;\n}\n.inline-flex {\n display: inline-flex;\n}\n.table {\n display: table;\n}\n.hidden {\n display: none;\n}\n.\\!h-10 {\n height: 2.5rem !important;\n}\n.h-0\\.5 {\n height: 0.125rem;\n}\n.h-1\\.5 {\n height: 0.375rem;\n}\n.h-10 {\n height: 2.5rem;\n}\n.h-12 {\n height: 3rem;\n}\n.h-24 {\n height: 6rem;\n}\n.h-\\[18px\\] {\n height: 18px;\n}\n.h-\\[39px\\] {\n height: 39px;\n}\n.h-full {\n height: 100%;\n}\n.min-h-6 {\n min-height: 1.5rem;\n}\n.min-h-\\[121px\\] {\n min-height: 121px;\n}\n.w-10 {\n width: 2.5rem;\n}\n.w-24 {\n width: 6rem;\n}\n.w-\\[18px\\] {\n width: 18px;\n}\n.w-\\[248px\\] {\n width: 248px;\n}\n.w-\\[300px\\] {\n width: 300px;\n}\n.w-\\[calc\\(50\\%-8px\\)\\] {\n width: calc(50% - 8px);\n}\n.w-full {\n width: 100%;\n}\n.w-px {\n width: 1px;\n}\n.min-w-0 {\n min-width: 0px;\n}\n.min-w-6 {\n min-width: 1.5rem;\n}\n.min-w-\\[1000px\\] {\n min-width: 1000px;\n}\n.min-w-\\[220px\\] {\n min-width: 220px;\n}\n.min-w-\\[240px\\] {\n min-width: 240px;\n}\n.min-w-\\[51px\\] {\n min-width: 51px;\n}\n.max-w-2xl {\n max-width: 42rem;\n}\n.max-w-\\[600px\\] {\n max-width: 600px;\n}\n.flex-1 {\n flex: 1 1 0%;\n}\n.flex-shrink-0 {\n flex-shrink: 0;\n}\n.shrink-0 {\n flex-shrink: 0;\n}\n.grow {\n flex-grow: 1;\n}\n.border-collapse {\n border-collapse: collapse;\n}\n.rotate-180 {\n --tw-rotate: 180deg;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n@keyframes spin {\n\n to {\n transform: rotate(360deg);\n }\n}\n.animate-spin {\n animation: spin 1s linear infinite;\n}\n.cursor-not-allowed {\n cursor: not-allowed;\n}\n.cursor-pointer {\n cursor: pointer;\n}\n.resize-y {\n resize: vertical;\n}\n.resize {\n resize: both;\n}\n.scroll-m-9 {\n scroll-margin: 2.25rem;\n}\n.flex-col {\n flex-direction: column;\n}\n.flex-wrap {\n flex-wrap: wrap;\n}\n.items-start {\n align-items: flex-start;\n}\n.\\!items-end {\n align-items: flex-end !important;\n}\n.items-center {\n align-items: center;\n}\n.justify-center {\n justify-content: center;\n}\n.justify-between {\n justify-content: space-between;\n}\n.gap-0\\.5 {\n gap: 0.125rem;\n}\n.gap-1 {\n gap: 0.25rem;\n}\n.gap-2 {\n gap: 0.5rem;\n}\n.gap-3 {\n gap: 0.75rem;\n}\n.gap-4 {\n gap: 1rem;\n}\n.gap-5 {\n gap: 1.25rem;\n}\n.gap-6 {\n gap: 1.5rem;\n}\n.gap-7 {\n gap: 1.75rem;\n}\n.gap-\\[26px\\] {\n gap: 26px;\n}\n.gap-px {\n gap: 1px;\n}\n.self-start {\n align-self: flex-start;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-x-auto {\n overflow-x: auto;\n}\n.whitespace-nowrap {\n white-space: nowrap;\n}\n.whitespace-pre-wrap {\n white-space: pre-wrap;\n}\n.break-words {\n overflow-wrap: break-word;\n}\n.\\!rounded-2xl {\n border-radius: 1rem !important;\n}\n.rounded {\n border-radius: 0.25rem;\n}\n.rounded-2xl {\n border-radius: 1rem;\n}\n.rounded-3xl {\n border-radius: 1.5rem;\n}\n.rounded-\\[32px\\] {\n border-radius: 32px;\n}\n.rounded-full {\n border-radius: 9999px;\n}\n.rounded-lg {\n border-radius: 0.5rem;\n}\n.rounded-md {\n border-radius: 0.375rem;\n}\n.rounded-xl {\n border-radius: 0.75rem;\n}\n.rounded-l-xl {\n border-top-left-radius: 0.75rem;\n border-bottom-left-radius: 0.75rem;\n}\n.rounded-r-xl {\n border-top-right-radius: 0.75rem;\n border-bottom-right-radius: 0.75rem;\n}\n.border {\n border-width: 1px;\n}\n.border-b {\n border-bottom-width: 1px;\n}\n.border-l-2 {\n border-left-width: 2px;\n}\n.border-solid {\n border-style: solid;\n}\n.border-none {\n border-style: none;\n}\n.\\!border-gray-100 {\n --tw-border-opacity: 1 !important;\n border-color: rgb(240 241 243 / var(--tw-border-opacity, 1)) !important;\n}\n.\\!border-primary-700 {\n border-color: var(--color-primary-700, #153CB7) !important;\n}\n.border-error-300 {\n --tw-border-opacity: 1;\n border-color: rgb(255 196 178 / var(--tw-border-opacity, 1));\n}\n.border-gray-200 {\n --tw-border-opacity: 1;\n border-color: rgb(233 234 237 / var(--tw-border-opacity, 1));\n}\n.border-gray-300 {\n --tw-border-opacity: 1;\n border-color: rgb(210 213 217 / var(--tw-border-opacity, 1));\n}\n.border-primary {\n border-color: var(--color-primary-700, #153CB7);\n}\n.border-primary-100 {\n border-color: var(--color-primary-100, #EBEFFA);\n}\n.border-primary-700 {\n border-color: var(--color-primary-700, #153CB7);\n}\n.border-primary-900 {\n border-color: var(--color-primary-900, #022DB7);\n}\n.border-success {\n --tw-border-opacity: 1;\n border-color: rgb(73 183 21 / var(--tw-border-opacity, 1));\n}\n.border-warning-300 {\n --tw-border-opacity: 1;\n border-color: rgb(255 221 153 / var(--tw-border-opacity, 1));\n}\n.\\!bg-primary-100 {\n background-color: var(--color-primary-100, #EBEFFA) !important;\n}\n.bg-error-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(255 224 214 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(240 241 243 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(233 234 237 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(250 250 251 / var(--tw-bg-opacity, 1));\n}\n.bg-primary {\n background-color: var(--color-primary-700, #153CB7);\n}\n.bg-primary-100 {\n background-color: var(--color-primary-100, #EBEFFA);\n}\n.bg-primary-700 {\n background-color: var(--color-primary-700, #153CB7);\n}\n.bg-transparent {\n background-color: transparent;\n}\n.bg-warning-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(255 241 214 / var(--tw-bg-opacity, 1));\n}\n.bg-white {\n --tw-bg-opacity: 1;\n background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n}\n.object-cover {\n -o-object-fit: cover;\n object-fit: cover;\n}\n.p-2 {\n padding: 0.5rem;\n}\n.p-4 {\n padding: 1rem;\n}\n.p-6 {\n padding: 1.5rem;\n}\n.\\!px-4 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n}\n.\\!py-0 {\n padding-top: 0px !important;\n padding-bottom: 0px !important;\n}\n.\\!py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n.px-2 {\n padding-left: 0.5rem;\n padding-right: 0.5rem;\n}\n.px-3\\.5 {\n padding-left: 0.875rem;\n padding-right: 0.875rem;\n}\n.px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\n.px-6 {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n}\n.px-\\[10px\\] {\n padding-left: 10px;\n padding-right: 10px;\n}\n.py-16 {\n padding-top: 4rem;\n padding-bottom: 4rem;\n}\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\n.py-3\\.5 {\n padding-top: 0.875rem;\n padding-bottom: 0.875rem;\n}\n.py-4 {\n padding-top: 1rem;\n padding-bottom: 1rem;\n}\n.py-5 {\n padding-top: 1.25rem;\n padding-bottom: 1.25rem;\n}\n.py-6 {\n padding-top: 1.5rem;\n padding-bottom: 1.5rem;\n}\n.py-8 {\n padding-top: 2rem;\n padding-bottom: 2rem;\n}\n.py-\\[10px\\] {\n padding-top: 10px;\n padding-bottom: 10px;\n}\n.pb-3 {\n padding-bottom: 0.75rem;\n}\n.pb-4 {\n padding-bottom: 1rem;\n}\n.pb-\\[calc\\(50\\%-8px\\)\\] {\n padding-bottom: calc(50% - 8px);\n}\n.pl-4 {\n padding-left: 1rem;\n}\n.pr-10 {\n padding-right: 2.5rem;\n}\n.pr-2 {\n padding-right: 0.5rem;\n}\n.pr-6 {\n padding-right: 1.5rem;\n}\n.text-left {\n text-align: left;\n}\n.text-center {\n text-align: center;\n}\n.align-middle {\n vertical-align: middle;\n}\n.font-primary {\n font-family: var(--font-primary), serif;\n}\n.text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n.text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n.text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.text-xl {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n.text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.\\!font-bold {\n font-weight: 500 !important;\n}\n.font-bold {\n font-weight: 500;\n}\n.font-extrabold {\n font-weight: 600;\n}\n.font-medium {\n font-weight: 300;\n}\n.font-normal {\n font-weight: 400;\n}\n.uppercase {\n text-transform: uppercase;\n}\n.normal-case {\n text-transform: none;\n}\n.leading-5 {\n line-height: 1.25rem;\n}\n.leading-\\[30px\\] {\n line-height: 30px;\n}\n.tracking-\\[-0\\.072px\\] {\n letter-spacing: -0.072px;\n}\n.tracking-\\[-0\\.084px\\] {\n letter-spacing: -0.084px;\n}\n.tracking-\\[-0\\.15px\\] {\n letter-spacing: -0.15px;\n}\n.tracking-\\[-0\\.176px\\] {\n letter-spacing: -0.176px;\n}\n.tracking-\\[-0\\.27px\\] {\n letter-spacing: -0.27px;\n}\n.tracking-\\[-0\\.31px\\] {\n letter-spacing: -0.31px;\n}\n.\\!text-error {\n --tw-text-opacity: 1 !important;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1)) !important;\n}\n.\\!text-primary-300 {\n color: var(--color-primary-300, #ABBBED) !important;\n}\n.text-\\[\\#111\\] {\n --tw-text-opacity: 1;\n color: rgb(17 17 17 / var(--tw-text-opacity, 1));\n}\n.text-\\[\\#1a2f91\\] {\n --tw-text-opacity: 1;\n color: rgb(26 47 145 / var(--tw-text-opacity, 1));\n}\n.text-\\[\\#364153\\] {\n --tw-text-opacity: 1;\n color: rgb(54 65 83 / var(--tw-text-opacity, 1));\n}\n.text-black {\n --tw-text-opacity: 1;\n color: rgb(0 0 0 / var(--tw-text-opacity, 1));\n}\n.text-error {\n --tw-text-opacity: 1;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1));\n}\n.text-error-900 {\n --tw-text-opacity: 1;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1));\n}\n.text-gray-400 {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(109 118 132 / var(--tw-text-opacity, 1));\n}\n.text-gray-600 {\n --tw-text-opacity: 1;\n color: rgb(98 106 119 / var(--tw-text-opacity, 1));\n}\n.text-gray-700 {\n --tw-text-opacity: 1;\n color: rgb(78 84 94 / var(--tw-text-opacity, 1));\n}\n.text-gray-800 {\n --tw-text-opacity: 1;\n color: rgb(57 62 69 / var(--tw-text-opacity, 1));\n}\n.text-gray-900 {\n --tw-text-opacity: 1;\n color: rgb(36 39 43 / var(--tw-text-opacity, 1));\n}\n.text-primary {\n color: var(--color-primary-700, #153CB7);\n}\n.text-primary-700 {\n color: var(--color-primary-700, #153CB7);\n}\n.text-primary-900 {\n color: var(--color-primary-900, #022DB7);\n}\n.text-secondary {\n color: var(--color-secondary-700, #080F30);\n}\n.text-success {\n --tw-text-opacity: 1;\n color: rgb(73 183 21 / var(--tw-text-opacity, 1));\n}\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n.underline {\n text-decoration-line: underline;\n}\n.no-underline {\n text-decoration-line: none;\n}\n.opacity-25 {\n opacity: 0.25;\n}\n.opacity-60 {\n opacity: 0.6;\n}\n.opacity-75 {\n opacity: 0.75;\n}\n.\\!shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) !important;\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important;\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;\n}\n.\\!shadow-none {\n --tw-shadow: 0 0 #0000 !important;\n --tw-shadow-colored: 0 0 #0000 !important;\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;\n}\n.shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.shadow-md {\n --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.outline-none {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.outline {\n outline-style: solid;\n}\n.outline-1 {\n outline-width: 1px;\n}\n.outline-offset-0 {\n outline-offset: 0px;\n}\n.\\!outline-error {\n outline-color: #FF4D1A !important;\n}\n.outline-gray-300 {\n outline-color: #D2D5D9;\n}\n.\\!ring-0 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color) !important;\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color) !important;\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) !important;\n}\n.ring-2 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);\n}\n.ring-error-900 {\n --tw-ring-opacity: 1;\n --tw-ring-color: rgb(255 77 26 / var(--tw-ring-opacity, 1));\n}\n.ring-offset-1 {\n --tw-ring-offset-width: 1px;\n}\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.transition {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.transition-colors {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.duration-300 {\n transition-duration: 300ms;\n}\n\n/* Preflight styles html and body, which a shadow root does not have, so the base type goes on the host. */\n:host {\n display: block;\n --font-primary:\n \"Poppins\", system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial,\n sans-serif;\n --font-heading: \"Fraunces\", Georgia, serif;\n font-family: var(--font-primary);\n line-height: 1.5;\n text-align: left;\n color: #24272b;\n -webkit-font-smoothing: antialiased;\n}\n.placeholder\\:text-gray-400::-moz-placeholder {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.placeholder\\:text-gray-400::placeholder {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.last\\:pr-0:last-child {\n padding-right: 0px;\n}\n.focus-within\\:outline-2:focus-within {\n outline-width: 2px;\n}\n.focus-within\\:outline-primary:focus-within {\n outline-color: var(--color-primary-700, #153CB7);\n}\n.hover\\:border-primary-900:hover {\n border-color: var(--color-primary-900, #022DB7);\n}\n.hover\\:bg-gray-200:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(233 234 237 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-gray-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(250 250 251 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-primary-100:hover {\n background-color: var(--color-primary-100, #EBEFFA);\n}\n.hover\\:bg-primary-900:hover {\n background-color: var(--color-primary-900, #022DB7);\n}\n.hover\\:text-gray-900:hover {\n --tw-text-opacity: 1;\n color: rgb(36 39 43 / var(--tw-text-opacity, 1));\n}\n.hover\\:text-primary-900:hover {\n color: var(--color-primary-900, #022DB7);\n}\n.hover\\:outline-gray-700:hover {\n outline-color: #4E545E;\n}\n.focus\\:border-primary-700:focus {\n border-color: var(--color-primary-700, #153CB7);\n}\n.focus\\:ring-primary-700:focus {\n --tw-ring-color: var(--color-primary-700, #153CB7);\n}\n.disabled\\:opacity-60:disabled {\n opacity: 0.6;\n}\n.has-\\[\\:focus-visible\\]\\:ring-2:has(:focus-visible) {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);\n}\n.has-\\[\\:focus-visible\\]\\:ring-primary-300:has(:focus-visible) {\n --tw-ring-color: var(--color-primary-300, #ABBBED);\n}\n@media (min-width: 600px) {\n\n .sm\\:mt-14 {\n margin-top: 3.5rem;\n }\n\n .sm\\:p-6 {\n padding: 1.5rem;\n }\n\n .sm\\:text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n }\n\n .sm\\:text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n }\n\n .sm\\:text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n }\n\n .sm\\:text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n }\n}\n@media (min-width: 768px) {\n\n .md\\:mb-10 {\n margin-bottom: 2.5rem;\n }\n\n .md\\:mt-10 {\n margin-top: 2.5rem;\n }\n\n .md\\:mt-12 {\n margin-top: 3rem;\n }\n\n .md\\:text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n }\n\n .md\\:text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n }\n\n .md\\:leading-\\[22px\\] {\n line-height: 22px;\n }\n}\n@media (min-width: 900px) {\n\n .lg\\:pr-12 {\n padding-right: 3rem;\n }\n}\n.\\[\\&_a\\]\\:underline a {\n text-decoration-line: underline;\n}\n.\\[\\&_b\\]\\:font-bold b {\n font-weight: 500;\n}\n.\\[\\&_h3\\]\\:mb-2 h3 {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_h3\\]\\:mt-4 h3 {\n margin-top: 1rem;\n}\n.\\[\\&_h3\\]\\:text-base h3 {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n.\\[\\&_h3\\]\\:font-bold h3 {\n font-weight: 500;\n}\n@media (min-width: 600px) {\n\n .sm\\:\\[\\&_h3\\]\\:text-lg h3 {\n font-size: 1.125rem;\n line-height: 1.75rem;\n }\n}\n.\\[\\&_h4\\]\\:mb-1 h4 {\n margin-bottom: 0.25rem;\n}\n.\\[\\&_h4\\]\\:mt-4 h4 {\n margin-top: 1rem;\n}\n.\\[\\&_h4\\]\\:font-bold h4 {\n font-weight: 500;\n}\n.\\[\\&_img\\]\\:mx-auto img {\n margin-left: auto;\n margin-right: auto;\n}\n.\\[\\&_img\\]\\:my-1 img {\n margin-top: 0.25rem;\n margin-bottom: 0.25rem;\n}\n.\\[\\&_img\\]\\:max-w-full img {\n max-width: 100%;\n}\n.\\[\\&_li\\]\\:ml-4 li {\n margin-left: 1rem;\n}\n.\\[\\&_p\\:not\\(\\:last-child\\)\\]\\:mb-2 p:not(:last-child) {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_p\\]\\:m-0 p {\n margin: 0px;\n}\n.\\[\\&_p\\]\\:mb-2 p {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_p\\]\\:mb-3 p {\n margin-bottom: 0.75rem;\n}\n.\\[\\&_strong\\]\\:font-bold strong {\n font-weight: 500;\n}\n.\\[\\&_ul\\]\\:mb-2 ul {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_ul\\]\\:ml-4 ul {\n margin-left: 1rem;\n}\n.\\[\\&_ul\\]\\:list-disc ul {\n list-style-type: disc;\n}\n";
19575
20732
  // sdk/src/ui/ShadowRoot.tsx
19576
20733
  var FONT_LINK_ID = "evaro-consultation-fonts";
19577
20734
  var FONT_URL = "https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600&family=Poppins:wght@300;400;500;600&display=swap";
@@ -19626,7 +20783,8 @@ var EMPTY_SAVED = {
19626
20783
  answers: {},
19627
20784
  status: "empty",
19628
20785
  consultationVersion: null,
19629
- submittedAt: null
20786
+ submittedAt: null,
20787
+ version: null
19630
20788
  };
19631
20789
  var themeStyle = (theme) => {
19632
20790
  const style = {};
@@ -19636,41 +20794,72 @@ var themeStyle = (theme) => {
19636
20794
  }
19637
20795
  return style;
19638
20796
  };
19639
- var errorMessage = (error) => error instanceof Error ? error.message : "Something went wrong";
19640
- var ErrorNotice = ({ message, onRetry }) => /* @__PURE__ */ jsxs("div", {
19641
- role: "alert",
19642
- className: "my-4 flex flex-col gap-3 rounded-lg border border-error-300 bg-error-100 p-4 text-sm text-gray-900",
19643
- children: [/* @__PURE__ */ jsx("p", { children: message }), /* @__PURE__ */ jsx(Button, {
19644
- variant: "outlined",
19645
- fullWidth: false,
19646
- className: "self-start !py-2",
19647
- onClick: onRetry,
19648
- children: "Try again"
19649
- })]
19650
- });
20797
+ var targetApiKey = (environment, nodeApiUrl) => {
20798
+ try {
20799
+ return nodeApiBase(environment, nodeApiUrl);
20800
+ } catch {
20801
+ return `invalid:${environment}:${nodeApiUrl ?? ""}`;
20802
+ }
20803
+ };
19651
20804
  function Consultation(props) {
19652
- const { consultation: hash, vendor, environment, target, getToken, readOnly, preview } = props;
20805
+ const { consultation: hash, vendor, environment, target, getToken, nodeApiUrl, readOnly, preview, layout = "steps" } = props;
19653
20806
  const [loaded, setLoaded] = useState(null);
19654
20807
  const [loadError, setLoadError] = useState(null);
19655
20808
  const [attempt, setAttempt] = useState(0);
19656
20809
  const getTokenRef = useRef(getToken);
19657
20810
  getTokenRef.current = getToken;
19658
20811
  const targetKey = target ? `${target.type}:${target.id}` : "";
19659
- const adapter = useMemo(() => target ? createTargetAdapter(target, environment, () => getTokenRef.current?.() ?? null) : null, [targetKey, environment]);
20812
+ const targetApi = target ? targetApiKey(environment, nodeApiUrl) : "";
20813
+ const adapter = useMemo(() => target ? createTargetAdapter(target, environment, () => getTokenRef.current?.() ?? null, nodeApiUrl) : null, [targetKey, targetApi]);
20814
+ const previewFlag = !!preview;
20815
+ const readOnlyFlag = !!readOnly;
20816
+ const loadKey = JSON.stringify([
20817
+ hash,
20818
+ vendor,
20819
+ environment,
20820
+ previewFlag,
20821
+ targetKey,
20822
+ targetApi,
20823
+ readOnlyFlag,
20824
+ layout
20825
+ ]);
20826
+ const queueKey = JSON.stringify([targetApi, targetKey]);
20827
+ const shownQueueKey = useRef(queueKey);
20828
+ shownQueueKey.current = queueKey;
20829
+ const [conflict, setConflict] = useState(false);
20830
+ const savedOnly = useRef(false);
20831
+ const lastLoaded = useRef(null);
20832
+ const onConflict = useCallback(() => {
20833
+ if (shownQueueKey.current !== queueKey) return;
20834
+ savedOnly.current = true;
20835
+ setConflict(true);
20836
+ setAttempt((value) => value + 1);
20837
+ }, [queueKey]);
20838
+ const onSaved = useCallback(() => {
20839
+ if (shownQueueKey.current === queueKey) setConflict(false);
20840
+ }, [queueKey]);
20841
+ useEffect(() => setConflict(false), [loadKey]);
19660
20842
  useEffect(() => {
19661
20843
  let cancelled = false;
19662
20844
  setLoaded(null);
19663
20845
  setLoadError(null);
20846
+ const afterDrafts = () => draftQueueIdle(queueKey);
20847
+ const reuse = savedOnly.current && lastLoaded.current?.key === loadKey ? lastLoaded.current : null;
20848
+ savedOnly.current = false;
19664
20849
  Promise.resolve().then(() => Promise.all([
19665
- fetchVendorConsultation(environment, hash, vendor, !!preview),
19666
- fetchVendorTheme(environment, vendor).catch(() => null),
19667
- adapter ? adapter.load() : Promise.resolve(EMPTY_SAVED)
20850
+ reuse?.consultation ?? fetchVendorConsultation(environment, hash, vendor, previewFlag),
20851
+ reuse ? reuse.theme : fetchVendorTheme(environment, vendor).catch(() => null),
20852
+ adapter ? afterDrafts().then(() => adapter.load()) : Promise.resolve(EMPTY_SAVED)
19668
20853
  ])).then(([consultation, theme, saved]) => {
19669
- if (!cancelled) setLoaded({
20854
+ if (cancelled) return;
20855
+ lastLoaded.current = {
20856
+ key: loadKey,
20857
+ attempt,
19670
20858
  consultation,
19671
20859
  theme,
19672
20860
  saved
19673
- });
20861
+ };
20862
+ setLoaded(lastLoaded.current);
19674
20863
  }).catch((error) => {
19675
20864
  if (!cancelled) setLoadError(errorMessage(error));
19676
20865
  });
@@ -19681,8 +20870,11 @@ function Consultation(props) {
19681
20870
  hash,
19682
20871
  vendor,
19683
20872
  environment,
19684
- preview,
20873
+ previewFlag,
19685
20874
  adapter,
20875
+ queueKey,
20876
+ readOnlyFlag,
20877
+ layout,
19686
20878
  attempt
19687
20879
  ]);
19688
20880
  return /* @__PURE__ */ jsx(ShadowRoot, {
@@ -19694,25 +20886,40 @@ function Consultation(props) {
19694
20886
  message: `The consultation could not be loaded. ${loadError}`,
19695
20887
  onRetry: () => setAttempt((value) => value + 1)
19696
20888
  }),
19697
- !loaded && !loadError && /* @__PURE__ */ jsx("div", {
20889
+ loaded?.key !== loadKey && !loadError && /* @__PURE__ */ jsx("div", {
19698
20890
  className: "flex justify-center py-16 text-primary",
19699
20891
  "aria-busy": "true",
19700
20892
  children: /* @__PURE__ */ jsx(CircularProgress, { size: 48 })
19701
20893
  }),
19702
- loaded && (readOnly || loaded.saved.status === "submitted" ? /* @__PURE__ */ jsx(ReadOnlyAnswers, {
20894
+ conflict && loaded?.key === loadKey && /* @__PURE__ */ jsx("p", {
20895
+ role: "status",
20896
+ className: "my-4 rounded-lg border border-warning-300 bg-warning-100 p-4 text-sm text-gray-900",
20897
+ children: "The answers changed in another place. The saved answers are shown again."
20898
+ }),
20899
+ loaded?.key === loadKey && (readOnly || loaded.saved.status === "submitted" ? /* @__PURE__ */ jsx(ReadOnlyAnswers, {
19703
20900
  questions: loaded.consultation.questions ?? [],
19704
20901
  answers: loaded.saved.answers,
19705
20902
  submittedAt: loaded.saved.submittedAt
19706
- }) : /* @__PURE__ */ jsx(Flow, {
20903
+ }) : layout === "form" ? /* @__PURE__ */ jsx(FormFlow, {
19707
20904
  ...props,
19708
20905
  loaded,
19709
- adapter
19710
- }))
20906
+ adapter,
20907
+ queueKey,
20908
+ onConflict,
20909
+ onSaved
20910
+ }, `${loadKey}:${loaded.attempt}`) : /* @__PURE__ */ jsx(Flow, {
20911
+ ...props,
20912
+ loaded,
20913
+ adapter,
20914
+ queueKey,
20915
+ onConflict,
20916
+ onSaved
20917
+ }, `${loadKey}:${loaded.attempt}`))
19711
20918
  ]
19712
20919
  })
19713
20920
  });
19714
20921
  }
19715
- var Flow = ({ loaded, adapter, consultationType, onDone, onEvent }) => {
20922
+ var Flow = ({ loaded, adapter, consultationType, onDone, onEvent, queueKey, onConflict, onSaved }) => {
19716
20923
  const { consultation, saved } = loaded;
19717
20924
  const emit = useCallback((event) => onEvent?.(event), [onEvent]);
19718
20925
  const flow = useConsultationFlow({
@@ -19722,78 +20929,28 @@ var Flow = ({ loaded, adapter, consultationType, onDone, onEvent }) => {
19722
20929
  isNewStart: !Object.keys(saved.answers ?? {}).length,
19723
20930
  emit
19724
20931
  });
19725
- const [saveState, setSaveState] = useState("idle");
19726
- const [submitState, setSubmitState] = useState("idle");
19727
- const [submitError, setSubmitError] = useState(null);
19728
- const latestDraft = useRef(null);
19729
- latestDraft.current = () => draftAnswers(flow.history, {
19730
- allQuestions: flow.allQuestions,
19731
- visibleQuestions: flow.questions,
19732
- consultationHash: consultation.hash,
19733
- answers: flow.answers,
19734
- optionHashes: flow.optionHashes
19735
- });
19736
- const draftTimer = useRef(null);
19737
- const draftQueue = useRef(Promise.resolve());
19738
- const closed = useRef(false);
19739
- const draftSeq = useRef(0);
19740
- const saveDraft = useCallback(() => {
19741
- if (!adapter) return;
19742
- const seq = ++draftSeq.current;
19743
- const isLatest = () => seq === draftSeq.current;
19744
- setSaveState("saving");
19745
- const request = draftQueue.current.then(async () => {
19746
- if (closed.current) return false;
19747
- await adapter.saveDraft(latestDraft.current());
19748
- return true;
19749
- });
19750
- draftQueue.current = request.catch(() => void 0);
19751
- request.then((sent) => {
19752
- if (!isLatest()) return;
19753
- setSaveState((state) => sent ? "saved" : state === "saving" ? "idle" : state);
19754
- }, () => {
19755
- if (isLatest()) setSaveState("error");
19756
- });
19757
- }, [adapter]);
19758
- useEffect(() => {
19759
- if (!adapter || flow.revision === 0 || closed.current) return;
19760
- draftTimer.current = setTimeout(saveDraft, 600);
19761
- return () => clearTimeout(draftTimer.current);
19762
- }, [
20932
+ const { saveState, submitState, submitError, saveDraft, ...save } = useTargetSave({
19763
20933
  adapter,
19764
- flow.revision,
19765
- saveDraft
19766
- ]);
19767
- const submit = async () => {
20934
+ queueKey,
20935
+ revision: flow.revision,
20936
+ latestDraft: () => draftAnswers(flow.history, {
20937
+ allQuestions: flow.allQuestions,
20938
+ visibleQuestions: flow.questions,
20939
+ consultationHash: consultation.hash,
20940
+ answers: flow.answers,
20941
+ optionHashes: flow.optionHashes
20942
+ }),
20943
+ onDone,
20944
+ onConflict,
20945
+ onSaved
20946
+ });
20947
+ const submit = () => {
19768
20948
  if (!adapter) return;
19769
20949
  if (!isPathComplete(flow.history, flow.allQuestions, flow.answers, flow.optionHashes)) {
19770
- setSubmitState("error");
19771
- setSubmitError("Some questions have no answer. Go back and answer them before you submit.");
20950
+ save.rejectSubmit("Some questions have no answer. Go back and answer them before you submit.");
19772
20951
  return;
19773
20952
  }
19774
- closed.current = true;
19775
- clearTimeout(draftTimer.current);
19776
- setSubmitState("submitting");
19777
- setSubmitError(null);
19778
- const submitted = () => {
19779
- setSubmitState("submitted");
19780
- setSaveState("idle");
19781
- onDone?.();
19782
- };
19783
- try {
19784
- await draftQueue.current;
19785
- await adapter.submit(answersOnPath(flow.history, flow.allQuestions, flow.answers, flow.optionHashes));
19786
- submitted();
19787
- } catch (error) {
19788
- if (await adapter.load().then((saved) => saved.status, () => null) === "submitted") {
19789
- submitted();
19790
- return;
19791
- }
19792
- setSubmitState("error");
19793
- setSubmitError(`The answers could not be submitted. ${errorMessage(error)}`);
19794
- closed.current = false;
19795
- saveDraft();
19796
- }
20953
+ save.submit(answersOnPath(flow.history, flow.allQuestions, flow.answers, flow.optionHashes));
19797
20954
  };
19798
20955
  const progress = flow.phase === "complete" ? 1 : flow.questions.length ? (flow.questionIndex + 1) / flow.questions.length : 0;
19799
20956
  const busy = submitState === "submitting";