@odla-ai/chapter 0.56.0 → 0.56.2

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.
@@ -111,14 +111,185 @@ function SlotPicker(props) {
111
111
  }
112
112
 
113
113
  // src/ui/member-restart.tsx
114
- import { useEffect as useEffect2, useState as useState5 } from "preact/hooks";
114
+ import { useEffect as useEffect2, useState as useState7 } from "preact/hooks";
115
+
116
+ // src/ui/additional-member-editor.tsx
117
+ import { useRef as useRef2, useState as useState3 } from "preact/hooks";
118
+
119
+ // src/ui/additional-member-fields.tsx
120
+ import { useRef } from "preact/hooks";
121
+ import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
122
+ var ADDITIONAL_MEMBER_COPY = {
123
+ label: "Additional member",
124
+ includedChoice: "Include my additional member at no extra charge.",
125
+ paidChoice: "Add one additional member.",
126
+ includedHint: "Included with your membership at no extra charge.",
127
+ pendingHint: "Your additional-member rate will be confirmed at checkout.",
128
+ editTitle: "Edit additional member details"
129
+ };
130
+ function additionalMemberSelection(value) {
131
+ return value.selected ? { recipientName: value.name.trim(), recipientEmail: value.email.trim() } : null;
132
+ }
133
+ function AdditionalMemberFields({ value, onChange, offer, ...options }) {
134
+ const draft = useRef(value);
135
+ draft.current = value;
136
+ const change = (patch) => {
137
+ draft.current = { ...draft.current, ...patch };
138
+ onChange(draft.current);
139
+ };
140
+ const copy = { ...ADDITIONAL_MEMBER_COPY, ...options.copy };
141
+ const names = { selected: "additionalMemberSelected", name: "additionalMemberName", email: "additionalMemberEmail", ...options.names };
142
+ const price2 = offer && new Intl.NumberFormat("en-US", { style: "currency", currency: offer.currency ?? "USD" }).format(offer.amountCents / 100);
143
+ return /* @__PURE__ */ jsxs2("div", { className: "form-group additional-member-fields", children: [
144
+ /* @__PURE__ */ jsx2("label", { htmlFor: names.selected, children: copy.label }),
145
+ /* @__PURE__ */ jsx2("p", { className: "hint", children: offer?.amountCents === 0 ? copy.includedHint : offer ? `${price2} per ${offer.interval ?? "year"}, paid with your membership.` : copy.pendingHint }),
146
+ /* @__PURE__ */ jsxs2("label", { className: "checkbox-option journey-consent", children: [
147
+ /* @__PURE__ */ jsx2(
148
+ "input",
149
+ {
150
+ type: "checkbox",
151
+ id: names.selected,
152
+ name: names.selected,
153
+ value: "yes",
154
+ checked: value.selected,
155
+ onChange: (event) => change({ selected: event.currentTarget.checked })
156
+ }
157
+ ),
158
+ offer?.amountCents === 0 ? copy.includedChoice : copy.paidChoice
159
+ ] }),
160
+ value.selected ? /* @__PURE__ */ jsxs2("div", { className: "additional-member-details", children: [
161
+ /* @__PURE__ */ jsxs2("label", { htmlFor: names.name, children: [
162
+ "Full name ",
163
+ /* @__PURE__ */ jsx2("span", { className: "req", "aria-hidden": "true", children: "*" })
164
+ ] }),
165
+ /* @__PURE__ */ jsx2(
166
+ "input",
167
+ {
168
+ type: "text",
169
+ id: names.name,
170
+ name: names.name,
171
+ placeholder: "Full name",
172
+ required: true,
173
+ minLength: 2,
174
+ maxLength: 160,
175
+ pattern: ".*\\S.*\\S.*",
176
+ value: value.name,
177
+ onInput: (event) => change({ name: event.currentTarget.value })
178
+ }
179
+ ),
180
+ /* @__PURE__ */ jsxs2("label", { htmlFor: names.email, children: [
181
+ "Email ",
182
+ /* @__PURE__ */ jsx2("span", { className: "req", "aria-hidden": "true", children: "*" })
183
+ ] }),
184
+ /* @__PURE__ */ jsx2(
185
+ "input",
186
+ {
187
+ type: "email",
188
+ id: names.email,
189
+ name: names.email,
190
+ placeholder: "member@example.com",
191
+ required: true,
192
+ pattern: "[^\\s@]+@[^\\s@]+\\.[^\\s@]+",
193
+ maxLength: 254,
194
+ value: value.email,
195
+ onInput: (event) => change({ email: event.currentTarget.value })
196
+ }
197
+ )
198
+ ] }) : null
199
+ ] });
200
+ }
201
+
202
+ // src/ui/form-readiness.ts
203
+ import { useLayoutEffect, useState as useState2 } from "preact/hooks";
204
+ function isFormReady(form) {
205
+ return Array.from(form.elements).every((element) => {
206
+ const field = element;
207
+ return !field.willValidate || field.validity.valid && (!field.required || field.value.trim().length > 0);
208
+ });
209
+ }
210
+ function useFormReadiness(ref) {
211
+ const [ready, setReady] = useState2(false);
212
+ useLayoutEffect(() => {
213
+ const form = ref.current;
214
+ if (!form) return;
215
+ let live = true;
216
+ const update = () => {
217
+ if (live) setReady(isFormReady(form));
218
+ };
219
+ const afterInput = () => queueMicrotask(update);
220
+ update();
221
+ for (const type of ["input", "change", "focusin", "reset"]) form.addEventListener(type, afterInput);
222
+ const observer = new MutationObserver(afterInput);
223
+ observer.observe(form, {
224
+ childList: true,
225
+ subtree: true,
226
+ attributes: true,
227
+ attributeFilter: ["required", "disabled", "pattern", "minlength", "maxlength", "min", "max", "type"]
228
+ });
229
+ return () => {
230
+ live = false;
231
+ observer.disconnect();
232
+ for (const type of ["input", "change", "focusin", "reset"]) form.removeEventListener(type, afterInput);
233
+ };
234
+ });
235
+ return ready;
236
+ }
237
+
238
+ // src/ui/join-submit-button.tsx
239
+ import { jsx as jsx3 } from "preact/jsx-runtime";
240
+ function JoinSubmitButton(props) {
241
+ return /* @__PURE__ */ jsx3("button", { ...props, type: "submit", className: "submit-btn chapter-form-primary" });
242
+ }
243
+
244
+ // src/ui/join-recipient.ts
245
+ function validJoinRecipient(recipient) {
246
+ if (!recipient) return true;
247
+ const name = recipient.recipientName.trim(), email = recipient.recipientEmail.trim();
248
+ return name.length >= 2 && name.length <= 160 && !/[\u0000-\u001f\u007f]/.test(name) && email.length <= 254 && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
249
+ }
250
+
251
+ // src/ui/additional-member-editor.tsx
252
+ import { jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
253
+ function AdditionalMemberEditor({ selection, offer, save, cancel, onSaved, ...options }) {
254
+ const [value, setValue] = useState3({ selected: Boolean(selection), name: selection?.recipientName ?? "", email: selection?.recipientEmail ?? "" });
255
+ const [saving, setSaving] = useState3(false), [error, setError] = useState3("");
256
+ const form = useRef2(null), inFlight = useRef2(false);
257
+ const ready = useFormReadiness(form);
258
+ const valid = validJoinRecipient(additionalMemberSelection(value) ?? void 0);
259
+ return /* @__PURE__ */ jsxs3("form", { ref: form, className: "join-form additional-member-editor", onSubmit: async (event) => {
260
+ event.preventDefault();
261
+ if (inFlight.current || !valid || !isFormReady(event.currentTarget)) return;
262
+ inFlight.current = true;
263
+ setSaving(true);
264
+ setError("");
265
+ try {
266
+ await save(additionalMemberSelection(value));
267
+ onSaved?.(value);
268
+ } catch (failure) {
269
+ setError(failure instanceof Error ? failure.message : "Could not save your changes. Try again.");
270
+ } finally {
271
+ inFlight.current = false;
272
+ setSaving(false);
273
+ }
274
+ }, children: [
275
+ /* @__PURE__ */ jsx4("h2", { children: options.copy?.editTitle ?? ADDITIONAL_MEMBER_COPY.editTitle }),
276
+ /* @__PURE__ */ jsxs3("fieldset", { disabled: saving, children: [
277
+ /* @__PURE__ */ jsx4(AdditionalMemberFields, { ...options, value, onChange: setValue, offer }),
278
+ error ? /* @__PURE__ */ jsx4("p", { role: "alert", children: error }) : null,
279
+ /* @__PURE__ */ jsxs3("div", { className: "journey-actions additional-member-actions", children: [
280
+ /* @__PURE__ */ jsx4(JoinSubmitButton, { disabled: saving || !ready || !valid, children: saving ? "Saving\u2026" : "Save details" }),
281
+ /* @__PURE__ */ jsx4("button", { className: "journey-secondary", type: "button", onClick: cancel, children: "Cancel" })
282
+ ] })
283
+ ] })
284
+ ] });
285
+ }
115
286
 
116
287
  // src/ui/membership-payment-step.tsx
117
- import { useEffect, useState as useState4 } from "preact/hooks";
288
+ import { useEffect, useState as useState6 } from "preact/hooks";
118
289
 
119
290
  // src/ui/payment-step.tsx
120
- import { useRef, useState as useState2 } from "preact/hooks";
121
- import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
291
+ import { useRef as useRef3, useState as useState4 } from "preact/hooks";
292
+ import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
122
293
  var loader = null;
123
294
  function loadStripe() {
124
295
  const existing = globalThis.Stripe;
@@ -152,13 +323,13 @@ function PaymentStep(props) {
152
323
  simulation
153
324
  } = props;
154
325
  const copy = props.copy ?? inheritedCopy.join.payment;
155
- const [status, setStatus] = useState2("idle");
156
- const [error, setError] = useState2(null);
157
- const [lineItems, setLineItems] = useState2(null);
158
- const mountRef = useRef(null);
159
- const stripeRef = useRef(null);
160
- const elementsRef = useRef(null);
161
- const completionRef = useRef({ gift: false });
326
+ const [status, setStatus] = useState4("idle");
327
+ const [error, setError] = useState4(null);
328
+ const [lineItems, setLineItems] = useState4(null);
329
+ const mountRef = useRef3(null);
330
+ const stripeRef = useRef3(null);
331
+ const elementsRef = useRef3(null);
332
+ const completionRef = useRef3({ gift: false });
162
333
  const begin = async () => {
163
334
  setStatus("loading");
164
335
  setError(null);
@@ -228,12 +399,12 @@ function PaymentStep(props) {
228
399
  setStatus("ready");
229
400
  }
230
401
  };
231
- return /* @__PURE__ */ jsxs2("div", { className: "join-pay", children: [
232
- /* @__PURE__ */ jsxs2("div", { className: "compliance-box", children: [
233
- merchantDisclosureText ? /* @__PURE__ */ jsx2("p", { className: "compliance-merchant", children: merchantDisclosureText }) : null,
234
- /* @__PURE__ */ jsx2("p", { className: "compliance-policy", children: refundPolicyText }),
235
- /* @__PURE__ */ jsxs2("label", { className: "compliance-check", children: [
236
- /* @__PURE__ */ jsx2(
402
+ return /* @__PURE__ */ jsxs4("div", { className: "join-pay", children: [
403
+ /* @__PURE__ */ jsxs4("div", { className: "compliance-box", children: [
404
+ merchantDisclosureText ? /* @__PURE__ */ jsx5("p", { className: "compliance-merchant", children: merchantDisclosureText }) : null,
405
+ /* @__PURE__ */ jsx5("p", { className: "compliance-policy", children: refundPolicyText }),
406
+ /* @__PURE__ */ jsxs4("label", { className: "compliance-check", children: [
407
+ /* @__PURE__ */ jsx5(
237
408
  "input",
238
409
  {
239
410
  type: "checkbox",
@@ -243,52 +414,55 @@ function PaymentStep(props) {
243
414
  }
244
415
  }
245
416
  ),
246
- copy.consent ? /* @__PURE__ */ jsx2("span", { children: copy.consent }) : null
417
+ copy.consent ? /* @__PURE__ */ jsx5("span", { children: copy.consent }) : null
247
418
  ] })
248
419
  ] }),
249
- status === "loading" ? /* @__PURE__ */ jsx2("p", { className: "pay-status", children: copy.preparing }) : null,
420
+ status === "loading" ? /* @__PURE__ */ jsx5("p", { className: "pay-status", children: copy.preparing }) : null,
250
421
  lineItems && renderPriceLines ? renderPriceLines(lineItems) : null,
251
422
  children,
252
- simulation && (status === "ready" || status === "confirming") ? /* @__PURE__ */ jsxs2("fieldset", { className: "join-pay-simulation", children: [
253
- /* @__PURE__ */ jsx2("legend", { children: "Payment details" }),
254
- /* @__PURE__ */ jsxs2("label", { children: [
423
+ simulation && (status === "ready" || status === "confirming") ? /* @__PURE__ */ jsxs4("fieldset", { className: "join-pay-simulation", children: [
424
+ /* @__PURE__ */ jsx5("legend", { children: "Payment details" }),
425
+ /* @__PURE__ */ jsxs4("label", { children: [
255
426
  "Card number",
256
- /* @__PURE__ */ jsx2("input", { name: "simulationCardNumber", inputMode: "numeric", autoComplete: "off", defaultValue: "4242 4242 4242 4242" })
427
+ /* @__PURE__ */ jsx5("input", { name: "simulationCardNumber", inputMode: "numeric", autoComplete: "off", defaultValue: "4242 4242 4242 4242" })
257
428
  ] }),
258
- /* @__PURE__ */ jsxs2("label", { children: [
429
+ /* @__PURE__ */ jsxs4("label", { children: [
259
430
  "Expiration",
260
- /* @__PURE__ */ jsx2("input", { name: "simulationCardExpiry", inputMode: "numeric", autoComplete: "off", defaultValue: "12 / 34" })
431
+ /* @__PURE__ */ jsx5("input", { name: "simulationCardExpiry", inputMode: "numeric", autoComplete: "off", defaultValue: "12 / 34" })
261
432
  ] }),
262
- /* @__PURE__ */ jsxs2("label", { children: [
433
+ /* @__PURE__ */ jsxs4("label", { children: [
263
434
  "Security code",
264
- /* @__PURE__ */ jsx2("input", { name: "simulationCardCvc", inputMode: "numeric", autoComplete: "off", defaultValue: "123" })
435
+ /* @__PURE__ */ jsx5("input", { name: "simulationCardCvc", inputMode: "numeric", autoComplete: "off", defaultValue: "123" })
265
436
  ] }),
266
- /* @__PURE__ */ jsx2("p", { children: "This is a simulation. No card data is sent and no charge is created." })
437
+ /* @__PURE__ */ jsx5("p", { children: "This is a simulation. No card data is sent and no charge is created." })
267
438
  ] }) : null,
268
- /* @__PURE__ */ jsx2("div", { ref: mountRef, hidden: status === "idle" || status === "loading" }),
269
- status === "ready" || status === "confirming" ? /* @__PURE__ */ jsx2("button", { className: "submit-btn", disabled: status === "confirming", onClick: () => void pay(), children: status === "confirming" ? copy.processing : copy.payAndContinue }) : null,
270
- error ? /* @__PURE__ */ jsx2("p", { className: "pay-error", children: error }) : null
439
+ /* @__PURE__ */ jsx5("div", { ref: mountRef, hidden: status === "idle" || status === "loading" }),
440
+ status === "ready" || status === "confirming" ? /* @__PURE__ */ jsx5("button", { className: "submit-btn", disabled: status === "confirming", onClick: () => void pay(), children: status === "confirming" ? copy.processing : copy.payAndContinue }) : null,
441
+ error ? /* @__PURE__ */ jsx5("p", { className: "pay-error", children: error }) : null
271
442
  ] });
272
443
  }
273
444
 
274
445
  // src/ui/checkout-seat-choice.tsx
275
- import { useState as useState3 } from "preact/hooks";
276
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
446
+ import { useRef as useRef4, useState as useState5 } from "preact/hooks";
447
+ import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
277
448
  function CheckoutSeatChoice(props) {
278
449
  const saved = props.quote.namedSeat;
279
- const [selected, setSelected] = useState3(Boolean(saved || props.initial));
280
- const [name, setName] = useState3(saved?.recipientName ?? props.initial?.recipientName ?? "");
281
- const [email, setEmail] = useState3(saved?.recipientEmail ?? props.initial?.recipientEmail ?? "");
282
- const [saving, setSaving] = useState3(false), [error, setError] = useState3("");
283
- const amount = props.quote.seatOffer.amountCents;
284
- const money = new Intl.NumberFormat("en-US", { style: "currency", currency: props.quote.currency }).format(amount / 100);
450
+ const [selected, setSelected] = useState5(Boolean(saved || props.initial));
451
+ const [name, setName] = useState5(saved?.recipientName ?? props.initial?.recipientName ?? "");
452
+ const [email, setEmail] = useState5(saved?.recipientEmail ?? props.initial?.recipientEmail ?? "");
453
+ const [saving, setSaving] = useState5(false), [error, setError] = useState5("");
454
+ const form = useRef4(null), inFlight = useRef4(false);
455
+ const ready = useFormReadiness(form);
456
+ const valid = validJoinRecipient(selected ? { recipientName: name, recipientEmail: email } : void 0);
285
457
  const dirty = selected !== Boolean(saved) || selected && (name !== saved?.recipientName || email !== saved?.recipientEmail);
286
458
  const edit = (nextSelected, nextName = name, nextEmail = email) => {
287
459
  props.onEditing(nextSelected !== Boolean(saved) || nextSelected && (nextName !== saved?.recipientName || nextEmail !== saved?.recipientEmail));
288
460
  setError("");
289
461
  };
290
- return /* @__PURE__ */ jsx3("section", { className: "checkout-seat-choice", "aria-label": "Additional member", children: /* @__PURE__ */ jsxs3("form", { onSubmit: async (event) => {
462
+ return /* @__PURE__ */ jsx6("section", { className: "checkout-seat-choice", "aria-label": "Additional member", children: /* @__PURE__ */ jsxs5("form", { ref: form, onSubmit: async (event) => {
291
463
  event.preventDefault();
464
+ if (props.locked || inFlight.current || !valid || !isFormReady(event.currentTarget)) return;
465
+ inFlight.current = true;
292
466
  setSaving(true);
293
467
  setError("");
294
468
  try {
@@ -296,79 +470,93 @@ function CheckoutSeatChoice(props) {
296
470
  } catch (error2) {
297
471
  setError(error2 instanceof Error ? error2.message : "Could not save your selection. Try again.");
298
472
  } finally {
473
+ inFlight.current = false;
299
474
  setSaving(false);
300
475
  }
301
476
  }, children: [
302
- /* @__PURE__ */ jsxs3("fieldset", { disabled: props.locked || saving, children: [
303
- /* @__PURE__ */ jsx3("legend", { children: amount === 0 ? "Included additional member" : "Add an additional member" }),
304
- /* @__PURE__ */ jsx3("p", { children: amount === 0 ? "Your membership includes one additional member at no extra charge." : `Additional-member rate: ${money} per ${props.quote.interval}, paid together with your membership.` }),
305
- /* @__PURE__ */ jsxs3("label", { children: [
306
- /* @__PURE__ */ jsx3("input", { type: "checkbox", checked: selected, onChange: (event) => {
307
- setSelected(event.currentTarget.checked);
308
- edit(event.currentTarget.checked);
309
- } }),
310
- amount === 0 ? "Include my additional member" : "Add one member to my payment"
311
- ] }),
312
- selected ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
313
- /* @__PURE__ */ jsxs3("label", { children: [
314
- "Full name",
315
- /* @__PURE__ */ jsx3(
316
- "input",
317
- {
318
- required: true,
319
- minLength: 2,
320
- maxLength: 160,
321
- value: name,
322
- onInput: (event) => {
323
- setName(event.currentTarget.value);
324
- edit(selected, event.currentTarget.value);
325
- }
326
- }
327
- )
328
- ] }),
329
- /* @__PURE__ */ jsxs3("label", { children: [
330
- "Email",
331
- /* @__PURE__ */ jsx3(
332
- "input",
333
- {
334
- required: true,
335
- type: "email",
336
- maxLength: 254,
337
- value: email,
338
- onInput: (event) => {
339
- setEmail(event.currentTarget.value);
340
- edit(selected, name, event.currentTarget.value);
341
- }
342
- }
343
- )
344
- ] }),
345
- /* @__PURE__ */ jsx3("p", { children: "For one named person, who completes their own application and any required interview. Non-transferable; renews and ends with your membership." })
346
- ] }) : /* @__PURE__ */ jsx3("p", { children: "You can add someone from your member page after paying." }),
347
- dirty ? /* @__PURE__ */ jsx3("button", { type: "submit", disabled: saving, children: saving ? "Saving\u2026" : amount === 0 ? "Save member details" : "Update total" }) : null
477
+ /* @__PURE__ */ jsxs5("fieldset", { disabled: props.locked || saving, children: [
478
+ /* @__PURE__ */ jsx6(
479
+ AdditionalMemberFields,
480
+ {
481
+ value: { selected, name, email },
482
+ offer: { ...props.quote.seatOffer, currency: props.quote.currency, interval: props.quote.interval },
483
+ onChange: (value) => {
484
+ setSelected(value.selected);
485
+ setName(value.name);
486
+ setEmail(value.email);
487
+ edit(value.selected, value.name, value.email);
488
+ }
489
+ }
490
+ ),
491
+ selected ? /* @__PURE__ */ jsx6("p", { children: "For one named person, who completes their own application and any required interview. Non-transferable; renews and ends with your membership." }) : /* @__PURE__ */ jsx6("p", { children: "You can add someone from your member page after paying." }),
492
+ dirty ? /* @__PURE__ */ jsx6(JoinSubmitButton, { disabled: saving || !ready || !valid, children: saving ? "Saving\u2026" : "Save details" }) : null
348
493
  ] }),
349
- error ? /* @__PURE__ */ jsx3("p", { role: "alert", children: error }) : null
494
+ error ? /* @__PURE__ */ jsx6("p", { role: "alert", children: error }) : null
350
495
  ] }) });
351
496
  }
352
497
 
498
+ // src/ui/checkout-seat-review.tsx
499
+ import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
500
+ function CheckoutSeatReview({ quote, label, locked, onEdit }) {
501
+ if (!quote.namedSeat) return null;
502
+ return /* @__PURE__ */ jsxs6("section", { className: "checkout-seat-review", "aria-label": label, children: [
503
+ /* @__PURE__ */ jsx7("h3", { children: label }),
504
+ /* @__PURE__ */ jsxs6("p", { children: [
505
+ quote.namedSeat.recipientName,
506
+ /* @__PURE__ */ jsx7("br", {}),
507
+ quote.namedSeat.recipientEmail
508
+ ] }),
509
+ !locked ? /* @__PURE__ */ jsx7("button", { type: "button", onClick: onEdit, children: "Edit details" }) : null
510
+ ] });
511
+ }
512
+
353
513
  // src/ui/membership-payment-step.tsx
354
- import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
514
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
355
515
  function MembershipPaymentStep(props) {
516
+ const renderEditor = props.renderNamedSeatEditor ?? (props.additionalMember ? (context) => /* @__PURE__ */ jsx8(AdditionalMemberEditor, { ...props.additionalMember, ...context }) : void 0);
356
517
  const inherited = useChapterCopy();
357
518
  const copy = props.copy ?? inherited.join.payment;
358
- const [quote, setQuote] = useState4(null);
359
- const [locked, setLocked] = useState4(false), [editing, setEditing] = useState4(Boolean(props.initialNamedSeat));
360
- const [prefillConsumed, setPrefillConsumed] = useState4(false);
361
- const [error, setError] = useState4(false);
362
- const [revision, setRevision] = useState4(0);
519
+ const [quote, setQuote] = useState6(null);
520
+ const [locked, setLocked] = useState6(false), [editing, setEditing] = useState6(Boolean(props.initialNamedSeat));
521
+ const [prefillConsumed, setPrefillConsumed] = useState6(false);
522
+ const [error, setError] = useState6(false);
523
+ const [revision, setRevision] = useState6(0);
524
+ useEffect(() => {
525
+ props.onNamedSeatEditingChange?.(Boolean(renderEditor && editing && !locked && quote));
526
+ }, [editing, locked, Boolean(quote), Boolean(renderEditor)]);
363
527
  useEffect(() => {
364
528
  let live = true;
365
529
  setQuote(null);
366
530
  setError(false);
367
531
  void fetch(`/api/payments/quote?application=${encodeURIComponent(props.applicationId)}`, { method: "POST", cache: "no-store" }).then(async (response) => {
368
532
  if (!response.ok) throw new Error("quote unavailable");
369
- const value = await response.json();
533
+ let value = await response.json();
370
534
  value.checkoutStarted = response.headers.get("x-chapter-checkout-started") === "true";
371
535
  if (value.applicationId !== props.applicationId || !/^[a-f0-9]{64}$/.test(value.digest) || !/^[a-z]{3}$/.test(value.currency) || !Number.isSafeInteger(value.lineItems?.dueTodayCents) || value.lineItems.dueTodayCents <= 0 || typeof value.refundPolicyText !== "string") throw new Error("invalid quote");
536
+ if (!live) return;
537
+ if (renderEditor && props.initialNamedSeat && !prefillConsumed && !value.namedSeat && !value.checkoutStarted && value.seatOffer) {
538
+ const saved = await fetch(`/api/payments/quote?application=${encodeURIComponent(props.applicationId)}`, {
539
+ method: "POST",
540
+ cache: "no-store",
541
+ headers: { "content-type": "application/json" },
542
+ body: JSON.stringify({ namedSeat: props.initialNamedSeat })
543
+ });
544
+ if (!saved.ok) {
545
+ if (saved.status === 409) {
546
+ if (live) setRevision((current) => current + 1);
547
+ return;
548
+ }
549
+ if (live) {
550
+ setQuote(value);
551
+ setEditing(true);
552
+ setError(true);
553
+ }
554
+ return;
555
+ }
556
+ value = await saved.json();
557
+ if (value.applicationId !== props.applicationId || !/^[a-f0-9]{64}$/.test(value.digest) || !value.namedSeat) throw new Error("invalid quote");
558
+ if (live) setPrefillConsumed(true);
559
+ }
372
560
  if (live) {
373
561
  setQuote(value);
374
562
  setLocked(Boolean(value.checkoutStarted));
@@ -385,10 +573,10 @@ function MembershipPaymentStep(props) {
385
573
  setQuote(null);
386
574
  setRevision((value) => value + 1);
387
575
  };
388
- if (!quote || quote.applicationId !== props.applicationId) return /* @__PURE__ */ jsxs4("div", { className: "join-pay", role: "status", children: [
389
- props.merchantDisclosureText ? /* @__PURE__ */ jsx4("p", { className: "compliance-merchant", children: props.merchantDisclosureText }) : null,
390
- /* @__PURE__ */ jsx4("p", { children: error ? copy.setupFailed : copy.preparing }),
391
- error ? /* @__PURE__ */ jsx4("button", { type: "button", onClick: refresh, children: "Reload payment terms" }) : null
576
+ if (!quote || quote.applicationId !== props.applicationId) return /* @__PURE__ */ jsxs7("div", { className: "join-pay", role: "status", children: [
577
+ props.merchantDisclosureText ? /* @__PURE__ */ jsx8("p", { className: "compliance-merchant", children: props.merchantDisclosureText }) : null,
578
+ /* @__PURE__ */ jsx8("p", { children: error ? copy.setupFailed : copy.preparing }),
579
+ error ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: refresh, children: "Reload payment terms" }) : null
392
580
  ] });
393
581
  const startCheckout = async () => {
394
582
  setLocked(true);
@@ -424,10 +612,33 @@ function MembershipPaymentStep(props) {
424
612
  if (value.applicationId !== props.applicationId || !/^[a-f0-9]{64}$/.test(value.digest)) throw new Error(copy.setupFailed);
425
613
  setQuote(value);
426
614
  setEditing(false);
615
+ setError(false);
427
616
  setPrefillConsumed(true);
428
617
  };
429
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
430
- quote.seatOffer ? /* @__PURE__ */ jsx4(
618
+ const pickLater = !quote.namedSeat && quote.seatOffer?.amountCents === 0;
619
+ if (renderEditor && editing && !locked && quote.seatOffer) return /* @__PURE__ */ jsxs7(Fragment2, { children: [
620
+ error ? /* @__PURE__ */ jsx8("p", { role: "alert", children: "Enter a different recipient's full name and valid email to continue." }) : null,
621
+ renderEditor({
622
+ selection: quote.namedSeat ?? (!prefillConsumed ? props.initialNamedSeat : void 0) ?? null,
623
+ offer: { ...quote.seatOffer, currency: quote.currency, interval: quote.interval },
624
+ save: changeSeat,
625
+ cancel: () => {
626
+ setEditing(false);
627
+ setError(false);
628
+ setPrefillConsumed(true);
629
+ }
630
+ })
631
+ ] });
632
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
633
+ renderEditor ? /* @__PURE__ */ jsx8(
634
+ CheckoutSeatReview,
635
+ {
636
+ quote,
637
+ label: props.namedSeatLabel ?? "Additional member",
638
+ locked,
639
+ onEdit: () => setEditing(true)
640
+ }
641
+ ) : quote.seatOffer ? /* @__PURE__ */ jsx8(
431
642
  CheckoutSeatChoice,
432
643
  {
433
644
  quote,
@@ -438,47 +649,52 @@ function MembershipPaymentStep(props) {
438
649
  },
439
650
  quote.digest
440
651
  ) : null,
441
- /* @__PURE__ */ jsxs4("section", { className: "payment-quote", "aria-label": "Membership price", children: [
442
- /* @__PURE__ */ jsx4("h3", { children: quote.tierName }),
443
- quote.founding ? /* @__PURE__ */ jsx4("p", { className: "payment-founding", role: "note", "aria-label": "Founding member", children: copy.foundingMember }) : null,
652
+ /* @__PURE__ */ jsxs7("section", { className: "payment-quote", "aria-label": "Membership price", children: [
653
+ /* @__PURE__ */ jsx8("h3", { children: quote.tierName }),
654
+ quote.founding ? /* @__PURE__ */ jsx8("p", { className: "payment-founding", role: "note", "aria-label": "Founding member", children: copy.foundingMember }) : null,
444
655
  props.renderPriceLines ? props.renderPriceLines({
445
656
  ...quote.lineItems,
446
657
  ...quote.founding ? { founding: quote.founding } : {},
658
+ ...pickLater ? { namedSeatPickLater: true } : {},
447
659
  ...quote.presentation ? { presentation: quote.presentation } : {}
448
- }) : /* @__PURE__ */ jsxs4("dl", { children: [
449
- quote.lineItems.discountCents > 0 && !quote.founding && quote.presentation ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
450
- /* @__PURE__ */ jsx4("dt", { children: copy.standardRate }),
451
- /* @__PURE__ */ jsxs4("dd", { children: [
660
+ }) : /* @__PURE__ */ jsxs7("dl", { children: [
661
+ quote.lineItems.discountCents > 0 && !quote.founding && quote.presentation ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
662
+ /* @__PURE__ */ jsx8("dt", { children: copy.standardRate }),
663
+ /* @__PURE__ */ jsxs7("dd", { children: [
452
664
  amount(quote.lineItems.standardCents),
453
665
  " per ",
454
666
  quote.interval
455
667
  ] }),
456
- /* @__PURE__ */ jsx4("dt", { children: copy.yourRate }),
457
- /* @__PURE__ */ jsxs4("dd", { children: [
668
+ /* @__PURE__ */ jsx8("dt", { children: copy.yourRate }),
669
+ /* @__PURE__ */ jsxs7("dd", { children: [
458
670
  amount(quote.lineItems.dueTodayCents - (quote.lineItems.namedSeatCents ?? 0)),
459
671
  " per ",
460
672
  quote.interval
461
673
  ] })
462
- ] }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
463
- /* @__PURE__ */ jsx4("dt", { children: "Membership price" }),
464
- /* @__PURE__ */ jsxs4("dd", { children: [
674
+ ] }) : /* @__PURE__ */ jsxs7(Fragment2, { children: [
675
+ /* @__PURE__ */ jsx8("dt", { children: "Membership price" }),
676
+ /* @__PURE__ */ jsxs7("dd", { children: [
465
677
  amount(quote.lineItems.standardCents),
466
678
  " per ",
467
679
  quote.interval
468
680
  ] }),
469
- quote.lineItems.discountCents > 0 ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
470
- /* @__PURE__ */ jsx4("dt", { children: quote.founding ? copy.foundingDiscount : "Discount" }),
471
- /* @__PURE__ */ jsx4("dd", { children: amount(quote.lineItems.discountCents) })
681
+ quote.lineItems.discountCents > 0 ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
682
+ /* @__PURE__ */ jsx8("dt", { children: quote.founding ? copy.foundingDiscount : "Discount" }),
683
+ /* @__PURE__ */ jsx8("dd", { children: amount(quote.lineItems.discountCents) })
472
684
  ] }) : null
473
685
  ] }),
474
- quote.namedSeat ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
475
- /* @__PURE__ */ jsx4("dt", { children: "Additional member" }),
476
- /* @__PURE__ */ jsx4("dd", { children: quote.lineItems.namedSeatCents === 0 ? "Included" : amount(quote.lineItems.namedSeatCents) })
686
+ quote.namedSeat ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
687
+ /* @__PURE__ */ jsx8("dt", { children: "Additional member" }),
688
+ /* @__PURE__ */ jsx8("dd", { children: quote.lineItems.namedSeatCents === 0 ? "Included" : amount(quote.lineItems.namedSeatCents) })
689
+ ] }) : null,
690
+ pickLater ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
691
+ /* @__PURE__ */ jsx8("dt", { children: "Additional member" }),
692
+ /* @__PURE__ */ jsx8("dd", { children: "Pick later" })
477
693
  ] }) : null,
478
- /* @__PURE__ */ jsx4("dt", { children: "Due today" }),
479
- /* @__PURE__ */ jsx4("dd", { children: amount(quote.lineItems.dueTodayCents) })
694
+ /* @__PURE__ */ jsx8("dt", { children: "Due today" }),
695
+ /* @__PURE__ */ jsx8("dd", { children: amount(quote.lineItems.dueTodayCents) })
480
696
  ] }),
481
- quote.namedSeat ? /* @__PURE__ */ jsxs4("p", { children: [
697
+ quote.namedSeat && !renderEditor ? /* @__PURE__ */ jsxs7("p", { children: [
482
698
  "For ",
483
699
  quote.namedSeat.recipientName,
484
700
  " \xB7 ",
@@ -486,8 +702,8 @@ function MembershipPaymentStep(props) {
486
702
  ".",
487
703
  props.renderPriceLines ? ` Additional member: ${quote.lineItems.namedSeatCents === 0 ? "included" : amount(quote.lineItems.namedSeatCents)} in the total above.` : ""
488
704
  ] }) : null,
489
- quote.commercial ? /* @__PURE__ */ jsxs4("div", { role: "note", "aria-label": "Renewal terms", children: [
490
- quote.commercial.initialTermEndsAt !== void 0 ? /* @__PURE__ */ jsxs4("p", { children: [
705
+ quote.commercial ? /* @__PURE__ */ jsxs7("div", { role: "note", "aria-label": "Renewal terms", children: [
706
+ quote.commercial.initialTermEndsAt !== void 0 ? /* @__PURE__ */ jsxs7("p", { children: [
491
707
  "First renewal: ",
492
708
  new Intl.DateTimeFormat("en-US", {
493
709
  dateStyle: "long",
@@ -495,19 +711,23 @@ function MembershipPaymentStep(props) {
495
711
  }).format(quote.commercial.initialTermEndsAt),
496
712
  "."
497
713
  ] }) : null,
498
- /* @__PURE__ */ jsxs4("p", { children: [
714
+ /* @__PURE__ */ jsxs7("p", { children: [
499
715
  "Renews automatically at ",
500
716
  amount(quote.commercial.renewalCents),
501
717
  " per ",
502
718
  quote.interval,
503
719
  " unless canceled before renewal."
504
720
  ] }),
505
- quote.commercial.discountDuration === "first_term" ? /* @__PURE__ */ jsx4("p", { children: "The discount applies to your first paid term only." }) : null,
506
- quote.commercial.discountDuration === "while_active" ? /* @__PURE__ */ jsx4("p", { children: "The discounted rate continues while this membership stays active." }) : null
721
+ quote.commercial.discountDuration === "first_term" ? /* @__PURE__ */ jsx8("p", { children: "The discount applies to your first paid term only." }) : null,
722
+ quote.commercial.discountDuration === "while_active" ? /* @__PURE__ */ jsx8("p", { children: "The discounted rate continues while this membership stays active." }) : null
507
723
  ] }) : null,
508
- quote.trustCopy ? /* @__PURE__ */ jsx4("p", { className: "pay-trust", children: quote.trustCopy }) : null
724
+ quote.trustCopy ? /* @__PURE__ */ jsx8("p", { className: "pay-trust", children: quote.trustCopy }) : null
509
725
  ] }),
510
- !editing ? /* @__PURE__ */ jsx4(
726
+ quote.namedSeat && renderEditor ? /* @__PURE__ */ jsxs7("details", { className: "checkout-seat-terms", children: [
727
+ /* @__PURE__ */ jsx8("summary", { children: "Additional-member terms" }),
728
+ /* @__PURE__ */ jsx8("p", { children: "For one named person, who completes their own application and any required interview. Non-transferable; renews and ends with your membership." })
729
+ ] }) : null,
730
+ !editing ? /* @__PURE__ */ jsx8(
511
731
  PaymentStep,
512
732
  {
513
733
  ...props,
@@ -518,12 +738,12 @@ function MembershipPaymentStep(props) {
518
738
  startCheckout
519
739
  },
520
740
  quote.digest
521
- ) : /* @__PURE__ */ jsx4("p", { role: "status", children: "Save your member selection to continue to payment." })
741
+ ) : /* @__PURE__ */ jsx8("p", { role: "status", children: "Save your member selection to continue to payment." })
522
742
  ] });
523
743
  }
524
744
 
525
745
  // src/ui/journey-frame.tsx
526
- import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
746
+ import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
527
747
  var styles = `
528
748
  .chapter-journey{color:var(--ui-text,#202827);font-family:var(--ui-font-sans,inherit);line-height:1.65;max-width:46rem;margin-inline:auto;min-width:0}
529
749
  .chapter-journey *{box-sizing:border-box}
@@ -531,7 +751,7 @@ var styles = `
531
751
  .chapter-journey :where(h2,h3){font-family:var(--ui-font-display,inherit);line-height:1.2;letter-spacing:-.025em;text-transform:none}
532
752
  .chapter-journey h2{font-size:clamp(1.6rem,3vw,2.15rem)}
533
753
  .chapter-journey h3{font-size:1.3rem}
534
- .chapter-journey :where(.card,.join-form,.payment-quote,.join-book){padding:clamp(1.25rem,4vw,2.4rem);border:1px solid var(--ui-border,#dfe3df);border-radius:var(--ui-radius-lg,16px);background:var(--ui-surface,#fff);margin-block:1.2rem}
754
+ .chapter-journey :where(.card,.join-form,.payment-quote,.join-book,.checkout-seat-review){padding:clamp(1.25rem,4vw,2.4rem);border:1px solid var(--ui-border,#dfe3df);border-radius:var(--ui-radius-lg,16px);background:var(--ui-surface,#fff);margin-block:1.2rem}
535
755
  .chapter-journey :where(.card-label,.journey-eyebrow){font-size:.75rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ui-text-muted,#536360);margin-bottom:.7rem;font-weight:600}
536
756
  .chapter-journey :where(form,.journey-fields){display:grid;gap:1.25rem}
537
757
  .chapter-journey :where(label){display:block;font-size:.95rem;font-weight:500;letter-spacing:normal;text-transform:none;line-height:1.55}
@@ -545,6 +765,14 @@ var styles = `
545
765
  .chapter-journey :is(.slot-time,.slot-day):hover{background:var(--ui-surface-2,#f3f5f2);color:var(--ui-text,#202827);border-color:var(--ui-accent,#285c50)}
546
766
  .chapter-journey :is(.slot-time,.slot-day)[aria-pressed=true]{background:var(--ui-accent,#285c50);color:var(--ui-on-accent,#fff)}
547
767
  .chapter-journey button:disabled{opacity:.55;cursor:default}
768
+ .chapter-journey .chapter-form-primary{background:var(--ui-accent,#285c50);color:var(--ui-on-accent,#fff);width:100%;margin-top:1rem}
769
+ .chapter-journey .chapter-form-primary:hover:not(:disabled){filter:brightness(.92)}
770
+ .chapter-journey .chapter-form-primary:disabled{background:var(--ui-surface-2,#e3e8df);color:var(--ui-text-muted,#687361);opacity:1;cursor:not-allowed;transform:none;filter:none}
771
+ .chapter-journey .additional-member-details{padding:1rem;margin-top:1rem;background:var(--ui-surface-2,#f3f5f2);border:1px solid var(--ui-border,#dfe3df);border-radius:8px}
772
+ .chapter-journey .additional-member-details label:not(:first-child){margin-top:.6rem}
773
+ .chapter-journey .additional-member-actions .chapter-form-primary{width:auto;flex:1;margin-top:0}
774
+ .chapter-journey .additional-member-actions .journey-secondary{min-width:7rem}
775
+ @media(max-width:520px){.chapter-journey .additional-member-actions{flex-direction:column}}
548
776
  .chapter-journey :where(.compliance-box,.journey-note){padding:1.2rem;background:var(--ui-surface-2,#f3f5f2);border-radius:10px;margin-block:1.2rem}
549
777
  .chapter-journey :where(.compliance-box p:last-child,.journey-note p:last-child,.card p:last-child){margin-bottom:0}
550
778
  .chapter-journey .journey-invitation{padding:1.1rem 1.3rem;border-left:3px solid var(--ui-accent,#285c50);background:var(--ui-surface-2,#f3f5f2);border-radius:0 10px 10px 0;margin-block:1rem}
@@ -555,24 +783,26 @@ var styles = `
555
783
  .chapter-journey :where(.pay-status,.meeting-note,.slots-status){color:var(--ui-text-muted,#536360)}
556
784
  .chapter-journey .journey-actions{display:flex;gap:.75rem;flex-wrap:wrap;margin-top:1.25rem}
557
785
  .chapter-journey fieldset{min-width:0;border:0;padding:0;margin:0}
786
+ .chapter-journey .checkout-seat-terms{margin-block:1rem}
787
+ .chapter-journey .checkout-seat-terms summary{cursor:pointer}
558
788
  @media(max-width:520px){.chapter-journey .journey-actions>*{width:100%}.chapter-journey .journey-summary{grid-template-columns:1fr}.chapter-journey .journey-summary dd{text-align:left}.chapter-journey :where(.card,.join-form,.payment-quote,.join-book){padding:1.25rem}}
559
789
  `;
560
790
  function JourneyFrame({ children }) {
561
- return /* @__PURE__ */ jsxs5("div", { className: "chapter-journey", children: [
562
- /* @__PURE__ */ jsx5("style", { children: styles }),
791
+ return /* @__PURE__ */ jsxs8("div", { className: "chapter-journey", children: [
792
+ /* @__PURE__ */ jsx9("style", { children: styles }),
563
793
  children
564
794
  ] });
565
795
  }
566
796
 
567
797
  // src/ui/member-restart.tsx
568
- import { jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
798
+ import { jsx as jsx10, jsxs as jsxs9 } from "preact/jsx-runtime";
569
799
  function MemberRestart(props) {
570
- const [id, setId] = useState5(props.applicationId);
571
- const [busy, setBusy] = useState5(false);
572
- const [error, setError] = useState5("");
573
- const [paid, setPaid] = useState5(false);
574
- const [checking, setChecking] = useState5(false);
575
- const [revision, setRevision] = useState5(0);
800
+ const [id, setId] = useState7(props.applicationId);
801
+ const [busy, setBusy] = useState7(false);
802
+ const [error, setError] = useState7("");
803
+ const [paid, setPaid] = useState7(false);
804
+ const [checking, setChecking] = useState7(false);
805
+ const [revision, setRevision] = useState7(0);
576
806
  useEffect2(() => {
577
807
  if (!paid) return;
578
808
  let live = true, timer, attempt = 0;
@@ -610,40 +840,40 @@ function MemberRestart(props) {
610
840
  setBusy(false);
611
841
  }
612
842
  };
613
- return /* @__PURE__ */ jsx6(JourneyFrame, { children: /* @__PURE__ */ jsxs6("section", { className: "card", "aria-label": "Restart membership", children: [
614
- /* @__PURE__ */ jsx6("p", { className: "card-label", children: "Welcome back" }),
615
- /* @__PURE__ */ jsx6("h2", { children: "Restart your membership" }),
616
- /* @__PURE__ */ jsx6("p", { children: "Your account is ready, and your previous approval still counts. No new application or interview is needed." }),
617
- /* @__PURE__ */ jsxs6("dl", { className: "journey-summary", children: [
618
- /* @__PURE__ */ jsx6("dt", { children: "Name" }),
619
- /* @__PURE__ */ jsx6("dd", { children: [props.application.firstName, props.application.lastName].filter(Boolean).join(" ") }),
620
- /* @__PURE__ */ jsx6("dt", { children: "Email" }),
621
- /* @__PURE__ */ jsx6("dd", { children: props.application.email })
843
+ return /* @__PURE__ */ jsx10(JourneyFrame, { children: /* @__PURE__ */ jsxs9("section", { className: "card", "aria-label": "Restart membership", children: [
844
+ /* @__PURE__ */ jsx10("p", { className: "card-label", children: "Welcome back" }),
845
+ /* @__PURE__ */ jsx10("h2", { children: "Restart your membership" }),
846
+ /* @__PURE__ */ jsx10("p", { children: "Your account is ready, and your previous approval still counts. No new application or interview is needed." }),
847
+ /* @__PURE__ */ jsxs9("dl", { className: "journey-summary", children: [
848
+ /* @__PURE__ */ jsx10("dt", { children: "Name" }),
849
+ /* @__PURE__ */ jsx10("dd", { children: [props.application.firstName, props.application.lastName].filter(Boolean).join(" ") }),
850
+ /* @__PURE__ */ jsx10("dt", { children: "Email" }),
851
+ /* @__PURE__ */ jsx10("dd", { children: props.application.email })
622
852
  ] }),
623
- paid ? /* @__PURE__ */ jsxs6("div", { role: "status", children: [
624
- /* @__PURE__ */ jsx6("h3", { children: "Confirming your membership" }),
625
- /* @__PURE__ */ jsx6("p", { children: "We\u2019re waiting for payment confirmation. Your membership will open here as soon as it arrives." }),
626
- !checking ? /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Check membership" }) : null
627
- ] }) : id ? /* @__PURE__ */ jsx6(MembershipPaymentStep, { applicationId: id, refundPolicyText: "", onPaid: () => setPaid(true) }) : /* @__PURE__ */ jsx6("button", { type: "button", disabled: busy, onClick: () => void start(), children: busy ? "Preparing your membership\u2026" : "Review membership and payment" }),
628
- error ? /* @__PURE__ */ jsx6("p", { role: "alert", children: error }) : null
853
+ paid ? /* @__PURE__ */ jsxs9("div", { role: "status", children: [
854
+ /* @__PURE__ */ jsx10("h3", { children: "Confirming your membership" }),
855
+ /* @__PURE__ */ jsx10("p", { children: "We\u2019re waiting for payment confirmation. Your membership will open here as soon as it arrives." }),
856
+ !checking ? /* @__PURE__ */ jsx10("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Check membership" }) : null
857
+ ] }) : id ? /* @__PURE__ */ jsx10(MembershipPaymentStep, { applicationId: id, refundPolicyText: "", onPaid: () => setPaid(true) }) : /* @__PURE__ */ jsx10("button", { type: "button", disabled: busy, onClick: () => void start(), children: busy ? "Preparing your membership\u2026" : "Review membership and payment" }),
858
+ error ? /* @__PURE__ */ jsx10("p", { role: "alert", children: error }) : null
629
859
  ] }) });
630
860
  }
631
861
 
632
862
  // src/ui/members.tsx
633
- import { useEffect as useEffect6, useState as useState10 } from "preact/hooks";
863
+ import { useEffect as useEffect6, useState as useState12 } from "preact/hooks";
634
864
 
635
865
  // src/ui/reschedule.tsx
636
- import { useState as useState6 } from "preact/hooks";
637
- import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
866
+ import { useState as useState8 } from "preact/hooks";
867
+ import { jsx as jsx11, jsxs as jsxs10 } from "preact/jsx-runtime";
638
868
  function Rescheduler(props) {
639
869
  const inheritedCopy = useChapterCopy();
640
870
  const copy = props.copy ?? inheritedCopy.members.reschedule;
641
871
  const { api, applicationId, timezone, onRescheduled, label = copy.changeTime } = props;
642
- const [open, setOpen] = useState6(false);
643
- const [slots, setSlots] = useState6(null);
644
- const [tz, setTz] = useState6(timezone);
645
- const [busy, setBusy] = useState6(false);
646
- const [msg, setMsg] = useState6(null);
872
+ const [open, setOpen] = useState8(false);
873
+ const [slots, setSlots] = useState8(null);
874
+ const [tz, setTz] = useState8(timezone);
875
+ const [busy, setBusy] = useState8(false);
876
+ const [msg, setMsg] = useState8(null);
647
877
  const start = async () => {
648
878
  setOpen(true);
649
879
  setSlots(null);
@@ -676,7 +906,7 @@ function Rescheduler(props) {
676
906
  }
677
907
  };
678
908
  if (!open) {
679
- return /* @__PURE__ */ jsx7("p", { className: "meeting-note", children: /* @__PURE__ */ jsx7(
909
+ return /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: /* @__PURE__ */ jsx11(
680
910
  "a",
681
911
  {
682
912
  href: "#",
@@ -688,8 +918,8 @@ function Rescheduler(props) {
688
918
  }
689
919
  ) });
690
920
  }
691
- return /* @__PURE__ */ jsxs7("div", { className: "msched", children: [
692
- slots === null ? /* @__PURE__ */ jsx7("p", { className: "meeting-note", children: copy.loading }) : slots.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "meeting-note", children: msg ?? copy.noTimes }) : /* @__PURE__ */ jsx7(
921
+ return /* @__PURE__ */ jsxs10("div", { className: "msched", children: [
922
+ slots === null ? /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: copy.loading }) : slots.length === 0 ? /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: msg ?? copy.noTimes }) : /* @__PURE__ */ jsx11(
693
923
  SlotPicker,
694
924
  {
695
925
  slots,
@@ -698,9 +928,9 @@ function Rescheduler(props) {
698
928
  onPick: (s) => void pick(s)
699
929
  }
700
930
  ),
701
- busy ? /* @__PURE__ */ jsx7("p", { className: "meeting-note", children: copy.rescheduling }) : null,
702
- msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx7("p", { className: "meeting-note error", children: msg }) : null,
703
- /* @__PURE__ */ jsx7("p", { className: "meeting-note", children: /* @__PURE__ */ jsx7(
931
+ busy ? /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: copy.rescheduling }) : null,
932
+ msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx11("p", { className: "meeting-note error", children: msg }) : null,
933
+ /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: /* @__PURE__ */ jsx11(
704
934
  "a",
705
935
  {
706
936
  href: "#",
@@ -715,32 +945,32 @@ function Rescheduler(props) {
715
945
  }
716
946
 
717
947
  // src/ui/member-gifts.tsx
718
- import { useEffect as useEffect3, useRef as useRef2, useState as useState7 } from "preact/hooks";
948
+ import { useEffect as useEffect3, useRef as useRef5, useState as useState9 } from "preact/hooks";
719
949
 
720
950
  // src/ui/gift-offer-cards.tsx
721
- import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
951
+ import { jsx as jsx12, jsxs as jsxs11 } from "preact/jsx-runtime";
722
952
  var price = (cents, currency) => currency === "usd" ? fmtMoney(cents) : new Intl.NumberFormat(void 0, { style: "currency", currency: currency.toUpperCase() }).format(cents / 100);
723
953
  function GiftOfferCards(props) {
724
954
  const mode = props.mode ?? "preview";
725
- return /* @__PURE__ */ jsx8("section", { className: "chapter-gift-offers", "aria-label": props.ariaLabel ?? "Gift options", children: props.offers.map((offer) => /* @__PURE__ */ jsxs8("article", { className: "card chapter-gift-offer", "data-offer-id": offer.id, children: [
726
- /* @__PURE__ */ jsx8("div", { className: "card-label", children: offer.title }),
727
- /* @__PURE__ */ jsx8("p", { children: offer.description }),
728
- /* @__PURE__ */ jsxs8("p", { className: "meeting-date", children: [
955
+ return /* @__PURE__ */ jsx12("section", { className: "chapter-gift-offers", "aria-label": props.ariaLabel ?? "Gift options", children: props.offers.map((offer) => /* @__PURE__ */ jsxs11("article", { className: "card chapter-gift-offer", "data-offer-id": offer.id, children: [
956
+ /* @__PURE__ */ jsx12("div", { className: "card-label", children: offer.title }),
957
+ /* @__PURE__ */ jsx12("p", { children: offer.description }),
958
+ /* @__PURE__ */ jsxs11("p", { className: "meeting-date", children: [
729
959
  price(offer.priceCents, offer.currency),
730
960
  offer.billingMode === "subscription" ? " recurring" : ""
731
961
  ] }),
732
- mode === "select" ? /* @__PURE__ */ jsx8("button", { type: "button", className: "btn secondary", "aria-pressed": props.selectedOfferId === offer.id, onClick: () => props.onSelect?.(offer.id), children: offer.ctaLabel }) : /* @__PURE__ */ jsx8("span", { className: "btn secondary", "aria-disabled": "true", children: offer.ctaLabel })
962
+ mode === "select" ? /* @__PURE__ */ jsx12("button", { type: "button", className: "btn secondary", "aria-pressed": props.selectedOfferId === offer.id, onClick: () => props.onSelect?.(offer.id), children: offer.ctaLabel }) : /* @__PURE__ */ jsx12("span", { className: "btn secondary", "aria-disabled": "true", children: offer.ctaLabel })
733
963
  ] }, offer.id)) });
734
964
  }
735
965
 
736
966
  // src/ui/member-gifts.tsx
737
- import { jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
967
+ import { jsx as jsx13, jsxs as jsxs12 } from "preact/jsx-runtime";
738
968
  function GiftPurchases(props) {
739
- const [data, setData] = useState7(null);
740
- const [selected, setSelected] = useState7(null);
741
- const [code, setCode] = useState7("");
742
- const [message, setMessage] = useState7("");
743
- const purchaseId = useRef2(crypto.randomUUID());
969
+ const [data, setData] = useState9(null);
970
+ const [selected, setSelected] = useState9(null);
971
+ const [code, setCode] = useState9("");
972
+ const [message, setMessage] = useState9("");
973
+ const purchaseId = useRef5(crypto.randomUUID());
744
974
  const load = async () => setData(await props.api("/api/gifts"));
745
975
  useEffect3(() => {
746
976
  void load().catch(() => setData({ offers: [], claims: [] }));
@@ -757,16 +987,16 @@ function GiftPurchases(props) {
757
987
  if (!data) return null;
758
988
  if (data.offers.length === 0 && data.claims.length === 0) return null;
759
989
  const offer = data.offers.find((item) => item.id === selected);
760
- return /* @__PURE__ */ jsxs9("section", { className: "member-gifts", children: [
761
- data.offers.length ? /* @__PURE__ */ jsxs9("div", { className: "card", children: [
762
- /* @__PURE__ */ jsx9("div", { className: "card-label", children: props.copy.title }),
763
- /* @__PURE__ */ jsx9("p", { children: props.copy.body }),
764
- /* @__PURE__ */ jsx9(GiftOfferCards, { offers: data.offers, mode: "select", selectedOfferId: selected, onSelect: (id) => {
990
+ return /* @__PURE__ */ jsxs12("section", { className: "member-gifts", children: [
991
+ data.offers.length ? /* @__PURE__ */ jsxs12("div", { className: "card", children: [
992
+ /* @__PURE__ */ jsx13("div", { className: "card-label", children: props.copy.title }),
993
+ /* @__PURE__ */ jsx13("p", { children: props.copy.body }),
994
+ /* @__PURE__ */ jsx13(GiftOfferCards, { offers: data.offers, mode: "select", selectedOfferId: selected, onSelect: (id) => {
765
995
  setSelected(id);
766
996
  setCode("");
767
997
  purchaseId.current = crypto.randomUUID();
768
998
  } }),
769
- offer ? /* @__PURE__ */ jsx9(
999
+ offer ? /* @__PURE__ */ jsx13(
770
1000
  PaymentStep,
771
1001
  {
772
1002
  applicationId: purchaseId.current,
@@ -786,27 +1016,27 @@ function GiftPurchases(props) {
786
1016
  }
787
1017
  }
788
1018
  ) : null,
789
- code ? /* @__PURE__ */ jsxs9("div", { className: "gift-code-once", children: [
790
- /* @__PURE__ */ jsx9("output", { children: code }),
791
- /* @__PURE__ */ jsx9("p", { children: props.copy.saveCode })
1019
+ code ? /* @__PURE__ */ jsxs12("div", { className: "gift-code-once", children: [
1020
+ /* @__PURE__ */ jsx13("output", { children: code }),
1021
+ /* @__PURE__ */ jsx13("p", { children: props.copy.saveCode })
792
1022
  ] }) : null
793
1023
  ] }) : null,
794
- /* @__PURE__ */ jsxs9("div", { className: "card", children: [
795
- /* @__PURE__ */ jsx9("div", { className: "card-label", children: props.copy.purchases }),
796
- data.claims.length ? data.claims.map((claim) => /* @__PURE__ */ jsxs9("div", { className: "member-row", children: [
797
- /* @__PURE__ */ jsx9("span", { children: claim.offerId }),
798
- /* @__PURE__ */ jsx9("span", { children: props.copy[claim.status] }),
799
- claim.status === "pending" ? /* @__PURE__ */ jsx9("button", { className: "btn secondary mini", onClick: () => void rotate(claim.id), children: props.copy.rotate }) : null
800
- ] }, claim.id)) : /* @__PURE__ */ jsx9("p", { children: props.copy.noPurchases }),
801
- message ? /* @__PURE__ */ jsx9("p", { role: "status", children: message }) : null
1024
+ /* @__PURE__ */ jsxs12("div", { className: "card", children: [
1025
+ /* @__PURE__ */ jsx13("div", { className: "card-label", children: props.copy.purchases }),
1026
+ data.claims.length ? data.claims.map((claim) => /* @__PURE__ */ jsxs12("div", { className: "member-row", children: [
1027
+ /* @__PURE__ */ jsx13("span", { children: claim.offerId }),
1028
+ /* @__PURE__ */ jsx13("span", { children: props.copy[claim.status] }),
1029
+ claim.status === "pending" ? /* @__PURE__ */ jsx13("button", { className: "btn secondary mini", onClick: () => void rotate(claim.id), children: props.copy.rotate }) : null
1030
+ ] }, claim.id)) : /* @__PURE__ */ jsx13("p", { children: props.copy.noPurchases }),
1031
+ message ? /* @__PURE__ */ jsx13("p", { role: "status", children: message }) : null
802
1032
  ] })
803
1033
  ] });
804
1034
  }
805
1035
  var MemberGifts = GiftPurchases;
806
1036
 
807
1037
  // src/ui/member-named-seat.tsx
808
- import { useEffect as useEffect4, useState as useState8 } from "preact/hooks";
809
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
1038
+ import { useEffect as useEffect4, useState as useState10 } from "preact/hooks";
1039
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs13 } from "preact/jsx-runtime";
810
1040
  var NAMED_SEAT_RECIPIENT_WORDS = {
811
1041
  payment_pending: "payment in progress",
812
1042
  invited: "invited, waiting for them to accept",
@@ -819,7 +1049,7 @@ var NAMED_SEAT_RECIPIENT_WORDS = {
819
1049
  var day = (at) => new Date(at).toLocaleDateString("en-US", { timeZone: "UTC" });
820
1050
  function NamedSeatRecipientLine({ seat }) {
821
1051
  const words = NAMED_SEAT_RECIPIENT_WORDS[seat.recipientState] ?? seat.status.replaceAll("_", " ");
822
- return /* @__PURE__ */ jsxs10("p", { className: "member-named-seat-recipient", children: [
1052
+ return /* @__PURE__ */ jsxs13("p", { className: "member-named-seat-recipient", children: [
823
1053
  seat.recipientName,
824
1054
  " \xB7 ",
825
1055
  seat.recipientEmail,
@@ -829,9 +1059,9 @@ function NamedSeatRecipientLine({ seat }) {
829
1059
  ] });
830
1060
  }
831
1061
  function NamedSeatPurchase({ api }) {
832
- const [data, setData] = useState8(null), [error, setError] = useState8("");
833
- const [name, setName] = useState8(""), [email, setEmail] = useState8(""), [reviewing, setReviewing] = useState8(false);
834
- const [terms, setTerms] = useState8(false), [pending, setPending] = useState8(false), [granting, setGranting] = useState8(false);
1062
+ const [data, setData] = useState10(null), [error, setError] = useState10("");
1063
+ const [name, setName] = useState10(""), [email, setEmail] = useState10(""), [reviewing, setReviewing] = useState10(false);
1064
+ const [terms, setTerms] = useState10(false), [pending, setPending] = useState10(false), [granting, setGranting] = useState10(false);
835
1065
  const load = async () => {
836
1066
  try {
837
1067
  const value = await api("/api/named-seat");
@@ -868,13 +1098,13 @@ function NamedSeatPurchase({ api }) {
868
1098
  }
869
1099
  };
870
1100
  const money = data ? new Intl.NumberFormat("en-US", { style: "currency", currency: data.currency }).format(data.amountCents / 100) : "";
871
- return /* @__PURE__ */ jsx10(JourneyFrame, { children: /* @__PURE__ */ jsxs10("section", { className: "card member-named-seat", "aria-label": "Named additional seat", children: [
872
- /* @__PURE__ */ jsx10("h2", { children: "Named additional seat" }),
873
- error ? /* @__PURE__ */ jsx10("p", { role: "alert", children: error }) : null,
874
- data ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
875
- data.seat ? /* @__PURE__ */ jsx10(NamedSeatRecipientLine, { seat: data.seat }) : null,
876
- data.eligible ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
877
- /* @__PURE__ */ jsxs10("p", { children: [
1101
+ return /* @__PURE__ */ jsx14(JourneyFrame, { children: /* @__PURE__ */ jsxs13("section", { className: "card member-named-seat", "aria-label": "Named additional seat", children: [
1102
+ /* @__PURE__ */ jsx14("h2", { children: "Named additional seat" }),
1103
+ error ? /* @__PURE__ */ jsx14("p", { role: "alert", children: error }) : null,
1104
+ data ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
1105
+ data.seat ? /* @__PURE__ */ jsx14(NamedSeatRecipientLine, { seat: data.seat }) : null,
1106
+ data.eligible ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
1107
+ /* @__PURE__ */ jsxs13("p", { children: [
878
1108
  data.included ? "Included with your membership at no charge." : `${money} paid in full now.`,
879
1109
  " This seat ",
880
1110
  data.autoRenew ? "renews with your primary membership" : "ends with your primary membership",
@@ -885,39 +1115,39 @@ function NamedSeatPurchase({ api }) {
885
1115
  " ",
886
1116
  "It belongs to one named person and cannot be reassigned."
887
1117
  ] }),
888
- /* @__PURE__ */ jsx10("p", { children: "The recipient must accept and complete their own application and any required interview. The seat stays open for them while your membership is paid; it is not refunded automatically. Contact the chapter if you need a refund." }),
889
- !reviewing ? /* @__PURE__ */ jsxs10("form", { onSubmit: (event) => {
1118
+ /* @__PURE__ */ jsx14("p", { children: "The recipient must accept and complete their own application and any required interview. The seat stays open for them while your membership is paid; it is not refunded automatically. Contact the chapter if you need a refund." }),
1119
+ !reviewing ? /* @__PURE__ */ jsxs13("form", { onSubmit: (event) => {
890
1120
  event.preventDefault();
891
1121
  setReviewing(true);
892
1122
  setTerms(false);
893
1123
  }, children: [
894
- /* @__PURE__ */ jsxs10("label", { children: [
1124
+ /* @__PURE__ */ jsxs13("label", { children: [
895
1125
  "Recipient name",
896
- /* @__PURE__ */ jsx10("input", { required: true, maxLength: 160, value: name, disabled: Boolean(data.seat), onInput: (event) => setName(event.currentTarget.value) })
1126
+ /* @__PURE__ */ jsx14("input", { required: true, maxLength: 160, value: name, disabled: Boolean(data.seat), onInput: (event) => setName(event.currentTarget.value) })
897
1127
  ] }),
898
- /* @__PURE__ */ jsxs10("label", { children: [
1128
+ /* @__PURE__ */ jsxs13("label", { children: [
899
1129
  "Recipient email",
900
- /* @__PURE__ */ jsx10("input", { required: true, type: "email", maxLength: 254, value: email, disabled: Boolean(data.seat), onInput: (event) => setEmail(event.currentTarget.value) })
1130
+ /* @__PURE__ */ jsx14("input", { required: true, type: "email", maxLength: 254, value: email, disabled: Boolean(data.seat), onInput: (event) => setEmail(event.currentTarget.value) })
901
1131
  ] }),
902
- /* @__PURE__ */ jsx10("button", { className: "btn primary", type: "submit", children: "Review named seat" })
903
- ] }) : /* @__PURE__ */ jsxs10(Fragment4, { children: [
904
- /* @__PURE__ */ jsxs10("p", { children: [
1132
+ /* @__PURE__ */ jsx14("button", { className: "btn primary", type: "submit", children: "Review named seat" })
1133
+ ] }) : /* @__PURE__ */ jsxs13(Fragment3, { children: [
1134
+ /* @__PURE__ */ jsxs13("p", { children: [
905
1135
  "For ",
906
1136
  name,
907
1137
  " \xB7 ",
908
1138
  email
909
1139
  ] }),
910
- /* @__PURE__ */ jsx10("button", { className: "btn secondary", type: "button", onClick: () => {
1140
+ /* @__PURE__ */ jsx14("button", { className: "btn secondary", type: "button", onClick: () => {
911
1141
  setReviewing(false);
912
1142
  setTerms(false);
913
1143
  void load();
914
1144
  }, children: "Back to recipient" }),
915
- /* @__PURE__ */ jsxs10("label", { className: "journey-consent", children: [
916
- /* @__PURE__ */ jsx10("input", { type: "checkbox", checked: terms, onChange: (event) => setTerms(event.currentTarget.checked) }),
917
- /* @__PURE__ */ jsx10("span", { children: data.included ? "I agree to the seat terms and the refund policy; the seat is included with my membership and follows its renewal." : data.autoRenew ? "I agree to the full initial payment and automatic renewal with my membership; I can cancel renewal." : "I agree to the full initial payment and the displayed end date, with no automatic renewal while my membership renewal is canceled." })
1145
+ /* @__PURE__ */ jsxs13("label", { className: "journey-consent", children: [
1146
+ /* @__PURE__ */ jsx14("input", { type: "checkbox", checked: terms, onChange: (event) => setTerms(event.currentTarget.checked) }),
1147
+ /* @__PURE__ */ jsx14("span", { children: data.included ? "I agree to the seat terms and the refund policy; the seat is included with my membership and follows its renewal." : data.autoRenew ? "I agree to the full initial payment and automatic renewal with my membership; I can cancel renewal." : "I agree to the full initial payment and the displayed end date, with no automatic renewal while my membership renewal is canceled." })
918
1148
  ] }),
919
- terms && data.included ? /* @__PURE__ */ jsx10("button", { className: "btn primary", type: "button", disabled: granting, onClick: () => void grant(), children: granting ? "Sending the invitation\u2026" : "Send the invitation" }) : null,
920
- terms && !data.included ? /* @__PURE__ */ jsx10(
1149
+ terms && data.included ? /* @__PURE__ */ jsx14("button", { className: "btn primary", type: "button", disabled: granting, onClick: () => void grant(), children: granting ? "Sending the invitation\u2026" : "Send the invitation" }) : null,
1150
+ terms && !data.included ? /* @__PURE__ */ jsx14(
921
1151
  PaymentStep,
922
1152
  {
923
1153
  applicationId: data.membershipId,
@@ -932,17 +1162,17 @@ function NamedSeatPurchase({ api }) {
932
1162
  }
933
1163
  ) : null
934
1164
  ] })
935
- ] }) : !data.seat ? /* @__PURE__ */ jsx10("p", { children: data.purchaseWindowed === false ? "An additional seat can be purchased for as long as your membership stays paid." : `Additional seats can be purchased until ${day(data.purchaseEndsAt)}.` }) : null,
936
- pending ? /* @__PURE__ */ jsx10("p", { role: "status", children: "Payment confirmation is being reconciled. The recipient\u2019s access begins only after their own acceptance and approval." }) : null
937
- ] }) : !error ? /* @__PURE__ */ jsx10("p", { children: "Loading your seat terms\u2026" }) : null,
938
- /* @__PURE__ */ jsx10("button", { className: "btn secondary", onClick: () => void load(), children: "Refresh seat status" })
1165
+ ] }) : !data.seat ? /* @__PURE__ */ jsx14("p", { children: data.purchaseWindowed === false ? "An additional seat can be purchased for as long as your membership stays paid." : `Additional seats can be purchased until ${day(data.purchaseEndsAt)}.` }) : null,
1166
+ pending ? /* @__PURE__ */ jsx14("p", { role: "status", children: "Payment confirmation is being reconciled. The recipient\u2019s access begins only after their own acceptance and approval." }) : null
1167
+ ] }) : !error ? /* @__PURE__ */ jsx14("p", { children: "Loading your seat terms\u2026" }) : null,
1168
+ /* @__PURE__ */ jsx14("button", { className: "btn secondary", onClick: () => void load(), children: "Refresh seat status" })
939
1169
  ] }) });
940
1170
  }
941
1171
 
942
1172
  // src/ui/member-content.tsx
943
- import { useEffect as useEffect5, useMemo as useMemo2, useState as useState9 } from "preact/hooks";
1173
+ import { useEffect as useEffect5, useMemo as useMemo2, useState as useState11 } from "preact/hooks";
944
1174
  import { Markdown } from "@odla-ai/ui/components";
945
- import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
1175
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs14 } from "preact/jsx-runtime";
946
1176
  var pageLabel = (page) => `${page.title}${page.access === "locked" ? " \xB7 Locked" : ""}`;
947
1177
  function MemberContentArea(props) {
948
1178
  const {
@@ -950,12 +1180,12 @@ function MemberContentArea(props) {
950
1180
  path = "/api/member-content",
951
1181
  upsellHref,
952
1182
  onUpsell,
953
- loadingContent = /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: "Loading member pages\u2026" }),
954
- emptyContent = /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: "No member pages have been published yet." })
1183
+ loadingContent = /* @__PURE__ */ jsx15("p", { className: "meeting-note", children: "Loading member pages\u2026" }),
1184
+ emptyContent = /* @__PURE__ */ jsx15("p", { className: "meeting-note", children: "No member pages have been published yet." })
955
1185
  } = props;
956
- const [data, setData] = useState9(props.initialData ?? null);
957
- const [error, setError] = useState9(null);
958
- const [selectedId, setSelectedId] = useState9(
1186
+ const [data, setData] = useState11(props.initialData ?? null);
1187
+ const [error, setError] = useState11(null);
1188
+ const [selectedId, setSelectedId] = useState11(
959
1189
  props.initialPageId ?? props.initialData?.pages[0]?.id ?? ""
960
1190
  );
961
1191
  useEffect5(() => {
@@ -980,18 +1210,18 @@ function MemberContentArea(props) {
980
1210
  }
981
1211
  return [...result.entries()];
982
1212
  }, [data]);
983
- if (error) return /* @__PURE__ */ jsxs11("p", { className: "meeting-note", role: "alert", children: [
1213
+ if (error) return /* @__PURE__ */ jsxs14("p", { className: "meeting-note", role: "alert", children: [
984
1214
  "Member pages could not be loaded: ",
985
1215
  error
986
1216
  ] });
987
- if (!data) return /* @__PURE__ */ jsx11(Fragment5, { children: loadingContent });
988
- if (data.pages.length === 0) return /* @__PURE__ */ jsx11(Fragment5, { children: emptyContent });
1217
+ if (!data) return /* @__PURE__ */ jsx15(Fragment4, { children: loadingContent });
1218
+ if (data.pages.length === 0) return /* @__PURE__ */ jsx15(Fragment4, { children: emptyContent });
989
1219
  const selected = data.pages.find((page) => page.id === selectedId) ?? data.pages[0];
990
1220
  const href = selected.upsell && upsellHref ? upsellHref(selected.upsell.tierId, selected) : void 0;
991
- return /* @__PURE__ */ jsxs11("div", { className: "chapter-member-content", "data-member-content": true, children: [
992
- /* @__PURE__ */ jsx11("nav", { className: "chapter-member-content-nav", "aria-label": "Member pages", children: sections.map(([section, pages]) => /* @__PURE__ */ jsxs11("section", { className: "chapter-member-content-section", children: [
993
- /* @__PURE__ */ jsx11("h2", { children: section }),
994
- /* @__PURE__ */ jsx11("ul", { children: pages.map((page) => /* @__PURE__ */ jsx11("li", { "data-parent-page": page.navigation.parentId ?? void 0, children: /* @__PURE__ */ jsx11(
1221
+ return /* @__PURE__ */ jsxs14("div", { className: "chapter-member-content", "data-member-content": true, children: [
1222
+ /* @__PURE__ */ jsx15("nav", { className: "chapter-member-content-nav", "aria-label": "Member pages", children: sections.map(([section, pages]) => /* @__PURE__ */ jsxs14("section", { className: "chapter-member-content-section", children: [
1223
+ /* @__PURE__ */ jsx15("h2", { children: section }),
1224
+ /* @__PURE__ */ jsx15("ul", { children: pages.map((page) => /* @__PURE__ */ jsx15("li", { "data-parent-page": page.navigation.parentId ?? void 0, children: /* @__PURE__ */ jsx15(
995
1225
  "button",
996
1226
  {
997
1227
  type: "button",
@@ -1001,23 +1231,23 @@ function MemberContentArea(props) {
1001
1231
  }
1002
1232
  ) }, page.id)) })
1003
1233
  ] }, section)) }),
1004
- /* @__PURE__ */ jsxs11("article", { className: "chapter-member-content-page", "data-access": selected.access, children: [
1005
- /* @__PURE__ */ jsxs11("header", { children: [
1006
- /* @__PURE__ */ jsxs11("div", { className: "card-label", children: [
1234
+ /* @__PURE__ */ jsxs14("article", { className: "chapter-member-content-page", "data-access": selected.access, children: [
1235
+ /* @__PURE__ */ jsxs14("header", { children: [
1236
+ /* @__PURE__ */ jsxs14("div", { className: "card-label", children: [
1007
1237
  selected.source.type === "parent" ? "Parent" : "Chapter",
1008
1238
  " \xB7 ",
1009
1239
  selected.access
1010
1240
  ] }),
1011
- /* @__PURE__ */ jsx11("h1", { children: selected.title }),
1012
- selected.summary ? /* @__PURE__ */ jsx11("p", { children: selected.summary }) : null
1241
+ /* @__PURE__ */ jsx15("h1", { children: selected.title }),
1242
+ selected.summary ? /* @__PURE__ */ jsx15("p", { children: selected.summary }) : null
1013
1243
  ] }),
1014
- selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx11(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs11("div", { className: "chapter-member-content-locked", children: [
1015
- selected.teaserMarkdown ? /* @__PURE__ */ jsx11(Markdown, { source: selected.teaserMarkdown }) : /* @__PURE__ */ jsx11("p", { children: "This page is not included in your current membership." }),
1016
- selected.upsell ? /* @__PURE__ */ jsxs11("aside", { className: "chapter-member-content-upsell", "aria-label": selected.upsell.label ?? "Membership option", children: [
1017
- /* @__PURE__ */ jsx11("h2", { children: selected.upsell.label ?? selected.upsell.tierName }),
1018
- /* @__PURE__ */ jsx11("p", { children: fmtMoney(selected.upsell.priceCents) }),
1019
- selected.upsell.bodyMarkdown ? /* @__PURE__ */ jsx11(Markdown, { source: selected.upsell.bodyMarkdown }) : null,
1020
- href ? /* @__PURE__ */ jsx11("a", { className: "btn primary", href, children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : onUpsell ? /* @__PURE__ */ jsx11("button", { className: "btn primary", type: "button", onClick: () => onUpsell(selected.upsell.tierId, selected), children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : null
1244
+ selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx15(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs14("div", { className: "chapter-member-content-locked", children: [
1245
+ selected.teaserMarkdown ? /* @__PURE__ */ jsx15(Markdown, { source: selected.teaserMarkdown }) : /* @__PURE__ */ jsx15("p", { children: "This page is not included in your current membership." }),
1246
+ selected.upsell ? /* @__PURE__ */ jsxs14("aside", { className: "chapter-member-content-upsell", "aria-label": selected.upsell.label ?? "Membership option", children: [
1247
+ /* @__PURE__ */ jsx15("h2", { children: selected.upsell.label ?? selected.upsell.tierName }),
1248
+ /* @__PURE__ */ jsx15("p", { children: fmtMoney(selected.upsell.priceCents) }),
1249
+ selected.upsell.bodyMarkdown ? /* @__PURE__ */ jsx15(Markdown, { source: selected.upsell.bodyMarkdown }) : null,
1250
+ href ? /* @__PURE__ */ jsx15("a", { className: "btn primary", href, children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : onUpsell ? /* @__PURE__ */ jsx15("button", { className: "btn primary", type: "button", onClick: () => onUpsell(selected.upsell.tierId, selected), children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : null
1021
1251
  ] }) : null
1022
1252
  ] })
1023
1253
  ] })
@@ -1025,12 +1255,12 @@ function MemberContentArea(props) {
1025
1255
  }
1026
1256
 
1027
1257
  // src/ui/members.tsx
1028
- import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
1258
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs15 } from "preact/jsx-runtime";
1029
1259
  function card(copy, kicker, body) {
1030
- return /* @__PURE__ */ jsxs12("div", { className: "card", children: [
1031
- /* @__PURE__ */ jsx12("div", { className: "card-label", children: copy.provisional.cardLabel }),
1032
- /* @__PURE__ */ jsxs12("div", { className: "meeting-block", children: [
1033
- /* @__PURE__ */ jsx12("div", { className: "meeting-kicker", children: kicker }),
1260
+ return /* @__PURE__ */ jsxs15("div", { className: "card", children: [
1261
+ /* @__PURE__ */ jsx16("div", { className: "card-label", children: copy.provisional.cardLabel }),
1262
+ /* @__PURE__ */ jsxs15("div", { className: "meeting-block", children: [
1263
+ /* @__PURE__ */ jsx16("div", { className: "meeting-kicker", children: kicker }),
1034
1264
  body
1035
1265
  ] })
1036
1266
  ] });
@@ -1041,9 +1271,9 @@ function ProvisionalCard(props) {
1041
1271
  return card(
1042
1272
  copy,
1043
1273
  copy.provisional.applicationNeeded,
1044
- /* @__PURE__ */ jsxs12(Fragment6, { children: [
1045
- /* @__PURE__ */ jsx12("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
1046
- /* @__PURE__ */ jsx12("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
1274
+ /* @__PURE__ */ jsxs15(Fragment5, { children: [
1275
+ /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
1276
+ /* @__PURE__ */ jsx16("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
1047
1277
  ] })
1048
1278
  );
1049
1279
  }
@@ -1051,19 +1281,19 @@ function ProvisionalCard(props) {
1051
1281
  return card(
1052
1282
  copy,
1053
1283
  copy.provisional.refunded,
1054
- /* @__PURE__ */ jsx12("div", { className: "meeting-note", children: copy.provisional.refundedBody })
1284
+ /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: copy.provisional.refundedBody })
1055
1285
  );
1056
1286
  }
1057
- const membership = application.paid ? /* @__PURE__ */ jsx12("div", { className: "meeting-note", children: application.renewalAt ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) }) : copy.provisional.active }) : null;
1287
+ const membership = application.paid ? /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: application.renewalAt ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) }) : copy.provisional.active }) : null;
1058
1288
  if (application.meetingAt) {
1059
1289
  return card(
1060
1290
  copy,
1061
1291
  copy.provisional.introductionCall,
1062
- /* @__PURE__ */ jsxs12(Fragment6, { children: [
1063
- /* @__PURE__ */ jsx12("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
1064
- /* @__PURE__ */ jsx12("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
1065
- application.meetUrl ? /* @__PURE__ */ jsx12("div", { className: "meeting-note", children: /* @__PURE__ */ jsx12("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
1066
- /* @__PURE__ */ jsx12(
1292
+ /* @__PURE__ */ jsxs15(Fragment5, { children: [
1293
+ /* @__PURE__ */ jsx16("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
1294
+ /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
1295
+ application.meetUrl ? /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: /* @__PURE__ */ jsx16("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
1296
+ /* @__PURE__ */ jsx16(
1067
1297
  Rescheduler,
1068
1298
  {
1069
1299
  api,
@@ -1080,9 +1310,9 @@ function ProvisionalCard(props) {
1080
1310
  return card(
1081
1311
  copy,
1082
1312
  copy.provisional.bookCall,
1083
- /* @__PURE__ */ jsxs12(Fragment6, { children: [
1084
- /* @__PURE__ */ jsx12("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
1085
- /* @__PURE__ */ jsx12(
1313
+ /* @__PURE__ */ jsxs15(Fragment5, { children: [
1314
+ /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
1315
+ /* @__PURE__ */ jsx16(
1086
1316
  Rescheduler,
1087
1317
  {
1088
1318
  api,
@@ -1109,8 +1339,8 @@ function MembersArea(props) {
1109
1339
  renderProvisional
1110
1340
  } = props;
1111
1341
  const copy = props.copy ?? inheritedCopy.members;
1112
- const [me, setMe] = useState10(null);
1113
- const [error, setError] = useState10(false);
1342
+ const [me, setMe] = useState12(null);
1343
+ const [error, setError] = useState12(false);
1114
1344
  const reload = async () => {
1115
1345
  try {
1116
1346
  setMe(await api("/api/me"));
@@ -1121,22 +1351,22 @@ function MembersArea(props) {
1121
1351
  useEffect6(() => {
1122
1352
  void reload();
1123
1353
  }, []);
1124
- if (error) return /* @__PURE__ */ jsx12("p", { className: "meeting-note", children: copy.loadFailed });
1125
- if (!me) return /* @__PURE__ */ jsx12("p", { className: "meeting-note", children: inheritedCopy.common.loading });
1354
+ if (error) return /* @__PURE__ */ jsx16("p", { className: "meeting-note", children: copy.loadFailed });
1355
+ if (!me) return /* @__PURE__ */ jsx16("p", { className: "meeting-note", children: inheritedCopy.common.loading });
1126
1356
  const role = me.role || "provisional";
1127
- return /* @__PURE__ */ jsxs12(Fragment6, { children: [
1128
- /* @__PURE__ */ jsxs12("div", { className: "card", children: [
1129
- /* @__PURE__ */ jsxs12("div", { className: "member-row", children: [
1130
- /* @__PURE__ */ jsx12("span", { className: "member-email", children: me.email }),
1131
- /* @__PURE__ */ jsx12("span", { className: "role-badge " + role, children: role })
1357
+ return /* @__PURE__ */ jsxs15(Fragment5, { children: [
1358
+ /* @__PURE__ */ jsxs15("div", { className: "card", children: [
1359
+ /* @__PURE__ */ jsxs15("div", { className: "member-row", children: [
1360
+ /* @__PURE__ */ jsx16("span", { className: "member-email", children: me.email }),
1361
+ /* @__PURE__ */ jsx16("span", { className: "role-badge " + role, children: role })
1132
1362
  ] }),
1133
- /* @__PURE__ */ jsxs12("div", { className: "account-actions", children: [
1134
- /* @__PURE__ */ jsx12("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
1135
- me.authorized || me.superAdmin ? /* @__PURE__ */ jsx12("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
1363
+ /* @__PURE__ */ jsxs15("div", { className: "account-actions", children: [
1364
+ /* @__PURE__ */ jsx16("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
1365
+ me.authorized || me.superAdmin ? /* @__PURE__ */ jsx16("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
1136
1366
  ] })
1137
1367
  ] }),
1138
- me.memberAccess !== true && me.membershipRestart?.eligible && me.application ? /* @__PURE__ */ jsx12(MemberRestart, { api, application: me.application, applicationId: me.membershipRestart.applicationId, onComplete: reload }) : me.memberAccess !== true ? (() => {
1139
- const defaultContent = /* @__PURE__ */ jsx12(
1368
+ me.memberAccess !== true && me.membershipRestart?.eligible && me.application ? /* @__PURE__ */ jsx16(MemberRestart, { api, application: me.application, applicationId: me.membershipRestart.applicationId, onComplete: reload }) : me.memberAccess !== true ? (() => {
1369
+ const defaultContent = /* @__PURE__ */ jsx16(
1140
1370
  ProvisionalCard,
1141
1371
  {
1142
1372
  api,
@@ -1153,30 +1383,30 @@ function MembersArea(props) {
1153
1383
  reload,
1154
1384
  defaultContent
1155
1385
  }) ?? defaultContent;
1156
- })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx12(
1386
+ })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx16(
1157
1387
  MemberContentArea,
1158
1388
  {
1159
1389
  api,
1160
1390
  ...memberPages === true ? {} : memberPages
1161
1391
  }
1162
- ) : /* @__PURE__ */ jsx12("div", { className: "card", children: /* @__PURE__ */ jsx12("div", { className: "card-label", children: copy.full.welcome }) })),
1163
- /* @__PURE__ */ jsx12(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
1164
- me.namedSeatsEnabled ? /* @__PURE__ */ jsx12(NamedSeatPurchase, { api }) : null
1392
+ ) : /* @__PURE__ */ jsx16("div", { className: "card", children: /* @__PURE__ */ jsx16("div", { className: "card-label", children: copy.full.welcome }) })),
1393
+ /* @__PURE__ */ jsx16(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
1394
+ me.namedSeatsEnabled ? /* @__PURE__ */ jsx16(NamedSeatPurchase, { api }) : null
1165
1395
  ] });
1166
1396
  }
1167
1397
 
1168
1398
  // src/ui/join.tsx
1169
- import { useEffect as useEffect11, useRef as useRef3, useState as useState18 } from "preact/hooks";
1399
+ import { useEffect as useEffect11, useRef as useRef7, useState as useState20 } from "preact/hooks";
1170
1400
 
1171
1401
  // src/ui/join-booking.tsx
1172
- import { useEffect as useEffect7, useState as useState11 } from "preact/hooks";
1173
- import { jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
1402
+ import { useEffect as useEffect7, useState as useState13 } from "preact/hooks";
1403
+ import { jsx as jsx17, jsxs as jsxs16 } from "preact/jsx-runtime";
1174
1404
  function JoinBooking(props) {
1175
1405
  const { applicationId, copy, onBooked, simulation } = props;
1176
- const [state, setState] = useState11(null);
1177
- const [msg, setMsg] = useState11(null);
1178
- const [busy, setBusy] = useState11(false);
1179
- const [selected, setSelected] = useState11(null);
1406
+ const [state, setState] = useState13(null);
1407
+ const [msg, setMsg] = useState13(null);
1408
+ const [busy, setBusy] = useState13(false);
1409
+ const [selected, setSelected] = useState13(null);
1180
1410
  const load = async () => {
1181
1411
  setMsg(null);
1182
1412
  try {
@@ -1233,11 +1463,11 @@ function JoinBooking(props) {
1233
1463
  setBusy(false);
1234
1464
  }
1235
1465
  };
1236
- if (!state) return /* @__PURE__ */ jsx13("p", { className: "slots-status", children: msg ?? copy.loading });
1237
- return /* @__PURE__ */ jsxs13("div", { className: "join-book", children: [
1238
- /* @__PURE__ */ jsx13("h2", { children: copy.title ?? DEFAULT_JOIN_COPY.booking.title }),
1239
- /* @__PURE__ */ jsx13("p", { children: copy.intro ?? DEFAULT_JOIN_COPY.booking.intro }),
1240
- /* @__PURE__ */ jsx13(
1466
+ if (!state) return /* @__PURE__ */ jsx17("p", { className: "slots-status", children: msg ?? copy.loading });
1467
+ return /* @__PURE__ */ jsxs16("div", { className: "join-book", children: [
1468
+ /* @__PURE__ */ jsx17("h2", { children: copy.title ?? DEFAULT_JOIN_COPY.booking.title }),
1469
+ /* @__PURE__ */ jsx17("p", { children: copy.intro ?? DEFAULT_JOIN_COPY.booking.intro }),
1470
+ /* @__PURE__ */ jsx17(
1241
1471
  SlotPicker,
1242
1472
  {
1243
1473
  slots: state.slots,
@@ -1247,29 +1477,29 @@ function JoinBooking(props) {
1247
1477
  onDayChange: () => setSelected(null)
1248
1478
  }
1249
1479
  ),
1250
- /* @__PURE__ */ jsxs13("div", { className: "slot-confirm", hidden: !selected, children: [
1251
- /* @__PURE__ */ jsx13("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
1252
- msg ? /* @__PURE__ */ jsx13("p", { className: "step2-note", children: msg }) : null
1480
+ /* @__PURE__ */ jsxs16("div", { className: "slot-confirm", hidden: !selected, children: [
1481
+ /* @__PURE__ */ jsx17("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
1482
+ msg ? /* @__PURE__ */ jsx17("p", { className: "step2-note", children: msg }) : null
1253
1483
  ] })
1254
1484
  ] });
1255
1485
  }
1256
1486
 
1257
1487
  // src/ui/join-accepted.tsx
1258
- import { jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
1488
+ import { jsx as jsx18, jsxs as jsxs17 } from "preact/jsx-runtime";
1259
1489
  function JoinAccepted(props) {
1260
- return /* @__PURE__ */ jsxs14("div", { className: "join-done card", children: [
1261
- /* @__PURE__ */ jsx14("div", { className: "card-label", children: props.copy.accepted.label }),
1262
- /* @__PURE__ */ jsx14("p", { className: "meeting-note", children: props.copy.accepted.body }),
1263
- /* @__PURE__ */ jsx14("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
1490
+ return /* @__PURE__ */ jsxs17("div", { className: "join-done card", children: [
1491
+ /* @__PURE__ */ jsx18("div", { className: "card-label", children: props.copy.accepted.label }),
1492
+ /* @__PURE__ */ jsx18("p", { className: "meeting-note", children: props.copy.accepted.body }),
1493
+ /* @__PURE__ */ jsx18("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
1264
1494
  ] });
1265
1495
  }
1266
1496
 
1267
1497
  // src/ui/gift-claim-card.tsx
1268
- import { useState as useState12 } from "preact/hooks";
1269
- import { Fragment as Fragment7, jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
1498
+ import { useState as useState14 } from "preact/hooks";
1499
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs18 } from "preact/jsx-runtime";
1270
1500
  function GiftClaimCard(props) {
1271
- const [code, setCode] = useState12(props.initialCode ?? "");
1272
- const [error, setError] = useState12(false);
1501
+ const [code, setCode] = useState14(props.initialCode ?? "");
1502
+ const [error, setError] = useState14(false);
1273
1503
  const rotate = async () => {
1274
1504
  if (!props.claimId) return;
1275
1505
  setError(false);
@@ -1282,24 +1512,27 @@ function GiftClaimCard(props) {
1282
1512
  if (response.ok && body.code) setCode(body.code);
1283
1513
  else setError(true);
1284
1514
  };
1285
- return /* @__PURE__ */ jsxs15("div", { className: "join-done card", children: [
1286
- /* @__PURE__ */ jsx15("div", { className: "card-label", children: props.copy.label }),
1287
- /* @__PURE__ */ jsx15("p", { className: "meeting-note", children: props.copy.body }),
1288
- code ? /* @__PURE__ */ jsxs15(Fragment7, { children: [
1289
- /* @__PURE__ */ jsx15("output", { className: "gift-code", children: code }),
1290
- /* @__PURE__ */ jsx15("p", { children: props.copy.save })
1515
+ return /* @__PURE__ */ jsxs18("div", { className: "join-done card", children: [
1516
+ /* @__PURE__ */ jsx19("div", { className: "card-label", children: props.copy.label }),
1517
+ /* @__PURE__ */ jsx19("p", { className: "meeting-note", children: props.copy.body }),
1518
+ code ? /* @__PURE__ */ jsxs18(Fragment6, { children: [
1519
+ /* @__PURE__ */ jsx19("output", { className: "gift-code", children: code }),
1520
+ /* @__PURE__ */ jsx19("p", { children: props.copy.save })
1291
1521
  ] }) : null,
1292
- !code && props.claimId ? /* @__PURE__ */ jsx15("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
1293
- error ? /* @__PURE__ */ jsx15("p", { role: "alert", children: props.copy.rotateFailed }) : null
1522
+ !code && props.claimId ? /* @__PURE__ */ jsx19("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
1523
+ error ? /* @__PURE__ */ jsx19("p", { role: "alert", children: props.copy.rotateFailed }) : null
1294
1524
  ] });
1295
1525
  }
1296
1526
 
1527
+ // src/ui/join-application-form.tsx
1528
+ import { useRef as useRef6 } from "preact/hooks";
1529
+
1297
1530
  // src/ui/join-tiers.tsx
1298
- import { useState as useState13 } from "preact/hooks";
1299
- import { Fragment as Fragment8, jsx as jsx16, jsxs as jsxs16 } from "preact/jsx-runtime";
1531
+ import { useState as useState15 } from "preact/hooks";
1532
+ import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs19 } from "preact/jsx-runtime";
1300
1533
  var joinTierMoney = (cents, currency = "usd") => new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
1301
1534
  function useTierSelection(tiers, initialTierId) {
1302
- const [selectedTierId, setSelectedTierId] = useState13(() => {
1535
+ const [selectedTierId, setSelectedTierId] = useState15(() => {
1303
1536
  const preset = tiers.find((tier) => tier.id === initialTierId);
1304
1537
  if (preset) return preset.id;
1305
1538
  return tiers.length === 1 ? tiers[0].id : null;
@@ -1314,10 +1547,10 @@ function useTierSelection(tiers, initialTierId) {
1314
1547
  };
1315
1548
  }
1316
1549
  function TierPlaceholder(context) {
1317
- return /* @__PURE__ */ jsxs16("fieldset", { "data-chapter-placeholder": "tiers", children: [
1318
- /* @__PURE__ */ jsx16("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
1319
- context.tiers.map((tier) => /* @__PURE__ */ jsxs16("label", { children: [
1320
- /* @__PURE__ */ jsx16(
1550
+ return /* @__PURE__ */ jsxs19("fieldset", { "data-chapter-placeholder": "tiers", children: [
1551
+ /* @__PURE__ */ jsx20("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
1552
+ context.tiers.map((tier) => /* @__PURE__ */ jsxs19("label", { children: [
1553
+ /* @__PURE__ */ jsx20(
1321
1554
  "input",
1322
1555
  {
1323
1556
  type: "radio",
@@ -1328,13 +1561,13 @@ function TierPlaceholder(context) {
1328
1561
  }
1329
1562
  ),
1330
1563
  tier.name,
1331
- tier.founding ? /* @__PURE__ */ jsxs16(Fragment8, { children: [
1564
+ tier.founding ? /* @__PURE__ */ jsxs19(Fragment7, { children: [
1332
1565
  " \u2014 ",
1333
1566
  joinTierMoney(tier.founding.dueTodayCents),
1334
1567
  " founding member rate (list ",
1335
1568
  joinTierMoney(tier.priceCents),
1336
1569
  ")"
1337
- ] }) : tier.free ? " \u2014 free" : /* @__PURE__ */ jsxs16(Fragment8, { children: [
1570
+ ] }) : tier.free ? " \u2014 free" : /* @__PURE__ */ jsxs19(Fragment7, { children: [
1338
1571
  " \u2014 ",
1339
1572
  joinTierMoney(tier.priceCents)
1340
1573
  ] })
@@ -1346,19 +1579,28 @@ function tierNeedsPayment(selected, paymentsReady) {
1346
1579
  }
1347
1580
 
1348
1581
  // src/ui/join-application-form.tsx
1349
- import { jsx as jsx17, jsxs as jsxs17 } from "preact/jsx-runtime";
1582
+ import { jsx as jsx21, jsxs as jsxs20 } from "preact/jsx-runtime";
1350
1583
  function JoinApplicationForm({ props, copy, invitation, requestedSeat, tierSelection, giftOfferId, selectGift, submitting, error, submit }) {
1351
- const disabled = submitting || props.submitDisabled === true;
1352
- return /* @__PURE__ */ jsxs17("form", { className: "join-form", onSubmit: (event) => void submit(event), children: [
1353
- !requestedSeat && (props.config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs17("fieldset", { children: [
1354
- /* @__PURE__ */ jsx17("legend", { children: copy.gift.optionsLabel }),
1355
- /* @__PURE__ */ jsx17("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => selectGift(null), children: copy.gift.chooseSelf }),
1356
- /* @__PURE__ */ jsx17(GiftOfferCards, { offers: props.config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: selectGift })
1584
+ const form = useRef6(null);
1585
+ const ready = useFormReadiness(form);
1586
+ const disabled = submitting || props.submitDisabled === true || !ready || !validJoinRecipient(props.payment?.initialNamedSeat);
1587
+ return /* @__PURE__ */ jsxs20("form", { ref: form, className: "join-form", onSubmit: (event) => {
1588
+ if (!isFormReady(event.currentTarget)) {
1589
+ event.preventDefault();
1590
+ event.currentTarget.reportValidity();
1591
+ return;
1592
+ }
1593
+ void submit(event);
1594
+ }, children: [
1595
+ !requestedSeat && (props.config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs20("fieldset", { children: [
1596
+ /* @__PURE__ */ jsx21("legend", { children: copy.gift.optionsLabel }),
1597
+ /* @__PURE__ */ jsx21("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => selectGift(null), children: copy.gift.chooseSelf }),
1598
+ /* @__PURE__ */ jsx21(GiftOfferCards, { offers: props.config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: selectGift })
1357
1599
  ] }) : null,
1358
1600
  !requestedSeat && (props.config.tiers?.length ?? 0) > 1 ? props.renderTiers?.(tierSelection) ?? TierPlaceholder(tierSelection) : null,
1359
1601
  typeof props.children === "function" ? props.children({ invitation }) : props.children,
1360
- error ? /* @__PURE__ */ jsx17("p", { className: "join-error", role: "alert", children: error }) : null,
1361
- props.renderSubmit?.({ submitting, disabled }) ?? /* @__PURE__ */ jsx17("button", { className: "submit-btn", type: "submit", disabled, children: submitting ? copy.form.submitting : copy.form.submit })
1602
+ error ? /* @__PURE__ */ jsx21("p", { className: "join-error", role: "alert", children: error }) : null,
1603
+ props.renderSubmit?.({ submitting, disabled }) ?? /* @__PURE__ */ jsx21(JoinSubmitButton, { disabled, children: submitting ? copy.form.submitting : copy.form.submit })
1362
1604
  ] });
1363
1605
  }
1364
1606
 
@@ -1383,8 +1625,8 @@ function joinStateFromApplicationResponse(data, fallbackNeedsPayment, submittedG
1383
1625
 
1384
1626
  // src/ui/gift-code-redeemer.tsx
1385
1627
  import { InlineActionField } from "@odla-ai/ui/components";
1386
- import { useState as useState14 } from "preact/hooks";
1387
- import { Fragment as Fragment9, jsx as jsx18, jsxs as jsxs18 } from "preact/jsx-runtime";
1628
+ import { useState as useState16 } from "preact/hooks";
1629
+ import { Fragment as Fragment8, jsx as jsx22, jsxs as jsxs21 } from "preact/jsx-runtime";
1388
1630
  var GIFT_CODE_REDEEMER_CSS = `
1389
1631
  .gift-code-redeemer{container-type:inline-size;display:grid;gap:var(--ui-space-1,.35rem);margin:0}
1390
1632
  .gift-code-redeemer .inline-action-field-label{display:block;color:var(--ui-text-muted,#66777f);font-size:var(--ui-text-xs,.72rem);font-weight:var(--ui-weight-strong,700);letter-spacing:var(--ui-tracking-caps,.08em);text-transform:uppercase}
@@ -1399,10 +1641,10 @@ var GIFT_CODE_REDEEMER_CSS = `
1399
1641
  @container (max-width:26rem){.gift-code-redeemer .inline-action-field-row{grid-template-columns:minmax(0,1fr)}.gift-code-redeemer .inline-action-field-action{width:100%}}
1400
1642
  `;
1401
1643
  function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1402
- const [code, setCode] = useState14("");
1403
- const [loading, setLoading] = useState14(false);
1404
- const [message, setMessage] = useState14("");
1405
- const [messageTone, setMessageTone] = useState14("neutral");
1644
+ const [code, setCode] = useState16("");
1645
+ const [loading, setLoading] = useState16(false);
1646
+ const [message, setMessage] = useState16("");
1647
+ const [messageTone, setMessageTone] = useState16("neutral");
1406
1648
  const normalizedCode = code.trim();
1407
1649
  const redeem = async () => {
1408
1650
  if (!normalizedCode || loading) return;
@@ -1429,9 +1671,9 @@ function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1429
1671
  setLoading(false);
1430
1672
  }
1431
1673
  };
1432
- return /* @__PURE__ */ jsxs18(Fragment9, { children: [
1433
- /* @__PURE__ */ jsx18("style", { children: GIFT_CODE_REDEEMER_CSS }),
1434
- /* @__PURE__ */ jsx18(
1674
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1675
+ /* @__PURE__ */ jsx22("style", { children: GIFT_CODE_REDEEMER_CSS }),
1676
+ /* @__PURE__ */ jsx22(
1435
1677
  InlineActionField,
1436
1678
  {
1437
1679
  id,
@@ -1487,12 +1729,12 @@ async function loadJoinResume(applicationId, fetcher = fetch) {
1487
1729
  }
1488
1730
 
1489
1731
  // src/ui/join-gift-claim.tsx
1490
- import { jsx as jsx19, jsxs as jsxs19 } from "preact/jsx-runtime";
1732
+ import { jsx as jsx23, jsxs as jsxs22 } from "preact/jsx-runtime";
1491
1733
  function JoinGiftClaim(props) {
1492
- return /* @__PURE__ */ jsxs19("section", { className: "join-gift-claim card", children: [
1493
- /* @__PURE__ */ jsx19("div", { className: "card-label", children: props.copy.claimTitle }),
1494
- /* @__PURE__ */ jsx19("p", { children: props.copy.claimBody }),
1495
- /* @__PURE__ */ jsx19(
1734
+ return /* @__PURE__ */ jsxs22("section", { className: "join-gift-claim card", children: [
1735
+ /* @__PURE__ */ jsx23("div", { className: "card-label", children: props.copy.claimTitle }),
1736
+ /* @__PURE__ */ jsx23("p", { children: props.copy.claimBody }),
1737
+ /* @__PURE__ */ jsx23(
1496
1738
  GiftCodeRedeemer,
1497
1739
  {
1498
1740
  api: props.api,
@@ -1516,21 +1758,21 @@ function JoinGiftClaim(props) {
1516
1758
  }
1517
1759
 
1518
1760
  // src/ui/join-named-seat.tsx
1519
- import { useState as useState15 } from "preact/hooks";
1520
- import { jsx as jsx20, jsxs as jsxs20 } from "preact/jsx-runtime";
1761
+ import { useState as useState17 } from "preact/hooks";
1762
+ import { jsx as jsx24, jsxs as jsxs23 } from "preact/jsx-runtime";
1521
1763
  function JoinNamedSeat(props) {
1522
1764
  const seatId = props.seatId ?? "";
1523
1765
  const inherited = useChapterCopy(), copy = props.copy ?? inherited.join.namedSeat;
1524
- const [accepted, setAccepted] = useState15(false), [busy, setBusy] = useState15(false), [error, setError] = useState15("");
1525
- return /* @__PURE__ */ jsxs20("section", { className: "join-named-seat card", children: [
1526
- /* @__PURE__ */ jsx20("h2", { children: copy.title }),
1527
- /* @__PURE__ */ jsx20("p", { children: copy.body }),
1528
- /* @__PURE__ */ jsxs20("label", { className: "journey-consent", children: [
1529
- /* @__PURE__ */ jsx20("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
1530
- /* @__PURE__ */ jsx20("span", { children: copy.consent })
1766
+ const [accepted, setAccepted] = useState17(false), [busy, setBusy] = useState17(false), [error, setError] = useState17("");
1767
+ return /* @__PURE__ */ jsxs23("section", { className: "join-named-seat card", children: [
1768
+ /* @__PURE__ */ jsx24("h2", { children: copy.title }),
1769
+ /* @__PURE__ */ jsx24("p", { children: copy.body }),
1770
+ /* @__PURE__ */ jsxs23("label", { className: "journey-consent", children: [
1771
+ /* @__PURE__ */ jsx24("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
1772
+ /* @__PURE__ */ jsx24("span", { children: copy.consent })
1531
1773
  ] }),
1532
- error ? /* @__PURE__ */ jsx20("p", { role: "alert", children: error }) : null,
1533
- /* @__PURE__ */ jsx20("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
1774
+ error ? /* @__PURE__ */ jsx24("p", { role: "alert", children: error }) : null,
1775
+ /* @__PURE__ */ jsx24("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
1534
1776
  setBusy(true);
1535
1777
  setError("");
1536
1778
  try {
@@ -1550,12 +1792,12 @@ function JoinNamedSeat(props) {
1550
1792
  }
1551
1793
 
1552
1794
  // src/ui/join-invitation.tsx
1553
- import { useEffect as useEffect8, useState as useState16 } from "preact/hooks";
1554
- import { jsx as jsx21, jsxs as jsxs21 } from "preact/jsx-runtime";
1795
+ import { useEffect as useEffect8, useState as useState18 } from "preact/hooks";
1796
+ import { jsx as jsx25, jsxs as jsxs24 } from "preact/jsx-runtime";
1555
1797
  function useJoinInvitation(seatId, api, fallback) {
1556
- const [invitation, setInvitation] = useState16(null);
1557
- const [error, setError] = useState16("");
1558
- const [revision, setRevision] = useState16(0);
1798
+ const [invitation, setInvitation] = useState18(null);
1799
+ const [error, setError] = useState18("");
1800
+ const [revision, setRevision] = useState18(0);
1559
1801
  useEffect8(() => {
1560
1802
  let live = true;
1561
1803
  setInvitation(null);
@@ -1578,12 +1820,22 @@ function useJoinInvitation(seatId, api, fallback) {
1578
1820
  return { invitation: invitation?.seatId === seatId ? invitation : null, error, retry: () => setRevision((value) => value + 1) };
1579
1821
  }
1580
1822
  function JoinInvitationNotice(props) {
1581
- return /* @__PURE__ */ jsxs21("aside", { className: "journey-invitation", "aria-label": props.copy.title, children: [
1582
- /* @__PURE__ */ jsx21("p", { children: /* @__PURE__ */ jsx21("strong", { children: formatChapterCopy(props.copy.from, { purchaserName: props.invitation.purchaserName }) }) }),
1583
- /* @__PURE__ */ jsx21("p", { children: formatChapterCopy(props.copy.prepaid, { tierName: props.tierName }) })
1823
+ return /* @__PURE__ */ jsxs24("aside", { className: "journey-invitation", "aria-label": props.copy.title, children: [
1824
+ /* @__PURE__ */ jsx25("p", { children: /* @__PURE__ */ jsx25("strong", { children: formatChapterCopy(props.copy.from, { purchaserName: props.invitation.purchaserName }) }) }),
1825
+ /* @__PURE__ */ jsx25("p", { children: formatChapterCopy(props.copy.prepaid, { tierName: props.tierName }) })
1584
1826
  ] });
1585
1827
  }
1586
1828
 
1829
+ // src/ui/join-simulation.ts
1830
+ function joinSimulationPayment(simulation, priceCents) {
1831
+ if (!simulation) return void 0;
1832
+ return simulation.payment ?? { lineItems: {
1833
+ standardCents: priceCents,
1834
+ discountCents: 0,
1835
+ dueTodayCents: priceCents
1836
+ } };
1837
+ }
1838
+
1587
1839
  // src/ui/join-pending.ts
1588
1840
  import { useEffect as useEffect9 } from "preact/hooks";
1589
1841
  function useJoinPending(state, loadResume, setState) {
@@ -1610,14 +1862,14 @@ function useJoinPending(state, loadResume, setState) {
1610
1862
  }
1611
1863
 
1612
1864
  // src/ui/payment-receipt.tsx
1613
- import { useEffect as useEffect10, useState as useState17 } from "preact/hooks";
1614
- import { Fragment as Fragment10, jsx as jsx22, jsxs as jsxs22 } from "preact/jsx-runtime";
1865
+ import { useEffect as useEffect10, useState as useState19 } from "preact/hooks";
1866
+ import { Fragment as Fragment9, jsx as jsx26, jsxs as jsxs25 } from "preact/jsx-runtime";
1615
1867
  function PaymentReceiptCard({ applicationId }) {
1616
- const [receipt, setReceipt] = useState17(null);
1617
- const [error, setError] = useState17("");
1618
- const [revision, setRevision] = useState17(0);
1619
- const [emailState, setEmailState] = useState17("idle");
1620
- const [previewEmail, setPreviewEmail] = useState17(false);
1868
+ const [receipt, setReceipt] = useState19(null);
1869
+ const [error, setError] = useState19("");
1870
+ const [revision, setRevision] = useState19(0);
1871
+ const [emailState, setEmailState] = useState19("idle");
1872
+ const [previewEmail, setPreviewEmail] = useState19(false);
1621
1873
  const endpoint = `/api/payments/receipt?application=${encodeURIComponent(applicationId)}`;
1622
1874
  useEffect10(() => {
1623
1875
  let live = true, timer;
@@ -1645,9 +1897,9 @@ function PaymentReceiptCard({ applicationId }) {
1645
1897
  if (timer) clearTimeout(timer);
1646
1898
  };
1647
1899
  }, [endpoint, revision]);
1648
- if (!receipt) return error ? /* @__PURE__ */ jsxs22("section", { "aria-label": "Payment receipt", children: [
1649
- /* @__PURE__ */ jsx22("p", { role: "status", children: error }),
1650
- /* @__PURE__ */ jsx22("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Reload receipt" })
1900
+ if (!receipt) return error ? /* @__PURE__ */ jsxs25("section", { "aria-label": "Payment receipt", children: [
1901
+ /* @__PURE__ */ jsx26("p", { role: "status", children: error }),
1902
+ /* @__PURE__ */ jsx26("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Reload receipt" })
1651
1903
  ] }) : null;
1652
1904
  const money = (cents) => new Intl.NumberFormat("en-US", { style: "currency", currency: receipt.currency }).format(cents / 100);
1653
1905
  const email = async () => {
@@ -1664,38 +1916,38 @@ function PaymentReceiptCard({ applicationId }) {
1664
1916
  setError("We could not confirm email delivery. You can print or save your receipt.");
1665
1917
  }
1666
1918
  };
1667
- return /* @__PURE__ */ jsxs22("section", { className: "payment-receipt card", "aria-label": "Payment receipt", children: [
1668
- /* @__PURE__ */ jsx22("h2", { children: "Payment received" }),
1669
- /* @__PURE__ */ jsx22("p", { children: receipt.tierName }),
1670
- /* @__PURE__ */ jsxs22("dl", { className: "journey-summary", children: [
1671
- /* @__PURE__ */ jsx22("dt", { children: "Membership" }),
1672
- /* @__PURE__ */ jsx22("dd", { children: money(receipt.membershipCents) }),
1673
- receipt.namedSeat ? /* @__PURE__ */ jsxs22(Fragment10, { children: [
1674
- /* @__PURE__ */ jsxs22("dt", { children: [
1919
+ return /* @__PURE__ */ jsxs25("section", { className: "payment-receipt card", "aria-label": "Payment receipt", children: [
1920
+ /* @__PURE__ */ jsx26("h2", { children: "Payment received" }),
1921
+ /* @__PURE__ */ jsx26("p", { children: receipt.tierName }),
1922
+ /* @__PURE__ */ jsxs25("dl", { className: "journey-summary", children: [
1923
+ /* @__PURE__ */ jsx26("dt", { children: "Membership" }),
1924
+ /* @__PURE__ */ jsx26("dd", { children: money(receipt.membershipCents) }),
1925
+ receipt.namedSeat ? /* @__PURE__ */ jsxs25(Fragment9, { children: [
1926
+ /* @__PURE__ */ jsxs25("dt", { children: [
1675
1927
  "Additional member \u2014 ",
1676
1928
  receipt.namedSeat.recipientName
1677
1929
  ] }),
1678
- /* @__PURE__ */ jsxs22("dd", { children: [
1930
+ /* @__PURE__ */ jsxs25("dd", { children: [
1679
1931
  receipt.namedSeat.amountCents === 0 ? "Included \u2014 " : "",
1680
1932
  money(receipt.namedSeat.amountCents)
1681
1933
  ] })
1682
1934
  ] }) : null,
1683
- /* @__PURE__ */ jsx22("dt", { children: "Total paid" }),
1684
- /* @__PURE__ */ jsx22("dd", { children: money(receipt.totalCents) })
1935
+ /* @__PURE__ */ jsx26("dt", { children: "Total paid" }),
1936
+ /* @__PURE__ */ jsx26("dd", { children: money(receipt.totalCents) })
1685
1937
  ] }),
1686
- /* @__PURE__ */ jsxs22("div", { className: "journey-actions", children: [
1687
- /* @__PURE__ */ jsx22("a", { className: "apply-link journey-secondary", href: `${endpoint}&format=html`, target: "_blank", rel: "noopener noreferrer", children: "Print / save receipt" }),
1688
- /* @__PURE__ */ jsx22("button", { type: "button", disabled: emailState !== "idle", onClick: () => void email(), children: emailState === "sending" ? "Sending\u2026" : emailState === "sent" ? "Receipt emailed" : "Email me my receipt" })
1938
+ /* @__PURE__ */ jsxs25("div", { className: "journey-actions", children: [
1939
+ /* @__PURE__ */ jsx26("a", { className: "apply-link journey-secondary", href: `${endpoint}&format=html`, target: "_blank", rel: "noopener noreferrer", children: "Print / save receipt" }),
1940
+ /* @__PURE__ */ jsx26("button", { type: "button", disabled: emailState !== "idle", onClick: () => void email(), children: emailState === "sending" ? "Sending\u2026" : emailState === "sent" ? "Receipt emailed" : "Email me my receipt" })
1689
1941
  ] }),
1690
- emailState === "sent" ? /* @__PURE__ */ jsx22("p", { role: "status", children: previewEmail ? "Test receipt sent to the configured test inbox." : "Sent to the email used for your payment." }) : null,
1691
- error ? /* @__PURE__ */ jsx22("p", { role: "alert", children: error }) : null
1942
+ emailState === "sent" ? /* @__PURE__ */ jsx26("p", { role: "status", children: previewEmail ? "Test receipt sent to the configured test inbox." : "Sent to the email used for your payment." }) : null,
1943
+ error ? /* @__PURE__ */ jsx26("p", { role: "alert", children: error }) : null
1692
1944
  ] });
1693
1945
  }
1694
1946
 
1695
1947
  // src/ui/join.tsx
1696
- import { Fragment as Fragment11, jsx as jsx23, jsxs as jsxs23 } from "preact/jsx-runtime";
1948
+ import { Fragment as Fragment10, jsx as jsx27, jsxs as jsxs26 } from "preact/jsx-runtime";
1697
1949
  function JoinIsland(props) {
1698
- return /* @__PURE__ */ jsx23(JourneyFrame, { children: /* @__PURE__ */ jsx23(JoinFlow, { ...props }) });
1950
+ return /* @__PURE__ */ jsx27(JourneyFrame, { children: /* @__PURE__ */ jsx27(JoinFlow, { ...props }) });
1699
1951
  }
1700
1952
  function JoinFlow(props) {
1701
1953
  const inheritedCopy = useChapterCopy();
@@ -1716,19 +1968,14 @@ function JoinFlow(props) {
1716
1968
  const invitation = invitationState.invitation;
1717
1969
  const selectedTierId = invitation?.skuId ?? tierSelection.selectedTierId;
1718
1970
  const selectedTier = tiers.find((tier) => tier.id === selectedTierId) ?? tierSelection.selected;
1719
- const [state, setState] = useState18(requestedSeat ? { step: "form" } : props.initialState ?? { step: "form" });
1720
- const [error, setError] = useState18(null);
1721
- const [submitting, setSubmitting] = useState18(false);
1722
- const [giftOfferId, setGiftOfferId] = useState18(props.initialGiftOfferId ?? null);
1723
- const simulatedPayment = props.simulation ? props.simulation.payment ?? {
1724
- lineItems: {
1725
- standardCents: selectedTier?.priceCents ?? 0,
1726
- discountCents: 0,
1727
- dueTodayCents: selectedTier?.priceCents ?? 0
1728
- }
1729
- } : void 0;
1971
+ const [state, setState] = useState20(requestedSeat ? { step: "form" } : props.initialState ?? { step: "form" });
1972
+ const [error, setError] = useState20(null);
1973
+ const [submitting, setSubmitting] = useState20(false);
1974
+ const [editingNamedSeat, setEditingNamedSeat] = useState20(false);
1975
+ const [giftOfferId, setGiftOfferId] = useState20(props.initialGiftOfferId ?? null);
1976
+ const simulatedPayment = joinSimulationPayment(props.simulation, selectedTier?.priceCents ?? 0);
1730
1977
  const simulatedBooking = props.simulation ? props.simulation.booking ?? { slots: [], timezone: "UTC" } : void 0;
1731
- const submissionId = useRef3(null);
1978
+ const submissionId = useRef7(null);
1732
1979
  useEffect11(() => {
1733
1980
  if (props.simulation) return;
1734
1981
  const resolver = requestedSeat ? () => invitation?.applicationId ? loadJoinResume(invitation.applicationId) : Promise.resolve(null) : props.loadResume ?? (() => {
@@ -1750,6 +1997,10 @@ function JoinFlow(props) {
1750
1997
  const submit = async (e) => {
1751
1998
  e.preventDefault();
1752
1999
  if (submitting || props.submitDisabled === true || requestedSeat && !invitation) return;
2000
+ if (!validJoinRecipient(props.payment?.initialNamedSeat)) {
2001
+ setError("Enter your family member's full name and valid email before continuing.");
2002
+ return;
2003
+ }
1753
2004
  setSubmitting(true);
1754
2005
  setError(null);
1755
2006
  try {
@@ -1798,13 +2049,13 @@ function JoinFlow(props) {
1798
2049
  setSubmitting(false);
1799
2050
  }
1800
2051
  };
1801
- if (requestedSeat && !invitation) return /* @__PURE__ */ jsxs23("section", { className: "card", "aria-live": "polite", children: [
1802
- /* @__PURE__ */ jsx23("h2", { children: seatCopy.title }),
1803
- /* @__PURE__ */ jsx23("p", { role: invitationState.error ? "alert" : "status", children: invitationState.error || seatCopy.loading }),
1804
- invitationState.error ? /* @__PURE__ */ jsx23("button", { type: "button", onClick: invitationState.retry, children: seatCopy.retry }) : null
2052
+ if (requestedSeat && !invitation) return /* @__PURE__ */ jsxs26("section", { className: "card", "aria-live": "polite", children: [
2053
+ /* @__PURE__ */ jsx27("h2", { children: seatCopy.title }),
2054
+ /* @__PURE__ */ jsx27("p", { role: invitationState.error ? "alert" : "status", children: invitationState.error || seatCopy.loading }),
2055
+ invitationState.error ? /* @__PURE__ */ jsx27("button", { type: "button", onClick: invitationState.retry, children: seatCopy.retry }) : null
1805
2056
  ] });
1806
- const header = /* @__PURE__ */ jsxs23(Fragment11, { children: [
1807
- invitation ? /* @__PURE__ */ jsx23(
2057
+ const header = /* @__PURE__ */ jsxs26(Fragment10, { children: [
2058
+ invitation ? /* @__PURE__ */ jsx27(
1808
2059
  JoinInvitationNotice,
1809
2060
  {
1810
2061
  invitation,
@@ -1814,51 +2065,52 @@ function JoinFlow(props) {
1814
2065
  ) : null,
1815
2066
  renderStepHeader?.({
1816
2067
  state,
2068
+ editingNamedSeat: state.step === "payment" && editingNamedSeat,
1817
2069
  ...invitation ? { invitation } : {},
1818
2070
  ..."applicationId" in state ? { applicationId: state.applicationId } : {}
1819
2071
  }),
1820
- !props.simulation && !requestedSeat && ["paymentPending", "booking", "accepted", "done"].includes(state.step) && "applicationId" in state ? /* @__PURE__ */ jsx23(PaymentReceiptCard, { applicationId: state.applicationId }) : null
2072
+ !props.simulation && !requestedSeat && ["paymentPending", "booking", "accepted", "done"].includes(state.step) && "applicationId" in state ? /* @__PURE__ */ jsx27(PaymentReceiptCard, { applicationId: state.applicationId }) : null
1821
2073
  ] });
1822
2074
  if (state.step === "closed") {
1823
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2075
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1824
2076
  header,
1825
- /* @__PURE__ */ jsxs23("section", { className: "card", "aria-label": "Application closed", children: [
1826
- /* @__PURE__ */ jsx23("h2", { children: "This application is closed" }),
1827
- /* @__PURE__ */ jsx23("p", { children: "This link cannot resume payment or booking. Sign in to view your application status, or contact the chapter if you need help." }),
1828
- /* @__PURE__ */ jsx23("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
2077
+ /* @__PURE__ */ jsxs26("section", { className: "card", "aria-label": "Application closed", children: [
2078
+ /* @__PURE__ */ jsx27("h2", { children: "This application is closed" }),
2079
+ /* @__PURE__ */ jsx27("p", { children: "This link cannot resume payment or booking. Sign in to view your application status, or contact the chapter if you need help." }),
2080
+ /* @__PURE__ */ jsx27("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1829
2081
  ] })
1830
2082
  ] });
1831
2083
  }
1832
2084
  if (state.step === "done") {
1833
- const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs23("div", { className: "join-done card", children: [
1834
- /* @__PURE__ */ jsx23("div", { className: "card-label", children: copy.done.label }),
1835
- /* @__PURE__ */ jsx23("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
1836
- /* @__PURE__ */ jsx23("p", { className: "meeting-note", children: copy.done.calendarInvite }),
1837
- /* @__PURE__ */ jsx23("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
2085
+ const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs26("div", { className: "join-done card", children: [
2086
+ /* @__PURE__ */ jsx27("div", { className: "card-label", children: copy.done.label }),
2087
+ /* @__PURE__ */ jsx27("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
2088
+ /* @__PURE__ */ jsx27("p", { className: "meeting-note", children: copy.done.calendarInvite }),
2089
+ /* @__PURE__ */ jsx27("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1838
2090
  ] });
1839
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2091
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1840
2092
  header,
1841
2093
  body
1842
2094
  ] });
1843
2095
  }
1844
2096
  if (state.step === "accepted") {
1845
- const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx23(JoinAccepted, { copy, membersHref });
1846
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2097
+ const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx27(JoinAccepted, { copy, membersHref });
2098
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1847
2099
  header,
1848
2100
  body
1849
2101
  ] });
1850
2102
  }
1851
2103
  if (state.step === "gift") {
1852
- const body = renderGift?.(state) ?? /* @__PURE__ */ jsx23(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
1853
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2104
+ const body = renderGift?.(state) ?? /* @__PURE__ */ jsx27(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
2105
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1854
2106
  header,
1855
2107
  body
1856
2108
  ] });
1857
2109
  }
1858
2110
  if (state.step === "booking") {
1859
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2111
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1860
2112
  header,
1861
- /* @__PURE__ */ jsx23(
2113
+ /* @__PURE__ */ jsx27(
1862
2114
  JoinBooking,
1863
2115
  {
1864
2116
  applicationId: state.applicationId,
@@ -1873,9 +2125,9 @@ function JoinFlow(props) {
1873
2125
  }
1874
2126
  if (state.step === "payment") {
1875
2127
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation && !state.giftPurchase) {
1876
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2128
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1877
2129
  header,
1878
- /* @__PURE__ */ jsx23(
2130
+ /* @__PURE__ */ jsx27(
1879
2131
  JoinNamedSeat,
1880
2132
  {
1881
2133
  api: props.namedSeatClaimApi,
@@ -1888,9 +2140,9 @@ function JoinFlow(props) {
1888
2140
  ] });
1889
2141
  }
1890
2142
  const Payment = state.giftPurchase || props.simulation ? PaymentStep : MembershipPaymentStep;
1891
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2143
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1892
2144
  header,
1893
- props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx23(
2145
+ props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx27(
1894
2146
  JoinGiftClaim,
1895
2147
  {
1896
2148
  api: props.giftClaimApi,
@@ -1901,17 +2153,15 @@ function JoinFlow(props) {
1901
2153
  onError: () => setError(copy.form.unexpectedFailure)
1902
2154
  }
1903
2155
  ) : null,
1904
- /* @__PURE__ */ jsx23(
2156
+ /* @__PURE__ */ jsx27(
1905
2157
  Payment,
1906
2158
  {
1907
2159
  applicationId: state.applicationId,
1908
2160
  refundPolicyText: config.refundPolicyText ?? "",
1909
2161
  merchantDisclosureText: config.merchantDisclosureText,
1910
2162
  copy: copy.payment,
1911
- appearance: props.payment?.appearance,
1912
- initialNamedSeat: props.payment?.initialNamedSeat,
1913
- fonts: props.payment?.fonts,
1914
- renderPriceLines: props.payment?.renderPriceLines,
2163
+ ...props.payment,
2164
+ onNamedSeatEditingChange: setEditingNamedSeat,
1915
2165
  simulation: simulatedPayment,
1916
2166
  onPaid: (result) => {
1917
2167
  if (props.simulation) {
@@ -1931,9 +2181,9 @@ function JoinFlow(props) {
1931
2181
  }
1932
2182
  if (state.step === "paymentPending") {
1933
2183
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation) {
1934
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2184
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1935
2185
  header,
1936
- /* @__PURE__ */ jsx23(
2186
+ /* @__PURE__ */ jsx27(
1937
2187
  JoinNamedSeat,
1938
2188
  {
1939
2189
  api: props.namedSeatClaimApi,
@@ -1945,14 +2195,14 @@ function JoinFlow(props) {
1945
2195
  )
1946
2196
  ] });
1947
2197
  }
1948
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2198
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1949
2199
  header,
1950
- /* @__PURE__ */ jsx23("p", { className: "pay-status", role: "status", children: copy.payment.pending })
2200
+ /* @__PURE__ */ jsx27("p", { className: "pay-status", role: "status", children: copy.payment.pending })
1951
2201
  ] });
1952
2202
  }
1953
- return /* @__PURE__ */ jsxs23(Fragment11, { children: [
2203
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1954
2204
  header,
1955
- /* @__PURE__ */ jsx23(
2205
+ /* @__PURE__ */ jsx27(
1956
2206
  JoinApplicationForm,
1957
2207
  {
1958
2208
  props,
@@ -2142,7 +2392,7 @@ function membershipPageDocument(input, options = {}) {
2142
2392
  }
2143
2393
 
2144
2394
  // src/ui/membership-cards.tsx
2145
- import { jsx as jsx24, jsxs as jsxs24 } from "preact/jsx-runtime";
2395
+ import { jsx as jsx28, jsxs as jsxs27 } from "preact/jsx-runtime";
2146
2396
  var MEMBERSHIP_CARD_TOKEN_DEFAULTS = Object.freeze({
2147
2397
  "--ui-membership-grid-gap": "1.4rem",
2148
2398
  "--ui-membership-card-padding": "2rem 1.7rem 1.8rem",
@@ -2211,67 +2461,67 @@ function membershipApplicationHref(applicationPath, tierId) {
2211
2461
  }
2212
2462
  function TierAction(props) {
2213
2463
  if (props.mode === "select") {
2214
- return /* @__PURE__ */ jsxs24("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
2464
+ return /* @__PURE__ */ jsxs27("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
2215
2465
  props.label,
2216
- /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", children: "\u2192" })
2466
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u2192" })
2217
2467
  ] });
2218
2468
  }
2219
2469
  if (props.mode === "preview") {
2220
- return /* @__PURE__ */ jsxs24("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
2470
+ return /* @__PURE__ */ jsxs27("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
2221
2471
  props.label,
2222
- /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", children: "\u2192" })
2472
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u2192" })
2223
2473
  ] });
2224
2474
  }
2225
- return /* @__PURE__ */ jsxs24("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
2475
+ return /* @__PURE__ */ jsxs27("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
2226
2476
  props.label,
2227
- /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", children: "\u2192" })
2477
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u2192" })
2228
2478
  ] });
2229
2479
  }
2230
2480
  function TierCard(props) {
2231
2481
  const { tier, presentation } = props;
2232
2482
  const summary = presentation.summaryMarkdown ?? tier.blurb;
2233
2483
  const action = presentation.ctaLabel ?? (tier.free ? "Join for free" : `Choose ${tier.name}`);
2234
- return /* @__PURE__ */ jsxs24("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
2235
- presentation.badge ? /* @__PURE__ */ jsx24("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
2236
- /* @__PURE__ */ jsx24("h3", { className: "chapter-membership-name", children: tier.name }),
2237
- /* @__PURE__ */ jsxs24("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
2238
- presentation.capacity ? /* @__PURE__ */ jsxs24("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
2239
- /* @__PURE__ */ jsx24("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
2240
- /* @__PURE__ */ jsx24("small", { children: presentation.capacity.label ?? "available" })
2484
+ return /* @__PURE__ */ jsxs27("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
2485
+ presentation.badge ? /* @__PURE__ */ jsx28("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
2486
+ /* @__PURE__ */ jsx28("h3", { className: "chapter-membership-name", children: tier.name }),
2487
+ /* @__PURE__ */ jsxs27("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
2488
+ presentation.capacity ? /* @__PURE__ */ jsxs27("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
2489
+ /* @__PURE__ */ jsx28("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
2490
+ /* @__PURE__ */ jsx28("small", { children: presentation.capacity.label ?? "available" })
2241
2491
  ] }) : null,
2242
- /* @__PURE__ */ jsxs24("div", { className: "chapter-membership-price-stack", children: [
2243
- presentation.priceEyebrow ? /* @__PURE__ */ jsx24("p", { className: "chapter-membership-price-eyebrow", children: presentation.priceEyebrow }) : tier.founding ? /* @__PURE__ */ jsx24("p", { className: "chapter-membership-price-eyebrow", children: "Founding member rate" }) : null,
2244
- /* @__PURE__ */ jsxs24("div", { className: "chapter-membership-price-row", children: [
2245
- tier.founding ? /* @__PURE__ */ jsx24("span", { className: "chapter-membership-compare", children: fmtMoney(tier.priceCents) }) : presentation.compareAtPriceCents !== void 0 ? /* @__PURE__ */ jsx24("span", { className: "chapter-membership-compare", children: fmtMoney(presentation.compareAtPriceCents) }) : null,
2246
- /* @__PURE__ */ jsx24("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.founding ? tier.founding.dueTodayCents : tier.priceCents) }),
2247
- !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx24("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
2492
+ /* @__PURE__ */ jsxs27("div", { className: "chapter-membership-price-stack", children: [
2493
+ presentation.priceEyebrow ? /* @__PURE__ */ jsx28("p", { className: "chapter-membership-price-eyebrow", children: presentation.priceEyebrow }) : tier.founding ? /* @__PURE__ */ jsx28("p", { className: "chapter-membership-price-eyebrow", children: "Founding member rate" }) : null,
2494
+ /* @__PURE__ */ jsxs27("div", { className: "chapter-membership-price-row", children: [
2495
+ tier.founding ? /* @__PURE__ */ jsx28("span", { className: "chapter-membership-compare", children: fmtMoney(tier.priceCents) }) : presentation.compareAtPriceCents !== void 0 ? /* @__PURE__ */ jsx28("span", { className: "chapter-membership-compare", children: fmtMoney(presentation.compareAtPriceCents) }) : null,
2496
+ /* @__PURE__ */ jsx28("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.founding ? tier.founding.dueTodayCents : tier.priceCents) }),
2497
+ !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx28("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
2248
2498
  ] })
2249
2499
  ] })
2250
2500
  ] }),
2251
- presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx24(Markdown2, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
2252
- summary ? /* @__PURE__ */ jsx24(Markdown2, { className: "chapter-membership-summary", source: summary }) : null,
2253
- presentation.benefits?.length ? /* @__PURE__ */ jsx24("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs24("li", { children: [
2254
- /* @__PURE__ */ jsx24("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
2255
- /* @__PURE__ */ jsx24(Markdown2, { className: "chapter-membership-benefit", source: benefit })
2501
+ presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx28(Markdown2, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
2502
+ summary ? /* @__PURE__ */ jsx28(Markdown2, { className: "chapter-membership-summary", source: summary }) : null,
2503
+ presentation.benefits?.length ? /* @__PURE__ */ jsx28("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs27("li", { children: [
2504
+ /* @__PURE__ */ jsx28("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
2505
+ /* @__PURE__ */ jsx28(Markdown2, { className: "chapter-membership-benefit", source: benefit })
2256
2506
  ] }, `${tier.id}-benefit-${index}`)) }) : null,
2257
- presentation.callout ? /* @__PURE__ */ jsxs24("aside", { className: "chapter-membership-callout", children: [
2258
- presentation.callout.label ? /* @__PURE__ */ jsx24("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
2259
- /* @__PURE__ */ jsx24(Markdown2, { source: presentation.callout.bodyMarkdown })
2507
+ presentation.callout ? /* @__PURE__ */ jsxs27("aside", { className: "chapter-membership-callout", children: [
2508
+ presentation.callout.label ? /* @__PURE__ */ jsx28("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
2509
+ /* @__PURE__ */ jsx28(Markdown2, { source: presentation.callout.bodyMarkdown })
2260
2510
  ] }) : null,
2261
- /* @__PURE__ */ jsx24(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
2511
+ /* @__PURE__ */ jsx28(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
2262
2512
  ] });
2263
2513
  }
2264
2514
  function MembershipCards(props) {
2265
2515
  const document2 = membershipPageDocument(props.document, { tiers: props.tiers });
2266
2516
  const byId = new Map(props.tiers.map((tier) => [tier.id, tier]));
2267
2517
  const mode = props.mode ?? "link";
2268
- return /* @__PURE__ */ jsxs24("section", { className: ["chapter-membership", props.className].filter(Boolean).join(" "), "data-chapter-membership": MEMBERSHIP_PAGE_RENDERER, "aria-label": props.ariaLabel ?? "Membership options", children: [
2269
- /* @__PURE__ */ jsx24("style", { children: MEMBERSHIP_CARDS_CSS }),
2270
- /* @__PURE__ */ jsx24("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
2518
+ return /* @__PURE__ */ jsxs27("section", { className: ["chapter-membership", props.className].filter(Boolean).join(" "), "data-chapter-membership": MEMBERSHIP_PAGE_RENDERER, "aria-label": props.ariaLabel ?? "Membership options", children: [
2519
+ /* @__PURE__ */ jsx28("style", { children: MEMBERSHIP_CARDS_CSS }),
2520
+ /* @__PURE__ */ jsx28("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
2271
2521
  const tier = byId.get(presentation.tierId);
2272
- return /* @__PURE__ */ jsx24(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
2522
+ return /* @__PURE__ */ jsx28(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
2273
2523
  }) }),
2274
- document2.footnoteMarkdown ? /* @__PURE__ */ jsx24(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
2524
+ document2.footnoteMarkdown ? /* @__PURE__ */ jsx28(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
2275
2525
  ] });
2276
2526
  }
2277
2527
 
@@ -2285,6 +2535,11 @@ export {
2285
2535
  fmtDate,
2286
2536
  groupSlotsByDay,
2287
2537
  SlotPicker,
2538
+ ADDITIONAL_MEMBER_COPY,
2539
+ additionalMemberSelection,
2540
+ AdditionalMemberFields,
2541
+ JoinSubmitButton,
2542
+ AdditionalMemberEditor,
2288
2543
  PaymentStep,
2289
2544
  JourneyFrame,
2290
2545
  MemberRestart,
@@ -2307,4 +2562,4 @@ export {
2307
2562
  membershipApplicationHref,
2308
2563
  MembershipCards
2309
2564
  };
2310
- //# sourceMappingURL=chunk-IQZWCG42.js.map
2565
+ //# sourceMappingURL=chunk-E7CB5K2D.js.map