@odla-ai/chapter 0.56.1 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import {
2
2
  DEFAULT_JOIN_COPY,
3
3
  formatChapterCopy,
4
4
  useChapterCopy
5
- } from "./chunk-4M2PN5K3.js";
5
+ } from "./chunk-FHKZVF5G.js";
6
6
  import {
7
7
  collectFormFields
8
8
  } from "./chunk-IVXVECKR.js";
@@ -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,88 +470,60 @@ 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
 
353
498
  // src/ui/checkout-seat-review.tsx
354
- import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
499
+ import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
355
500
  function CheckoutSeatReview({ quote, label, locked, onEdit }) {
356
501
  if (!quote.namedSeat) return null;
357
- return /* @__PURE__ */ jsxs4("section", { className: "checkout-seat-review", "aria-label": label, children: [
358
- /* @__PURE__ */ jsx4("h3", { children: label }),
359
- /* @__PURE__ */ jsxs4("p", { children: [
502
+ return /* @__PURE__ */ jsxs6("section", { className: "checkout-seat-review", "aria-label": label, children: [
503
+ /* @__PURE__ */ jsx7("h3", { children: label }),
504
+ /* @__PURE__ */ jsxs6("p", { children: [
360
505
  quote.namedSeat.recipientName,
361
- /* @__PURE__ */ jsx4("br", {}),
506
+ /* @__PURE__ */ jsx7("br", {}),
362
507
  quote.namedSeat.recipientEmail
363
508
  ] }),
364
- !locked ? /* @__PURE__ */ jsx4("button", { type: "button", onClick: onEdit, children: "Edit details" }) : null
509
+ !locked ? /* @__PURE__ */ jsx7("button", { type: "button", onClick: onEdit, children: "Edit details" }) : null
365
510
  ] });
366
511
  }
367
512
 
368
513
  // src/ui/membership-payment-step.tsx
369
- import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
514
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
370
515
  function MembershipPaymentStep(props) {
516
+ const renderEditor = props.renderNamedSeatEditor ?? (props.additionalMember ? (context) => /* @__PURE__ */ jsx8(AdditionalMemberEditor, { ...props.additionalMember, ...context }) : void 0);
371
517
  const inherited = useChapterCopy();
372
518
  const copy = props.copy ?? inherited.join.payment;
373
- const [quote, setQuote] = useState4(null);
374
- const [locked, setLocked] = useState4(false), [editing, setEditing] = useState4(Boolean(props.initialNamedSeat));
375
- const [prefillConsumed, setPrefillConsumed] = useState4(false);
376
- const [error, setError] = useState4(false);
377
- 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);
378
524
  useEffect(() => {
379
- props.onNamedSeatEditingChange?.(Boolean(props.renderNamedSeatEditor && editing && !locked && quote));
380
- }, [editing, locked, Boolean(quote), Boolean(props.renderNamedSeatEditor)]);
525
+ props.onNamedSeatEditingChange?.(Boolean(renderEditor && editing && !locked && quote));
526
+ }, [editing, locked, Boolean(quote), Boolean(renderEditor)]);
381
527
  useEffect(() => {
382
528
  let live = true;
383
529
  setQuote(null);
@@ -388,7 +534,7 @@ function MembershipPaymentStep(props) {
388
534
  value.checkoutStarted = response.headers.get("x-chapter-checkout-started") === "true";
389
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");
390
536
  if (!live) return;
391
- if (props.renderNamedSeatEditor && props.initialNamedSeat && !prefillConsumed && !value.namedSeat && !value.checkoutStarted && value.seatOffer) {
537
+ if (renderEditor && props.initialNamedSeat && !prefillConsumed && !value.namedSeat && !value.checkoutStarted && value.seatOffer) {
392
538
  const saved = await fetch(`/api/payments/quote?application=${encodeURIComponent(props.applicationId)}`, {
393
539
  method: "POST",
394
540
  cache: "no-store",
@@ -427,10 +573,10 @@ function MembershipPaymentStep(props) {
427
573
  setQuote(null);
428
574
  setRevision((value) => value + 1);
429
575
  };
430
- if (!quote || quote.applicationId !== props.applicationId) return /* @__PURE__ */ jsxs5("div", { className: "join-pay", role: "status", children: [
431
- props.merchantDisclosureText ? /* @__PURE__ */ jsx5("p", { className: "compliance-merchant", children: props.merchantDisclosureText }) : null,
432
- /* @__PURE__ */ jsx5("p", { children: error ? copy.setupFailed : copy.preparing }),
433
- error ? /* @__PURE__ */ jsx5("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
434
580
  ] });
435
581
  const startCheckout = async () => {
436
582
  setLocked(true);
@@ -470,9 +616,9 @@ function MembershipPaymentStep(props) {
470
616
  setPrefillConsumed(true);
471
617
  };
472
618
  const pickLater = !quote.namedSeat && quote.seatOffer?.amountCents === 0;
473
- if (props.renderNamedSeatEditor && editing && !locked && quote.seatOffer) return /* @__PURE__ */ jsxs5(Fragment3, { children: [
474
- error ? /* @__PURE__ */ jsx5("p", { role: "alert", children: "Enter a different recipient's full name and valid email to continue." }) : null,
475
- props.renderNamedSeatEditor({
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({
476
622
  selection: quote.namedSeat ?? (!prefillConsumed ? props.initialNamedSeat : void 0) ?? null,
477
623
  offer: { ...quote.seatOffer, currency: quote.currency, interval: quote.interval },
478
624
  save: changeSeat,
@@ -483,8 +629,8 @@ function MembershipPaymentStep(props) {
483
629
  }
484
630
  })
485
631
  ] });
486
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
487
- props.renderNamedSeatEditor ? /* @__PURE__ */ jsx5(
632
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
633
+ renderEditor ? /* @__PURE__ */ jsx8(
488
634
  CheckoutSeatReview,
489
635
  {
490
636
  quote,
@@ -492,7 +638,7 @@ function MembershipPaymentStep(props) {
492
638
  locked,
493
639
  onEdit: () => setEditing(true)
494
640
  }
495
- ) : quote.seatOffer ? /* @__PURE__ */ jsx5(
641
+ ) : quote.seatOffer ? /* @__PURE__ */ jsx8(
496
642
  CheckoutSeatChoice,
497
643
  {
498
644
  quote,
@@ -503,52 +649,52 @@ function MembershipPaymentStep(props) {
503
649
  },
504
650
  quote.digest
505
651
  ) : null,
506
- /* @__PURE__ */ jsxs5("section", { className: "payment-quote", "aria-label": "Membership price", children: [
507
- /* @__PURE__ */ jsx5("h3", { children: quote.tierName }),
508
- quote.founding ? /* @__PURE__ */ jsx5("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,
509
655
  props.renderPriceLines ? props.renderPriceLines({
510
656
  ...quote.lineItems,
511
657
  ...quote.founding ? { founding: quote.founding } : {},
512
658
  ...pickLater ? { namedSeatPickLater: true } : {},
513
659
  ...quote.presentation ? { presentation: quote.presentation } : {}
514
- }) : /* @__PURE__ */ jsxs5("dl", { children: [
515
- quote.lineItems.discountCents > 0 && !quote.founding && quote.presentation ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
516
- /* @__PURE__ */ jsx5("dt", { children: copy.standardRate }),
517
- /* @__PURE__ */ jsxs5("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: [
518
664
  amount(quote.lineItems.standardCents),
519
665
  " per ",
520
666
  quote.interval
521
667
  ] }),
522
- /* @__PURE__ */ jsx5("dt", { children: copy.yourRate }),
523
- /* @__PURE__ */ jsxs5("dd", { children: [
668
+ /* @__PURE__ */ jsx8("dt", { children: copy.yourRate }),
669
+ /* @__PURE__ */ jsxs7("dd", { children: [
524
670
  amount(quote.lineItems.dueTodayCents - (quote.lineItems.namedSeatCents ?? 0)),
525
671
  " per ",
526
672
  quote.interval
527
673
  ] })
528
- ] }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
529
- /* @__PURE__ */ jsx5("dt", { children: "Membership price" }),
530
- /* @__PURE__ */ jsxs5("dd", { children: [
674
+ ] }) : /* @__PURE__ */ jsxs7(Fragment2, { children: [
675
+ /* @__PURE__ */ jsx8("dt", { children: "Membership price" }),
676
+ /* @__PURE__ */ jsxs7("dd", { children: [
531
677
  amount(quote.lineItems.standardCents),
532
678
  " per ",
533
679
  quote.interval
534
680
  ] }),
535
- quote.lineItems.discountCents > 0 ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
536
- /* @__PURE__ */ jsx5("dt", { children: quote.founding ? copy.foundingDiscount : "Discount" }),
537
- /* @__PURE__ */ jsx5("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) })
538
684
  ] }) : null
539
685
  ] }),
540
- quote.namedSeat ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
541
- /* @__PURE__ */ jsx5("dt", { children: "Additional member" }),
542
- /* @__PURE__ */ jsx5("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) })
543
689
  ] }) : null,
544
- pickLater ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
545
- /* @__PURE__ */ jsx5("dt", { children: "Additional member" }),
546
- /* @__PURE__ */ jsx5("dd", { children: "Pick later" })
690
+ pickLater ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
691
+ /* @__PURE__ */ jsx8("dt", { children: "Additional member" }),
692
+ /* @__PURE__ */ jsx8("dd", { children: "Pick later" })
547
693
  ] }) : null,
548
- /* @__PURE__ */ jsx5("dt", { children: "Due today" }),
549
- /* @__PURE__ */ jsx5("dd", { children: amount(quote.lineItems.dueTodayCents) })
694
+ /* @__PURE__ */ jsx8("dt", { children: "Due today" }),
695
+ /* @__PURE__ */ jsx8("dd", { children: amount(quote.lineItems.dueTodayCents) })
550
696
  ] }),
551
- quote.namedSeat && !props.renderNamedSeatEditor ? /* @__PURE__ */ jsxs5("p", { children: [
697
+ quote.namedSeat && !renderEditor ? /* @__PURE__ */ jsxs7("p", { children: [
552
698
  "For ",
553
699
  quote.namedSeat.recipientName,
554
700
  " \xB7 ",
@@ -556,8 +702,8 @@ function MembershipPaymentStep(props) {
556
702
  ".",
557
703
  props.renderPriceLines ? ` Additional member: ${quote.lineItems.namedSeatCents === 0 ? "included" : amount(quote.lineItems.namedSeatCents)} in the total above.` : ""
558
704
  ] }) : null,
559
- quote.commercial ? /* @__PURE__ */ jsxs5("div", { role: "note", "aria-label": "Renewal terms", children: [
560
- quote.commercial.initialTermEndsAt !== void 0 ? /* @__PURE__ */ jsxs5("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: [
561
707
  "First renewal: ",
562
708
  new Intl.DateTimeFormat("en-US", {
563
709
  dateStyle: "long",
@@ -565,23 +711,23 @@ function MembershipPaymentStep(props) {
565
711
  }).format(quote.commercial.initialTermEndsAt),
566
712
  "."
567
713
  ] }) : null,
568
- /* @__PURE__ */ jsxs5("p", { children: [
714
+ /* @__PURE__ */ jsxs7("p", { children: [
569
715
  "Renews automatically at ",
570
716
  amount(quote.commercial.renewalCents),
571
717
  " per ",
572
718
  quote.interval,
573
719
  " unless canceled before renewal."
574
720
  ] }),
575
- quote.commercial.discountDuration === "first_term" ? /* @__PURE__ */ jsx5("p", { children: "The discount applies to your first paid term only." }) : null,
576
- quote.commercial.discountDuration === "while_active" ? /* @__PURE__ */ jsx5("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
577
723
  ] }) : null,
578
- quote.trustCopy ? /* @__PURE__ */ jsx5("p", { className: "pay-trust", children: quote.trustCopy }) : null
724
+ quote.trustCopy ? /* @__PURE__ */ jsx8("p", { className: "pay-trust", children: quote.trustCopy }) : null
579
725
  ] }),
580
- quote.namedSeat && props.renderNamedSeatEditor ? /* @__PURE__ */ jsxs5("details", { className: "checkout-seat-terms", children: [
581
- /* @__PURE__ */ jsx5("summary", { children: "Additional-member terms" }),
582
- /* @__PURE__ */ jsx5("p", { children: "For one named person, who completes their own application and any required interview. Non-transferable; renews and ends with your membership." })
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." })
583
729
  ] }) : null,
584
- !editing ? /* @__PURE__ */ jsx5(
730
+ !editing ? /* @__PURE__ */ jsx8(
585
731
  PaymentStep,
586
732
  {
587
733
  ...props,
@@ -592,12 +738,12 @@ function MembershipPaymentStep(props) {
592
738
  startCheckout
593
739
  },
594
740
  quote.digest
595
- ) : /* @__PURE__ */ jsx5("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." })
596
742
  ] });
597
743
  }
598
744
 
599
745
  // src/ui/journey-frame.tsx
600
- import { jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
746
+ import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
601
747
  var styles = `
602
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}
603
749
  .chapter-journey *{box-sizing:border-box}
@@ -619,6 +765,14 @@ var styles = `
619
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)}
620
766
  .chapter-journey :is(.slot-time,.slot-day)[aria-pressed=true]{background:var(--ui-accent,#285c50);color:var(--ui-on-accent,#fff)}
621
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}}
622
776
  .chapter-journey :where(.compliance-box,.journey-note){padding:1.2rem;background:var(--ui-surface-2,#f3f5f2);border-radius:10px;margin-block:1.2rem}
623
777
  .chapter-journey :where(.compliance-box p:last-child,.journey-note p:last-child,.card p:last-child){margin-bottom:0}
624
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}
@@ -634,21 +788,21 @@ var styles = `
634
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}}
635
789
  `;
636
790
  function JourneyFrame({ children }) {
637
- return /* @__PURE__ */ jsxs6("div", { className: "chapter-journey", children: [
638
- /* @__PURE__ */ jsx6("style", { children: styles }),
791
+ return /* @__PURE__ */ jsxs8("div", { className: "chapter-journey", children: [
792
+ /* @__PURE__ */ jsx9("style", { children: styles }),
639
793
  children
640
794
  ] });
641
795
  }
642
796
 
643
797
  // src/ui/member-restart.tsx
644
- import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
798
+ import { jsx as jsx10, jsxs as jsxs9 } from "preact/jsx-runtime";
645
799
  function MemberRestart(props) {
646
- const [id, setId] = useState5(props.applicationId);
647
- const [busy, setBusy] = useState5(false);
648
- const [error, setError] = useState5("");
649
- const [paid, setPaid] = useState5(false);
650
- const [checking, setChecking] = useState5(false);
651
- 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);
652
806
  useEffect2(() => {
653
807
  if (!paid) return;
654
808
  let live = true, timer, attempt = 0;
@@ -686,40 +840,40 @@ function MemberRestart(props) {
686
840
  setBusy(false);
687
841
  }
688
842
  };
689
- return /* @__PURE__ */ jsx7(JourneyFrame, { children: /* @__PURE__ */ jsxs7("section", { className: "card", "aria-label": "Restart membership", children: [
690
- /* @__PURE__ */ jsx7("p", { className: "card-label", children: "Welcome back" }),
691
- /* @__PURE__ */ jsx7("h2", { children: "Restart your membership" }),
692
- /* @__PURE__ */ jsx7("p", { children: "Your account is ready, and your previous approval still counts. No new application or interview is needed." }),
693
- /* @__PURE__ */ jsxs7("dl", { className: "journey-summary", children: [
694
- /* @__PURE__ */ jsx7("dt", { children: "Name" }),
695
- /* @__PURE__ */ jsx7("dd", { children: [props.application.firstName, props.application.lastName].filter(Boolean).join(" ") }),
696
- /* @__PURE__ */ jsx7("dt", { children: "Email" }),
697
- /* @__PURE__ */ jsx7("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 })
698
852
  ] }),
699
- paid ? /* @__PURE__ */ jsxs7("div", { role: "status", children: [
700
- /* @__PURE__ */ jsx7("h3", { children: "Confirming your membership" }),
701
- /* @__PURE__ */ jsx7("p", { children: "We\u2019re waiting for payment confirmation. Your membership will open here as soon as it arrives." }),
702
- !checking ? /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Check membership" }) : null
703
- ] }) : id ? /* @__PURE__ */ jsx7(MembershipPaymentStep, { applicationId: id, refundPolicyText: "", onPaid: () => setPaid(true) }) : /* @__PURE__ */ jsx7("button", { type: "button", disabled: busy, onClick: () => void start(), children: busy ? "Preparing your membership\u2026" : "Review membership and payment" }),
704
- error ? /* @__PURE__ */ jsx7("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
705
859
  ] }) });
706
860
  }
707
861
 
708
862
  // src/ui/members.tsx
709
- import { useEffect as useEffect6, useState as useState10 } from "preact/hooks";
863
+ import { useEffect as useEffect6, useState as useState12 } from "preact/hooks";
710
864
 
711
865
  // src/ui/reschedule.tsx
712
- import { useState as useState6 } from "preact/hooks";
713
- import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
866
+ import { useState as useState8 } from "preact/hooks";
867
+ import { jsx as jsx11, jsxs as jsxs10 } from "preact/jsx-runtime";
714
868
  function Rescheduler(props) {
715
869
  const inheritedCopy = useChapterCopy();
716
870
  const copy = props.copy ?? inheritedCopy.members.reschedule;
717
871
  const { api, applicationId, timezone, onRescheduled, label = copy.changeTime } = props;
718
- const [open, setOpen] = useState6(false);
719
- const [slots, setSlots] = useState6(null);
720
- const [tz, setTz] = useState6(timezone);
721
- const [busy, setBusy] = useState6(false);
722
- 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);
723
877
  const start = async () => {
724
878
  setOpen(true);
725
879
  setSlots(null);
@@ -752,7 +906,7 @@ function Rescheduler(props) {
752
906
  }
753
907
  };
754
908
  if (!open) {
755
- return /* @__PURE__ */ jsx8("p", { className: "meeting-note", children: /* @__PURE__ */ jsx8(
909
+ return /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: /* @__PURE__ */ jsx11(
756
910
  "a",
757
911
  {
758
912
  href: "#",
@@ -764,8 +918,8 @@ function Rescheduler(props) {
764
918
  }
765
919
  ) });
766
920
  }
767
- return /* @__PURE__ */ jsxs8("div", { className: "msched", children: [
768
- slots === null ? /* @__PURE__ */ jsx8("p", { className: "meeting-note", children: copy.loading }) : slots.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "meeting-note", children: msg ?? copy.noTimes }) : /* @__PURE__ */ jsx8(
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(
769
923
  SlotPicker,
770
924
  {
771
925
  slots,
@@ -774,9 +928,9 @@ function Rescheduler(props) {
774
928
  onPick: (s) => void pick(s)
775
929
  }
776
930
  ),
777
- busy ? /* @__PURE__ */ jsx8("p", { className: "meeting-note", children: copy.rescheduling }) : null,
778
- msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx8("p", { className: "meeting-note error", children: msg }) : null,
779
- /* @__PURE__ */ jsx8("p", { className: "meeting-note", children: /* @__PURE__ */ jsx8(
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(
780
934
  "a",
781
935
  {
782
936
  href: "#",
@@ -791,32 +945,32 @@ function Rescheduler(props) {
791
945
  }
792
946
 
793
947
  // src/ui/member-gifts.tsx
794
- 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";
795
949
 
796
950
  // src/ui/gift-offer-cards.tsx
797
- import { jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
951
+ import { jsx as jsx12, jsxs as jsxs11 } from "preact/jsx-runtime";
798
952
  var price = (cents, currency) => currency === "usd" ? fmtMoney(cents) : new Intl.NumberFormat(void 0, { style: "currency", currency: currency.toUpperCase() }).format(cents / 100);
799
953
  function GiftOfferCards(props) {
800
954
  const mode = props.mode ?? "preview";
801
- return /* @__PURE__ */ jsx9("section", { className: "chapter-gift-offers", "aria-label": props.ariaLabel ?? "Gift options", children: props.offers.map((offer) => /* @__PURE__ */ jsxs9("article", { className: "card chapter-gift-offer", "data-offer-id": offer.id, children: [
802
- /* @__PURE__ */ jsx9("div", { className: "card-label", children: offer.title }),
803
- /* @__PURE__ */ jsx9("p", { children: offer.description }),
804
- /* @__PURE__ */ jsxs9("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: [
805
959
  price(offer.priceCents, offer.currency),
806
960
  offer.billingMode === "subscription" ? " recurring" : ""
807
961
  ] }),
808
- mode === "select" ? /* @__PURE__ */ jsx9("button", { type: "button", className: "btn secondary", "aria-pressed": props.selectedOfferId === offer.id, onClick: () => props.onSelect?.(offer.id), children: offer.ctaLabel }) : /* @__PURE__ */ jsx9("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 })
809
963
  ] }, offer.id)) });
810
964
  }
811
965
 
812
966
  // src/ui/member-gifts.tsx
813
- import { jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
967
+ import { jsx as jsx13, jsxs as jsxs12 } from "preact/jsx-runtime";
814
968
  function GiftPurchases(props) {
815
- const [data, setData] = useState7(null);
816
- const [selected, setSelected] = useState7(null);
817
- const [code, setCode] = useState7("");
818
- const [message, setMessage] = useState7("");
819
- 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());
820
974
  const load = async () => setData(await props.api("/api/gifts"));
821
975
  useEffect3(() => {
822
976
  void load().catch(() => setData({ offers: [], claims: [] }));
@@ -833,16 +987,16 @@ function GiftPurchases(props) {
833
987
  if (!data) return null;
834
988
  if (data.offers.length === 0 && data.claims.length === 0) return null;
835
989
  const offer = data.offers.find((item) => item.id === selected);
836
- return /* @__PURE__ */ jsxs10("section", { className: "member-gifts", children: [
837
- data.offers.length ? /* @__PURE__ */ jsxs10("div", { className: "card", children: [
838
- /* @__PURE__ */ jsx10("div", { className: "card-label", children: props.copy.title }),
839
- /* @__PURE__ */ jsx10("p", { children: props.copy.body }),
840
- /* @__PURE__ */ jsx10(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) => {
841
995
  setSelected(id);
842
996
  setCode("");
843
997
  purchaseId.current = crypto.randomUUID();
844
998
  } }),
845
- offer ? /* @__PURE__ */ jsx10(
999
+ offer ? /* @__PURE__ */ jsx13(
846
1000
  PaymentStep,
847
1001
  {
848
1002
  applicationId: purchaseId.current,
@@ -862,27 +1016,27 @@ function GiftPurchases(props) {
862
1016
  }
863
1017
  }
864
1018
  ) : null,
865
- code ? /* @__PURE__ */ jsxs10("div", { className: "gift-code-once", children: [
866
- /* @__PURE__ */ jsx10("output", { children: code }),
867
- /* @__PURE__ */ jsx10("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 })
868
1022
  ] }) : null
869
1023
  ] }) : null,
870
- /* @__PURE__ */ jsxs10("div", { className: "card", children: [
871
- /* @__PURE__ */ jsx10("div", { className: "card-label", children: props.copy.purchases }),
872
- data.claims.length ? data.claims.map((claim) => /* @__PURE__ */ jsxs10("div", { className: "member-row", children: [
873
- /* @__PURE__ */ jsx10("span", { children: claim.offerId }),
874
- /* @__PURE__ */ jsx10("span", { children: props.copy[claim.status] }),
875
- claim.status === "pending" ? /* @__PURE__ */ jsx10("button", { className: "btn secondary mini", onClick: () => void rotate(claim.id), children: props.copy.rotate }) : null
876
- ] }, claim.id)) : /* @__PURE__ */ jsx10("p", { children: props.copy.noPurchases }),
877
- message ? /* @__PURE__ */ jsx10("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
878
1032
  ] })
879
1033
  ] });
880
1034
  }
881
1035
  var MemberGifts = GiftPurchases;
882
1036
 
883
1037
  // src/ui/member-named-seat.tsx
884
- import { useEffect as useEffect4, useState as useState8 } from "preact/hooks";
885
- import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs11 } 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";
886
1040
  var NAMED_SEAT_RECIPIENT_WORDS = {
887
1041
  payment_pending: "payment in progress",
888
1042
  invited: "invited, waiting for them to accept",
@@ -894,8 +1048,8 @@ var NAMED_SEAT_RECIPIENT_WORDS = {
894
1048
  };
895
1049
  var day = (at) => new Date(at).toLocaleDateString("en-US", { timeZone: "UTC" });
896
1050
  function NamedSeatRecipientLine({ seat }) {
897
- const words = NAMED_SEAT_RECIPIENT_WORDS[seat.recipientState] ?? seat.status.replaceAll("_", " ");
898
- return /* @__PURE__ */ jsxs11("p", { className: "member-named-seat-recipient", children: [
1051
+ const words = seat.invitationHeld ? "named; the invitation is sent once your membership is approved" : NAMED_SEAT_RECIPIENT_WORDS[seat.recipientState] ?? seat.status.replaceAll("_", " ");
1052
+ return /* @__PURE__ */ jsxs13("p", { className: "member-named-seat-recipient", children: [
899
1053
  seat.recipientName,
900
1054
  " \xB7 ",
901
1055
  seat.recipientEmail,
@@ -905,9 +1059,10 @@ function NamedSeatRecipientLine({ seat }) {
905
1059
  ] });
906
1060
  }
907
1061
  function NamedSeatPurchase({ api }) {
908
- const [data, setData] = useState8(null), [error, setError] = useState8("");
909
- const [name, setName] = useState8(""), [email, setEmail] = useState8(""), [reviewing, setReviewing] = useState8(false);
910
- 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);
1065
+ const [renaming, setRenaming] = useState10(false), [nextName, setNextName] = useState10(""), [nextEmail, setNextEmail] = useState10("");
911
1066
  const load = async () => {
912
1067
  try {
913
1068
  const value = await api("/api/named-seat");
@@ -944,13 +1099,56 @@ function NamedSeatPurchase({ api }) {
944
1099
  }
945
1100
  };
946
1101
  const money = data ? new Intl.NumberFormat("en-US", { style: "currency", currency: data.currency }).format(data.amountCents / 100) : "";
947
- return /* @__PURE__ */ jsx11(JourneyFrame, { children: /* @__PURE__ */ jsxs11("section", { className: "card member-named-seat", "aria-label": "Named additional seat", children: [
948
- /* @__PURE__ */ jsx11("h2", { children: "Named additional seat" }),
949
- error ? /* @__PURE__ */ jsx11("p", { role: "alert", children: error }) : null,
950
- data ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
951
- data.seat ? /* @__PURE__ */ jsx11(NamedSeatRecipientLine, { seat: data.seat }) : null,
952
- data.eligible ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
953
- /* @__PURE__ */ jsxs11("p", { children: [
1102
+ const regift = async (event) => {
1103
+ event.preventDefault();
1104
+ setGranting(true);
1105
+ setError("");
1106
+ try {
1107
+ await api("/api/named-seat/regift", {
1108
+ method: "POST",
1109
+ headers: { "content-type": "application/json" },
1110
+ body: JSON.stringify({ recipientName: nextName, recipientEmail: nextEmail })
1111
+ });
1112
+ setRenaming(false);
1113
+ await load();
1114
+ } catch {
1115
+ setError("The seat could not be given to someone else. Refresh to check its status before trying again.");
1116
+ } finally {
1117
+ setGranting(false);
1118
+ }
1119
+ };
1120
+ return /* @__PURE__ */ jsx14(JourneyFrame, { children: /* @__PURE__ */ jsxs13("section", { className: "card member-named-seat", "aria-label": "Named additional seat", children: [
1121
+ /* @__PURE__ */ jsx14("h2", { children: "Named additional seat" }),
1122
+ error ? /* @__PURE__ */ jsx14("p", { role: "alert", children: error }) : null,
1123
+ data ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
1124
+ data.seat ? /* @__PURE__ */ jsx14(NamedSeatRecipientLine, { seat: data.seat }) : null,
1125
+ data.seat?.regiftable && !renaming ? /* @__PURE__ */ jsx14("button", { className: "btn secondary", type: "button", onClick: () => {
1126
+ setRenaming(true);
1127
+ setNextName("");
1128
+ setNextEmail("");
1129
+ }, children: "Give the seat to someone else" }) : null,
1130
+ data.seat?.regiftable && renaming ? /* @__PURE__ */ jsxs13("form", { className: "member-named-seat-regift", "aria-label": "Give the seat to someone else", onSubmit: (event) => void regift(event), children: [
1131
+ /* @__PURE__ */ jsxs13("p", { children: [
1132
+ "The seat moves to the person you name here. ",
1133
+ data.seat.recipientName,
1134
+ "'s invitation stops working, and nothing is charged or refunded."
1135
+ ] }),
1136
+ /* @__PURE__ */ jsxs13("label", { children: [
1137
+ "New recipient name",
1138
+ /* @__PURE__ */ jsx14("input", { required: true, maxLength: 160, value: nextName, onInput: (event) => setNextName(event.currentTarget.value) })
1139
+ ] }),
1140
+ /* @__PURE__ */ jsxs13("label", { children: [
1141
+ "New recipient email",
1142
+ /* @__PURE__ */ jsx14("input", { required: true, type: "email", maxLength: 254, value: nextEmail, onInput: (event) => setNextEmail(event.currentTarget.value) })
1143
+ ] }),
1144
+ /* @__PURE__ */ jsx14("button", { className: "btn primary", type: "submit", disabled: granting, children: granting ? "Changing the recipient\u2026" : "Give the seat to this person" }),
1145
+ /* @__PURE__ */ jsxs13("button", { className: "btn secondary", type: "button", disabled: granting, onClick: () => setRenaming(false), children: [
1146
+ "Keep ",
1147
+ data.seat.recipientName
1148
+ ] })
1149
+ ] }) : null,
1150
+ data.eligible ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
1151
+ /* @__PURE__ */ jsxs13("p", { children: [
954
1152
  data.included ? "Included with your membership at no charge." : `${money} paid in full now.`,
955
1153
  " This seat ",
956
1154
  data.autoRenew ? "renews with your primary membership" : "ends with your primary membership",
@@ -961,39 +1159,39 @@ function NamedSeatPurchase({ api }) {
961
1159
  " ",
962
1160
  "It belongs to one named person and cannot be reassigned."
963
1161
  ] }),
964
- /* @__PURE__ */ jsx11("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." }),
965
- !reviewing ? /* @__PURE__ */ jsxs11("form", { onSubmit: (event) => {
1162
+ /* @__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." }),
1163
+ !reviewing ? /* @__PURE__ */ jsxs13("form", { onSubmit: (event) => {
966
1164
  event.preventDefault();
967
1165
  setReviewing(true);
968
1166
  setTerms(false);
969
1167
  }, children: [
970
- /* @__PURE__ */ jsxs11("label", { children: [
1168
+ /* @__PURE__ */ jsxs13("label", { children: [
971
1169
  "Recipient name",
972
- /* @__PURE__ */ jsx11("input", { required: true, maxLength: 160, value: name, disabled: Boolean(data.seat), onInput: (event) => setName(event.currentTarget.value) })
1170
+ /* @__PURE__ */ jsx14("input", { required: true, maxLength: 160, value: name, disabled: Boolean(data.seat), onInput: (event) => setName(event.currentTarget.value) })
973
1171
  ] }),
974
- /* @__PURE__ */ jsxs11("label", { children: [
1172
+ /* @__PURE__ */ jsxs13("label", { children: [
975
1173
  "Recipient email",
976
- /* @__PURE__ */ jsx11("input", { required: true, type: "email", maxLength: 254, value: email, disabled: Boolean(data.seat), onInput: (event) => setEmail(event.currentTarget.value) })
1174
+ /* @__PURE__ */ jsx14("input", { required: true, type: "email", maxLength: 254, value: email, disabled: Boolean(data.seat), onInput: (event) => setEmail(event.currentTarget.value) })
977
1175
  ] }),
978
- /* @__PURE__ */ jsx11("button", { className: "btn primary", type: "submit", children: "Review named seat" })
979
- ] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
980
- /* @__PURE__ */ jsxs11("p", { children: [
1176
+ /* @__PURE__ */ jsx14("button", { className: "btn primary", type: "submit", children: "Review named seat" })
1177
+ ] }) : /* @__PURE__ */ jsxs13(Fragment3, { children: [
1178
+ /* @__PURE__ */ jsxs13("p", { children: [
981
1179
  "For ",
982
1180
  name,
983
1181
  " \xB7 ",
984
1182
  email
985
1183
  ] }),
986
- /* @__PURE__ */ jsx11("button", { className: "btn secondary", type: "button", onClick: () => {
1184
+ /* @__PURE__ */ jsx14("button", { className: "btn secondary", type: "button", onClick: () => {
987
1185
  setReviewing(false);
988
1186
  setTerms(false);
989
1187
  void load();
990
1188
  }, children: "Back to recipient" }),
991
- /* @__PURE__ */ jsxs11("label", { className: "journey-consent", children: [
992
- /* @__PURE__ */ jsx11("input", { type: "checkbox", checked: terms, onChange: (event) => setTerms(event.currentTarget.checked) }),
993
- /* @__PURE__ */ jsx11("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." })
1189
+ /* @__PURE__ */ jsxs13("label", { className: "journey-consent", children: [
1190
+ /* @__PURE__ */ jsx14("input", { type: "checkbox", checked: terms, onChange: (event) => setTerms(event.currentTarget.checked) }),
1191
+ /* @__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." })
994
1192
  ] }),
995
- terms && data.included ? /* @__PURE__ */ jsx11("button", { className: "btn primary", type: "button", disabled: granting, onClick: () => void grant(), children: granting ? "Sending the invitation\u2026" : "Send the invitation" }) : null,
996
- terms && !data.included ? /* @__PURE__ */ jsx11(
1193
+ 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,
1194
+ terms && !data.included ? /* @__PURE__ */ jsx14(
997
1195
  PaymentStep,
998
1196
  {
999
1197
  applicationId: data.membershipId,
@@ -1008,17 +1206,17 @@ function NamedSeatPurchase({ api }) {
1008
1206
  }
1009
1207
  ) : null
1010
1208
  ] })
1011
- ] }) : !data.seat ? /* @__PURE__ */ jsx11("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,
1012
- pending ? /* @__PURE__ */ jsx11("p", { role: "status", children: "Payment confirmation is being reconciled. The recipient\u2019s access begins only after their own acceptance and approval." }) : null
1013
- ] }) : !error ? /* @__PURE__ */ jsx11("p", { children: "Loading your seat terms\u2026" }) : null,
1014
- /* @__PURE__ */ jsx11("button", { className: "btn secondary", onClick: () => void load(), children: "Refresh seat status" })
1209
+ ] }) : !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,
1210
+ 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
1211
+ ] }) : !error ? /* @__PURE__ */ jsx14("p", { children: "Loading your seat terms\u2026" }) : null,
1212
+ /* @__PURE__ */ jsx14("button", { className: "btn secondary", onClick: () => void load(), children: "Refresh seat status" })
1015
1213
  ] }) });
1016
1214
  }
1017
1215
 
1018
1216
  // src/ui/member-content.tsx
1019
- import { useEffect as useEffect5, useMemo as useMemo2, useState as useState9 } from "preact/hooks";
1217
+ import { useEffect as useEffect5, useMemo as useMemo2, useState as useState11 } from "preact/hooks";
1020
1218
  import { Markdown } from "@odla-ai/ui/components";
1021
- import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
1219
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs14 } from "preact/jsx-runtime";
1022
1220
  var pageLabel = (page) => `${page.title}${page.access === "locked" ? " \xB7 Locked" : ""}`;
1023
1221
  function MemberContentArea(props) {
1024
1222
  const {
@@ -1026,12 +1224,12 @@ function MemberContentArea(props) {
1026
1224
  path = "/api/member-content",
1027
1225
  upsellHref,
1028
1226
  onUpsell,
1029
- loadingContent = /* @__PURE__ */ jsx12("p", { className: "meeting-note", children: "Loading member pages\u2026" }),
1030
- emptyContent = /* @__PURE__ */ jsx12("p", { className: "meeting-note", children: "No member pages have been published yet." })
1227
+ loadingContent = /* @__PURE__ */ jsx15("p", { className: "meeting-note", children: "Loading member pages\u2026" }),
1228
+ emptyContent = /* @__PURE__ */ jsx15("p", { className: "meeting-note", children: "No member pages have been published yet." })
1031
1229
  } = props;
1032
- const [data, setData] = useState9(props.initialData ?? null);
1033
- const [error, setError] = useState9(null);
1034
- const [selectedId, setSelectedId] = useState9(
1230
+ const [data, setData] = useState11(props.initialData ?? null);
1231
+ const [error, setError] = useState11(null);
1232
+ const [selectedId, setSelectedId] = useState11(
1035
1233
  props.initialPageId ?? props.initialData?.pages[0]?.id ?? ""
1036
1234
  );
1037
1235
  useEffect5(() => {
@@ -1056,18 +1254,18 @@ function MemberContentArea(props) {
1056
1254
  }
1057
1255
  return [...result.entries()];
1058
1256
  }, [data]);
1059
- if (error) return /* @__PURE__ */ jsxs12("p", { className: "meeting-note", role: "alert", children: [
1257
+ if (error) return /* @__PURE__ */ jsxs14("p", { className: "meeting-note", role: "alert", children: [
1060
1258
  "Member pages could not be loaded: ",
1061
1259
  error
1062
1260
  ] });
1063
- if (!data) return /* @__PURE__ */ jsx12(Fragment5, { children: loadingContent });
1064
- if (data.pages.length === 0) return /* @__PURE__ */ jsx12(Fragment5, { children: emptyContent });
1261
+ if (!data) return /* @__PURE__ */ jsx15(Fragment4, { children: loadingContent });
1262
+ if (data.pages.length === 0) return /* @__PURE__ */ jsx15(Fragment4, { children: emptyContent });
1065
1263
  const selected = data.pages.find((page) => page.id === selectedId) ?? data.pages[0];
1066
1264
  const href = selected.upsell && upsellHref ? upsellHref(selected.upsell.tierId, selected) : void 0;
1067
- return /* @__PURE__ */ jsxs12("div", { className: "chapter-member-content", "data-member-content": true, children: [
1068
- /* @__PURE__ */ jsx12("nav", { className: "chapter-member-content-nav", "aria-label": "Member pages", children: sections.map(([section, pages]) => /* @__PURE__ */ jsxs12("section", { className: "chapter-member-content-section", children: [
1069
- /* @__PURE__ */ jsx12("h2", { children: section }),
1070
- /* @__PURE__ */ jsx12("ul", { children: pages.map((page) => /* @__PURE__ */ jsx12("li", { "data-parent-page": page.navigation.parentId ?? void 0, children: /* @__PURE__ */ jsx12(
1265
+ return /* @__PURE__ */ jsxs14("div", { className: "chapter-member-content", "data-member-content": true, children: [
1266
+ /* @__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: [
1267
+ /* @__PURE__ */ jsx15("h2", { children: section }),
1268
+ /* @__PURE__ */ jsx15("ul", { children: pages.map((page) => /* @__PURE__ */ jsx15("li", { "data-parent-page": page.navigation.parentId ?? void 0, children: /* @__PURE__ */ jsx15(
1071
1269
  "button",
1072
1270
  {
1073
1271
  type: "button",
@@ -1077,23 +1275,23 @@ function MemberContentArea(props) {
1077
1275
  }
1078
1276
  ) }, page.id)) })
1079
1277
  ] }, section)) }),
1080
- /* @__PURE__ */ jsxs12("article", { className: "chapter-member-content-page", "data-access": selected.access, children: [
1081
- /* @__PURE__ */ jsxs12("header", { children: [
1082
- /* @__PURE__ */ jsxs12("div", { className: "card-label", children: [
1278
+ /* @__PURE__ */ jsxs14("article", { className: "chapter-member-content-page", "data-access": selected.access, children: [
1279
+ /* @__PURE__ */ jsxs14("header", { children: [
1280
+ /* @__PURE__ */ jsxs14("div", { className: "card-label", children: [
1083
1281
  selected.source.type === "parent" ? "Parent" : "Chapter",
1084
1282
  " \xB7 ",
1085
1283
  selected.access
1086
1284
  ] }),
1087
- /* @__PURE__ */ jsx12("h1", { children: selected.title }),
1088
- selected.summary ? /* @__PURE__ */ jsx12("p", { children: selected.summary }) : null
1285
+ /* @__PURE__ */ jsx15("h1", { children: selected.title }),
1286
+ selected.summary ? /* @__PURE__ */ jsx15("p", { children: selected.summary }) : null
1089
1287
  ] }),
1090
- selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx12(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs12("div", { className: "chapter-member-content-locked", children: [
1091
- selected.teaserMarkdown ? /* @__PURE__ */ jsx12(Markdown, { source: selected.teaserMarkdown }) : /* @__PURE__ */ jsx12("p", { children: "This page is not included in your current membership." }),
1092
- selected.upsell ? /* @__PURE__ */ jsxs12("aside", { className: "chapter-member-content-upsell", "aria-label": selected.upsell.label ?? "Membership option", children: [
1093
- /* @__PURE__ */ jsx12("h2", { children: selected.upsell.label ?? selected.upsell.tierName }),
1094
- /* @__PURE__ */ jsx12("p", { children: fmtMoney(selected.upsell.priceCents) }),
1095
- selected.upsell.bodyMarkdown ? /* @__PURE__ */ jsx12(Markdown, { source: selected.upsell.bodyMarkdown }) : null,
1096
- href ? /* @__PURE__ */ jsx12("a", { className: "btn primary", href, children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : onUpsell ? /* @__PURE__ */ jsx12("button", { className: "btn primary", type: "button", onClick: () => onUpsell(selected.upsell.tierId, selected), children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : null
1288
+ selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx15(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs14("div", { className: "chapter-member-content-locked", children: [
1289
+ selected.teaserMarkdown ? /* @__PURE__ */ jsx15(Markdown, { source: selected.teaserMarkdown }) : /* @__PURE__ */ jsx15("p", { children: "This page is not included in your current membership." }),
1290
+ selected.upsell ? /* @__PURE__ */ jsxs14("aside", { className: "chapter-member-content-upsell", "aria-label": selected.upsell.label ?? "Membership option", children: [
1291
+ /* @__PURE__ */ jsx15("h2", { children: selected.upsell.label ?? selected.upsell.tierName }),
1292
+ /* @__PURE__ */ jsx15("p", { children: fmtMoney(selected.upsell.priceCents) }),
1293
+ selected.upsell.bodyMarkdown ? /* @__PURE__ */ jsx15(Markdown, { source: selected.upsell.bodyMarkdown }) : null,
1294
+ 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
1097
1295
  ] }) : null
1098
1296
  ] })
1099
1297
  ] })
@@ -1101,12 +1299,12 @@ function MemberContentArea(props) {
1101
1299
  }
1102
1300
 
1103
1301
  // src/ui/members.tsx
1104
- import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
1302
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs15 } from "preact/jsx-runtime";
1105
1303
  function card(copy, kicker, body) {
1106
- return /* @__PURE__ */ jsxs13("div", { className: "card", children: [
1107
- /* @__PURE__ */ jsx13("div", { className: "card-label", children: copy.provisional.cardLabel }),
1108
- /* @__PURE__ */ jsxs13("div", { className: "meeting-block", children: [
1109
- /* @__PURE__ */ jsx13("div", { className: "meeting-kicker", children: kicker }),
1304
+ return /* @__PURE__ */ jsxs15("div", { className: "card", children: [
1305
+ /* @__PURE__ */ jsx16("div", { className: "card-label", children: copy.provisional.cardLabel }),
1306
+ /* @__PURE__ */ jsxs15("div", { className: "meeting-block", children: [
1307
+ /* @__PURE__ */ jsx16("div", { className: "meeting-kicker", children: kicker }),
1110
1308
  body
1111
1309
  ] })
1112
1310
  ] });
@@ -1117,9 +1315,9 @@ function ProvisionalCard(props) {
1117
1315
  return card(
1118
1316
  copy,
1119
1317
  copy.provisional.applicationNeeded,
1120
- /* @__PURE__ */ jsxs13(Fragment6, { children: [
1121
- /* @__PURE__ */ jsx13("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
1122
- /* @__PURE__ */ jsx13("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
1318
+ /* @__PURE__ */ jsxs15(Fragment5, { children: [
1319
+ /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
1320
+ /* @__PURE__ */ jsx16("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
1123
1321
  ] })
1124
1322
  );
1125
1323
  }
@@ -1127,19 +1325,19 @@ function ProvisionalCard(props) {
1127
1325
  return card(
1128
1326
  copy,
1129
1327
  copy.provisional.refunded,
1130
- /* @__PURE__ */ jsx13("div", { className: "meeting-note", children: copy.provisional.refundedBody })
1328
+ /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: copy.provisional.refundedBody })
1131
1329
  );
1132
1330
  }
1133
- const membership = application.paid ? /* @__PURE__ */ jsx13("div", { className: "meeting-note", children: application.renewalAt ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) }) : copy.provisional.active }) : null;
1331
+ 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;
1134
1332
  if (application.meetingAt) {
1135
1333
  return card(
1136
1334
  copy,
1137
1335
  copy.provisional.introductionCall,
1138
- /* @__PURE__ */ jsxs13(Fragment6, { children: [
1139
- /* @__PURE__ */ jsx13("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
1140
- /* @__PURE__ */ jsx13("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
1141
- application.meetUrl ? /* @__PURE__ */ jsx13("div", { className: "meeting-note", children: /* @__PURE__ */ jsx13("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
1142
- /* @__PURE__ */ jsx13(
1336
+ /* @__PURE__ */ jsxs15(Fragment5, { children: [
1337
+ /* @__PURE__ */ jsx16("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
1338
+ /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
1339
+ application.meetUrl ? /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: /* @__PURE__ */ jsx16("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
1340
+ /* @__PURE__ */ jsx16(
1143
1341
  Rescheduler,
1144
1342
  {
1145
1343
  api,
@@ -1156,9 +1354,9 @@ function ProvisionalCard(props) {
1156
1354
  return card(
1157
1355
  copy,
1158
1356
  copy.provisional.bookCall,
1159
- /* @__PURE__ */ jsxs13(Fragment6, { children: [
1160
- /* @__PURE__ */ jsx13("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
1161
- /* @__PURE__ */ jsx13(
1357
+ /* @__PURE__ */ jsxs15(Fragment5, { children: [
1358
+ /* @__PURE__ */ jsx16("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
1359
+ /* @__PURE__ */ jsx16(
1162
1360
  Rescheduler,
1163
1361
  {
1164
1362
  api,
@@ -1185,8 +1383,8 @@ function MembersArea(props) {
1185
1383
  renderProvisional
1186
1384
  } = props;
1187
1385
  const copy = props.copy ?? inheritedCopy.members;
1188
- const [me, setMe] = useState10(null);
1189
- const [error, setError] = useState10(false);
1386
+ const [me, setMe] = useState12(null);
1387
+ const [error, setError] = useState12(false);
1190
1388
  const reload = async () => {
1191
1389
  try {
1192
1390
  setMe(await api("/api/me"));
@@ -1197,22 +1395,22 @@ function MembersArea(props) {
1197
1395
  useEffect6(() => {
1198
1396
  void reload();
1199
1397
  }, []);
1200
- if (error) return /* @__PURE__ */ jsx13("p", { className: "meeting-note", children: copy.loadFailed });
1201
- if (!me) return /* @__PURE__ */ jsx13("p", { className: "meeting-note", children: inheritedCopy.common.loading });
1398
+ if (error) return /* @__PURE__ */ jsx16("p", { className: "meeting-note", children: copy.loadFailed });
1399
+ if (!me) return /* @__PURE__ */ jsx16("p", { className: "meeting-note", children: inheritedCopy.common.loading });
1202
1400
  const role = me.role || "provisional";
1203
- return /* @__PURE__ */ jsxs13(Fragment6, { children: [
1204
- /* @__PURE__ */ jsxs13("div", { className: "card", children: [
1205
- /* @__PURE__ */ jsxs13("div", { className: "member-row", children: [
1206
- /* @__PURE__ */ jsx13("span", { className: "member-email", children: me.email }),
1207
- /* @__PURE__ */ jsx13("span", { className: "role-badge " + role, children: role })
1401
+ return /* @__PURE__ */ jsxs15(Fragment5, { children: [
1402
+ /* @__PURE__ */ jsxs15("div", { className: "card", children: [
1403
+ /* @__PURE__ */ jsxs15("div", { className: "member-row", children: [
1404
+ /* @__PURE__ */ jsx16("span", { className: "member-email", children: me.email }),
1405
+ /* @__PURE__ */ jsx16("span", { className: "role-badge " + role, children: role })
1208
1406
  ] }),
1209
- /* @__PURE__ */ jsxs13("div", { className: "account-actions", children: [
1210
- /* @__PURE__ */ jsx13("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
1211
- me.authorized || me.superAdmin ? /* @__PURE__ */ jsx13("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
1407
+ /* @__PURE__ */ jsxs15("div", { className: "account-actions", children: [
1408
+ /* @__PURE__ */ jsx16("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
1409
+ me.authorized || me.superAdmin ? /* @__PURE__ */ jsx16("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
1212
1410
  ] })
1213
1411
  ] }),
1214
- me.memberAccess !== true && me.membershipRestart?.eligible && me.application ? /* @__PURE__ */ jsx13(MemberRestart, { api, application: me.application, applicationId: me.membershipRestart.applicationId, onComplete: reload }) : me.memberAccess !== true ? (() => {
1215
- const defaultContent = /* @__PURE__ */ jsx13(
1412
+ me.memberAccess !== true && me.membershipRestart?.eligible && me.application ? /* @__PURE__ */ jsx16(MemberRestart, { api, application: me.application, applicationId: me.membershipRestart.applicationId, onComplete: reload }) : me.memberAccess !== true ? (() => {
1413
+ const defaultContent = /* @__PURE__ */ jsx16(
1216
1414
  ProvisionalCard,
1217
1415
  {
1218
1416
  api,
@@ -1229,30 +1427,30 @@ function MembersArea(props) {
1229
1427
  reload,
1230
1428
  defaultContent
1231
1429
  }) ?? defaultContent;
1232
- })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx13(
1430
+ })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx16(
1233
1431
  MemberContentArea,
1234
1432
  {
1235
1433
  api,
1236
1434
  ...memberPages === true ? {} : memberPages
1237
1435
  }
1238
- ) : /* @__PURE__ */ jsx13("div", { className: "card", children: /* @__PURE__ */ jsx13("div", { className: "card-label", children: copy.full.welcome }) })),
1239
- /* @__PURE__ */ jsx13(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
1240
- me.namedSeatsEnabled ? /* @__PURE__ */ jsx13(NamedSeatPurchase, { api }) : null
1436
+ ) : /* @__PURE__ */ jsx16("div", { className: "card", children: /* @__PURE__ */ jsx16("div", { className: "card-label", children: copy.full.welcome }) })),
1437
+ /* @__PURE__ */ jsx16(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
1438
+ me.namedSeatsEnabled ? /* @__PURE__ */ jsx16(NamedSeatPurchase, { api }) : null
1241
1439
  ] });
1242
1440
  }
1243
1441
 
1244
1442
  // src/ui/join.tsx
1245
- import { useEffect as useEffect11, useRef as useRef3, useState as useState18 } from "preact/hooks";
1443
+ import { useEffect as useEffect11, useRef as useRef7, useState as useState20 } from "preact/hooks";
1246
1444
 
1247
1445
  // src/ui/join-booking.tsx
1248
- import { useEffect as useEffect7, useState as useState11 } from "preact/hooks";
1249
- import { jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
1446
+ import { useEffect as useEffect7, useState as useState13 } from "preact/hooks";
1447
+ import { jsx as jsx17, jsxs as jsxs16 } from "preact/jsx-runtime";
1250
1448
  function JoinBooking(props) {
1251
1449
  const { applicationId, copy, onBooked, simulation } = props;
1252
- const [state, setState] = useState11(null);
1253
- const [msg, setMsg] = useState11(null);
1254
- const [busy, setBusy] = useState11(false);
1255
- const [selected, setSelected] = useState11(null);
1450
+ const [state, setState] = useState13(null);
1451
+ const [msg, setMsg] = useState13(null);
1452
+ const [busy, setBusy] = useState13(false);
1453
+ const [selected, setSelected] = useState13(null);
1256
1454
  const load = async () => {
1257
1455
  setMsg(null);
1258
1456
  try {
@@ -1309,11 +1507,11 @@ function JoinBooking(props) {
1309
1507
  setBusy(false);
1310
1508
  }
1311
1509
  };
1312
- if (!state) return /* @__PURE__ */ jsx14("p", { className: "slots-status", children: msg ?? copy.loading });
1313
- return /* @__PURE__ */ jsxs14("div", { className: "join-book", children: [
1314
- /* @__PURE__ */ jsx14("h2", { children: copy.title ?? DEFAULT_JOIN_COPY.booking.title }),
1315
- /* @__PURE__ */ jsx14("p", { children: copy.intro ?? DEFAULT_JOIN_COPY.booking.intro }),
1316
- /* @__PURE__ */ jsx14(
1510
+ if (!state) return /* @__PURE__ */ jsx17("p", { className: "slots-status", children: msg ?? copy.loading });
1511
+ return /* @__PURE__ */ jsxs16("div", { className: "join-book", children: [
1512
+ /* @__PURE__ */ jsx17("h2", { children: copy.title ?? DEFAULT_JOIN_COPY.booking.title }),
1513
+ /* @__PURE__ */ jsx17("p", { children: copy.intro ?? DEFAULT_JOIN_COPY.booking.intro }),
1514
+ /* @__PURE__ */ jsx17(
1317
1515
  SlotPicker,
1318
1516
  {
1319
1517
  slots: state.slots,
@@ -1323,29 +1521,29 @@ function JoinBooking(props) {
1323
1521
  onDayChange: () => setSelected(null)
1324
1522
  }
1325
1523
  ),
1326
- /* @__PURE__ */ jsxs14("div", { className: "slot-confirm", hidden: !selected, children: [
1327
- /* @__PURE__ */ jsx14("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
1328
- msg ? /* @__PURE__ */ jsx14("p", { className: "step2-note", children: msg }) : null
1524
+ /* @__PURE__ */ jsxs16("div", { className: "slot-confirm", hidden: !selected, children: [
1525
+ /* @__PURE__ */ jsx17("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
1526
+ msg ? /* @__PURE__ */ jsx17("p", { className: "step2-note", children: msg }) : null
1329
1527
  ] })
1330
1528
  ] });
1331
1529
  }
1332
1530
 
1333
1531
  // src/ui/join-accepted.tsx
1334
- import { jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
1532
+ import { jsx as jsx18, jsxs as jsxs17 } from "preact/jsx-runtime";
1335
1533
  function JoinAccepted(props) {
1336
- return /* @__PURE__ */ jsxs15("div", { className: "join-done card", children: [
1337
- /* @__PURE__ */ jsx15("div", { className: "card-label", children: props.copy.accepted.label }),
1338
- /* @__PURE__ */ jsx15("p", { className: "meeting-note", children: props.copy.accepted.body }),
1339
- /* @__PURE__ */ jsx15("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
1534
+ return /* @__PURE__ */ jsxs17("div", { className: "join-done card", children: [
1535
+ /* @__PURE__ */ jsx18("div", { className: "card-label", children: props.copy.accepted.label }),
1536
+ /* @__PURE__ */ jsx18("p", { className: "meeting-note", children: props.copy.accepted.body }),
1537
+ /* @__PURE__ */ jsx18("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
1340
1538
  ] });
1341
1539
  }
1342
1540
 
1343
1541
  // src/ui/gift-claim-card.tsx
1344
- import { useState as useState12 } from "preact/hooks";
1345
- import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs16 } from "preact/jsx-runtime";
1542
+ import { useState as useState14 } from "preact/hooks";
1543
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs18 } from "preact/jsx-runtime";
1346
1544
  function GiftClaimCard(props) {
1347
- const [code, setCode] = useState12(props.initialCode ?? "");
1348
- const [error, setError] = useState12(false);
1545
+ const [code, setCode] = useState14(props.initialCode ?? "");
1546
+ const [error, setError] = useState14(false);
1349
1547
  const rotate = async () => {
1350
1548
  if (!props.claimId) return;
1351
1549
  setError(false);
@@ -1358,24 +1556,27 @@ function GiftClaimCard(props) {
1358
1556
  if (response.ok && body.code) setCode(body.code);
1359
1557
  else setError(true);
1360
1558
  };
1361
- return /* @__PURE__ */ jsxs16("div", { className: "join-done card", children: [
1362
- /* @__PURE__ */ jsx16("div", { className: "card-label", children: props.copy.label }),
1363
- /* @__PURE__ */ jsx16("p", { className: "meeting-note", children: props.copy.body }),
1364
- code ? /* @__PURE__ */ jsxs16(Fragment7, { children: [
1365
- /* @__PURE__ */ jsx16("output", { className: "gift-code", children: code }),
1366
- /* @__PURE__ */ jsx16("p", { children: props.copy.save })
1559
+ return /* @__PURE__ */ jsxs18("div", { className: "join-done card", children: [
1560
+ /* @__PURE__ */ jsx19("div", { className: "card-label", children: props.copy.label }),
1561
+ /* @__PURE__ */ jsx19("p", { className: "meeting-note", children: props.copy.body }),
1562
+ code ? /* @__PURE__ */ jsxs18(Fragment6, { children: [
1563
+ /* @__PURE__ */ jsx19("output", { className: "gift-code", children: code }),
1564
+ /* @__PURE__ */ jsx19("p", { children: props.copy.save })
1367
1565
  ] }) : null,
1368
- !code && props.claimId ? /* @__PURE__ */ jsx16("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
1369
- error ? /* @__PURE__ */ jsx16("p", { role: "alert", children: props.copy.rotateFailed }) : null
1566
+ !code && props.claimId ? /* @__PURE__ */ jsx19("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
1567
+ error ? /* @__PURE__ */ jsx19("p", { role: "alert", children: props.copy.rotateFailed }) : null
1370
1568
  ] });
1371
1569
  }
1372
1570
 
1571
+ // src/ui/join-application-form.tsx
1572
+ import { useRef as useRef6 } from "preact/hooks";
1573
+
1373
1574
  // src/ui/join-tiers.tsx
1374
- import { useState as useState13 } from "preact/hooks";
1375
- import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs17 } from "preact/jsx-runtime";
1575
+ import { useState as useState15 } from "preact/hooks";
1576
+ import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs19 } from "preact/jsx-runtime";
1376
1577
  var joinTierMoney = (cents, currency = "usd") => new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
1377
1578
  function useTierSelection(tiers, initialTierId) {
1378
- const [selectedTierId, setSelectedTierId] = useState13(() => {
1579
+ const [selectedTierId, setSelectedTierId] = useState15(() => {
1379
1580
  const preset = tiers.find((tier) => tier.id === initialTierId);
1380
1581
  if (preset) return preset.id;
1381
1582
  return tiers.length === 1 ? tiers[0].id : null;
@@ -1390,10 +1591,10 @@ function useTierSelection(tiers, initialTierId) {
1390
1591
  };
1391
1592
  }
1392
1593
  function TierPlaceholder(context) {
1393
- return /* @__PURE__ */ jsxs17("fieldset", { "data-chapter-placeholder": "tiers", children: [
1394
- /* @__PURE__ */ jsx17("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
1395
- context.tiers.map((tier) => /* @__PURE__ */ jsxs17("label", { children: [
1396
- /* @__PURE__ */ jsx17(
1594
+ return /* @__PURE__ */ jsxs19("fieldset", { "data-chapter-placeholder": "tiers", children: [
1595
+ /* @__PURE__ */ jsx20("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
1596
+ context.tiers.map((tier) => /* @__PURE__ */ jsxs19("label", { children: [
1597
+ /* @__PURE__ */ jsx20(
1397
1598
  "input",
1398
1599
  {
1399
1600
  type: "radio",
@@ -1404,13 +1605,13 @@ function TierPlaceholder(context) {
1404
1605
  }
1405
1606
  ),
1406
1607
  tier.name,
1407
- tier.founding ? /* @__PURE__ */ jsxs17(Fragment8, { children: [
1608
+ tier.founding ? /* @__PURE__ */ jsxs19(Fragment7, { children: [
1408
1609
  " \u2014 ",
1409
1610
  joinTierMoney(tier.founding.dueTodayCents),
1410
1611
  " founding member rate (list ",
1411
1612
  joinTierMoney(tier.priceCents),
1412
1613
  ")"
1413
- ] }) : tier.free ? " \u2014 free" : /* @__PURE__ */ jsxs17(Fragment8, { children: [
1614
+ ] }) : tier.free ? " \u2014 free" : /* @__PURE__ */ jsxs19(Fragment7, { children: [
1414
1615
  " \u2014 ",
1415
1616
  joinTierMoney(tier.priceCents)
1416
1617
  ] })
@@ -1422,19 +1623,28 @@ function tierNeedsPayment(selected, paymentsReady) {
1422
1623
  }
1423
1624
 
1424
1625
  // src/ui/join-application-form.tsx
1425
- import { jsx as jsx18, jsxs as jsxs18 } from "preact/jsx-runtime";
1626
+ import { jsx as jsx21, jsxs as jsxs20 } from "preact/jsx-runtime";
1426
1627
  function JoinApplicationForm({ props, copy, invitation, requestedSeat, tierSelection, giftOfferId, selectGift, submitting, error, submit }) {
1427
- const disabled = submitting || props.submitDisabled === true;
1428
- return /* @__PURE__ */ jsxs18("form", { className: "join-form", onSubmit: (event) => void submit(event), children: [
1429
- !requestedSeat && (props.config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs18("fieldset", { children: [
1430
- /* @__PURE__ */ jsx18("legend", { children: copy.gift.optionsLabel }),
1431
- /* @__PURE__ */ jsx18("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => selectGift(null), children: copy.gift.chooseSelf }),
1432
- /* @__PURE__ */ jsx18(GiftOfferCards, { offers: props.config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: selectGift })
1628
+ const form = useRef6(null);
1629
+ const ready = useFormReadiness(form);
1630
+ const disabled = submitting || props.submitDisabled === true || !ready || !validJoinRecipient(props.payment?.initialNamedSeat);
1631
+ return /* @__PURE__ */ jsxs20("form", { ref: form, className: "join-form", onSubmit: (event) => {
1632
+ if (!isFormReady(event.currentTarget)) {
1633
+ event.preventDefault();
1634
+ event.currentTarget.reportValidity();
1635
+ return;
1636
+ }
1637
+ void submit(event);
1638
+ }, children: [
1639
+ !requestedSeat && (props.config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs20("fieldset", { children: [
1640
+ /* @__PURE__ */ jsx21("legend", { children: copy.gift.optionsLabel }),
1641
+ /* @__PURE__ */ jsx21("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => selectGift(null), children: copy.gift.chooseSelf }),
1642
+ /* @__PURE__ */ jsx21(GiftOfferCards, { offers: props.config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: selectGift })
1433
1643
  ] }) : null,
1434
1644
  !requestedSeat && (props.config.tiers?.length ?? 0) > 1 ? props.renderTiers?.(tierSelection) ?? TierPlaceholder(tierSelection) : null,
1435
1645
  typeof props.children === "function" ? props.children({ invitation }) : props.children,
1436
- error ? /* @__PURE__ */ jsx18("p", { className: "join-error", role: "alert", children: error }) : null,
1437
- props.renderSubmit?.({ submitting, disabled }) ?? /* @__PURE__ */ jsx18("button", { className: "submit-btn", type: "submit", disabled, children: submitting ? copy.form.submitting : copy.form.submit })
1646
+ error ? /* @__PURE__ */ jsx21("p", { className: "join-error", role: "alert", children: error }) : null,
1647
+ props.renderSubmit?.({ submitting, disabled }) ?? /* @__PURE__ */ jsx21(JoinSubmitButton, { disabled, children: submitting ? copy.form.submitting : copy.form.submit })
1438
1648
  ] });
1439
1649
  }
1440
1650
 
@@ -1459,8 +1669,8 @@ function joinStateFromApplicationResponse(data, fallbackNeedsPayment, submittedG
1459
1669
 
1460
1670
  // src/ui/gift-code-redeemer.tsx
1461
1671
  import { InlineActionField } from "@odla-ai/ui/components";
1462
- import { useState as useState14 } from "preact/hooks";
1463
- import { Fragment as Fragment9, jsx as jsx19, jsxs as jsxs19 } from "preact/jsx-runtime";
1672
+ import { useState as useState16 } from "preact/hooks";
1673
+ import { Fragment as Fragment8, jsx as jsx22, jsxs as jsxs21 } from "preact/jsx-runtime";
1464
1674
  var GIFT_CODE_REDEEMER_CSS = `
1465
1675
  .gift-code-redeemer{container-type:inline-size;display:grid;gap:var(--ui-space-1,.35rem);margin:0}
1466
1676
  .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}
@@ -1475,10 +1685,10 @@ var GIFT_CODE_REDEEMER_CSS = `
1475
1685
  @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%}}
1476
1686
  `;
1477
1687
  function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1478
- const [code, setCode] = useState14("");
1479
- const [loading, setLoading] = useState14(false);
1480
- const [message, setMessage] = useState14("");
1481
- const [messageTone, setMessageTone] = useState14("neutral");
1688
+ const [code, setCode] = useState16("");
1689
+ const [loading, setLoading] = useState16(false);
1690
+ const [message, setMessage] = useState16("");
1691
+ const [messageTone, setMessageTone] = useState16("neutral");
1482
1692
  const normalizedCode = code.trim();
1483
1693
  const redeem = async () => {
1484
1694
  if (!normalizedCode || loading) return;
@@ -1505,9 +1715,9 @@ function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1505
1715
  setLoading(false);
1506
1716
  }
1507
1717
  };
1508
- return /* @__PURE__ */ jsxs19(Fragment9, { children: [
1509
- /* @__PURE__ */ jsx19("style", { children: GIFT_CODE_REDEEMER_CSS }),
1510
- /* @__PURE__ */ jsx19(
1718
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1719
+ /* @__PURE__ */ jsx22("style", { children: GIFT_CODE_REDEEMER_CSS }),
1720
+ /* @__PURE__ */ jsx22(
1511
1721
  InlineActionField,
1512
1722
  {
1513
1723
  id,
@@ -1563,12 +1773,12 @@ async function loadJoinResume(applicationId, fetcher = fetch) {
1563
1773
  }
1564
1774
 
1565
1775
  // src/ui/join-gift-claim.tsx
1566
- import { jsx as jsx20, jsxs as jsxs20 } from "preact/jsx-runtime";
1776
+ import { jsx as jsx23, jsxs as jsxs22 } from "preact/jsx-runtime";
1567
1777
  function JoinGiftClaim(props) {
1568
- return /* @__PURE__ */ jsxs20("section", { className: "join-gift-claim card", children: [
1569
- /* @__PURE__ */ jsx20("div", { className: "card-label", children: props.copy.claimTitle }),
1570
- /* @__PURE__ */ jsx20("p", { children: props.copy.claimBody }),
1571
- /* @__PURE__ */ jsx20(
1778
+ return /* @__PURE__ */ jsxs22("section", { className: "join-gift-claim card", children: [
1779
+ /* @__PURE__ */ jsx23("div", { className: "card-label", children: props.copy.claimTitle }),
1780
+ /* @__PURE__ */ jsx23("p", { children: props.copy.claimBody }),
1781
+ /* @__PURE__ */ jsx23(
1572
1782
  GiftCodeRedeemer,
1573
1783
  {
1574
1784
  api: props.api,
@@ -1592,21 +1802,21 @@ function JoinGiftClaim(props) {
1592
1802
  }
1593
1803
 
1594
1804
  // src/ui/join-named-seat.tsx
1595
- import { useState as useState15 } from "preact/hooks";
1596
- import { jsx as jsx21, jsxs as jsxs21 } from "preact/jsx-runtime";
1805
+ import { useState as useState17 } from "preact/hooks";
1806
+ import { jsx as jsx24, jsxs as jsxs23 } from "preact/jsx-runtime";
1597
1807
  function JoinNamedSeat(props) {
1598
1808
  const seatId = props.seatId ?? "";
1599
1809
  const inherited = useChapterCopy(), copy = props.copy ?? inherited.join.namedSeat;
1600
- const [accepted, setAccepted] = useState15(false), [busy, setBusy] = useState15(false), [error, setError] = useState15("");
1601
- return /* @__PURE__ */ jsxs21("section", { className: "join-named-seat card", children: [
1602
- /* @__PURE__ */ jsx21("h2", { children: copy.title }),
1603
- /* @__PURE__ */ jsx21("p", { children: copy.body }),
1604
- /* @__PURE__ */ jsxs21("label", { className: "journey-consent", children: [
1605
- /* @__PURE__ */ jsx21("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
1606
- /* @__PURE__ */ jsx21("span", { children: copy.consent })
1810
+ const [accepted, setAccepted] = useState17(false), [busy, setBusy] = useState17(false), [error, setError] = useState17("");
1811
+ return /* @__PURE__ */ jsxs23("section", { className: "join-named-seat card", children: [
1812
+ /* @__PURE__ */ jsx24("h2", { children: copy.title }),
1813
+ /* @__PURE__ */ jsx24("p", { children: copy.body }),
1814
+ /* @__PURE__ */ jsxs23("label", { className: "journey-consent", children: [
1815
+ /* @__PURE__ */ jsx24("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
1816
+ /* @__PURE__ */ jsx24("span", { children: copy.consent })
1607
1817
  ] }),
1608
- error ? /* @__PURE__ */ jsx21("p", { role: "alert", children: error }) : null,
1609
- /* @__PURE__ */ jsx21("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
1818
+ error ? /* @__PURE__ */ jsx24("p", { role: "alert", children: error }) : null,
1819
+ /* @__PURE__ */ jsx24("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
1610
1820
  setBusy(true);
1611
1821
  setError("");
1612
1822
  try {
@@ -1626,12 +1836,12 @@ function JoinNamedSeat(props) {
1626
1836
  }
1627
1837
 
1628
1838
  // src/ui/join-invitation.tsx
1629
- import { useEffect as useEffect8, useState as useState16 } from "preact/hooks";
1630
- import { jsx as jsx22, jsxs as jsxs22 } from "preact/jsx-runtime";
1839
+ import { useEffect as useEffect8, useState as useState18 } from "preact/hooks";
1840
+ import { jsx as jsx25, jsxs as jsxs24 } from "preact/jsx-runtime";
1631
1841
  function useJoinInvitation(seatId, api, fallback) {
1632
- const [invitation, setInvitation] = useState16(null);
1633
- const [error, setError] = useState16("");
1634
- const [revision, setRevision] = useState16(0);
1842
+ const [invitation, setInvitation] = useState18(null);
1843
+ const [error, setError] = useState18("");
1844
+ const [revision, setRevision] = useState18(0);
1635
1845
  useEffect8(() => {
1636
1846
  let live = true;
1637
1847
  setInvitation(null);
@@ -1654,9 +1864,9 @@ function useJoinInvitation(seatId, api, fallback) {
1654
1864
  return { invitation: invitation?.seatId === seatId ? invitation : null, error, retry: () => setRevision((value) => value + 1) };
1655
1865
  }
1656
1866
  function JoinInvitationNotice(props) {
1657
- return /* @__PURE__ */ jsxs22("aside", { className: "journey-invitation", "aria-label": props.copy.title, children: [
1658
- /* @__PURE__ */ jsx22("p", { children: /* @__PURE__ */ jsx22("strong", { children: formatChapterCopy(props.copy.from, { purchaserName: props.invitation.purchaserName }) }) }),
1659
- /* @__PURE__ */ jsx22("p", { children: formatChapterCopy(props.copy.prepaid, { tierName: props.tierName }) })
1867
+ return /* @__PURE__ */ jsxs24("aside", { className: "journey-invitation", "aria-label": props.copy.title, children: [
1868
+ /* @__PURE__ */ jsx25("p", { children: /* @__PURE__ */ jsx25("strong", { children: formatChapterCopy(props.copy.from, { purchaserName: props.invitation.purchaserName }) }) }),
1869
+ /* @__PURE__ */ jsx25("p", { children: formatChapterCopy(props.copy.prepaid, { tierName: props.tierName }) })
1660
1870
  ] });
1661
1871
  }
1662
1872
 
@@ -1696,14 +1906,14 @@ function useJoinPending(state, loadResume, setState) {
1696
1906
  }
1697
1907
 
1698
1908
  // src/ui/payment-receipt.tsx
1699
- import { useEffect as useEffect10, useState as useState17 } from "preact/hooks";
1700
- import { Fragment as Fragment10, jsx as jsx23, jsxs as jsxs23 } from "preact/jsx-runtime";
1909
+ import { useEffect as useEffect10, useState as useState19 } from "preact/hooks";
1910
+ import { Fragment as Fragment9, jsx as jsx26, jsxs as jsxs25 } from "preact/jsx-runtime";
1701
1911
  function PaymentReceiptCard({ applicationId }) {
1702
- const [receipt, setReceipt] = useState17(null);
1703
- const [error, setError] = useState17("");
1704
- const [revision, setRevision] = useState17(0);
1705
- const [emailState, setEmailState] = useState17("idle");
1706
- const [previewEmail, setPreviewEmail] = useState17(false);
1912
+ const [receipt, setReceipt] = useState19(null);
1913
+ const [error, setError] = useState19("");
1914
+ const [revision, setRevision] = useState19(0);
1915
+ const [emailState, setEmailState] = useState19("idle");
1916
+ const [previewEmail, setPreviewEmail] = useState19(false);
1707
1917
  const endpoint = `/api/payments/receipt?application=${encodeURIComponent(applicationId)}`;
1708
1918
  useEffect10(() => {
1709
1919
  let live = true, timer;
@@ -1731,9 +1941,9 @@ function PaymentReceiptCard({ applicationId }) {
1731
1941
  if (timer) clearTimeout(timer);
1732
1942
  };
1733
1943
  }, [endpoint, revision]);
1734
- if (!receipt) return error ? /* @__PURE__ */ jsxs23("section", { "aria-label": "Payment receipt", children: [
1735
- /* @__PURE__ */ jsx23("p", { role: "status", children: error }),
1736
- /* @__PURE__ */ jsx23("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Reload receipt" })
1944
+ if (!receipt) return error ? /* @__PURE__ */ jsxs25("section", { "aria-label": "Payment receipt", children: [
1945
+ /* @__PURE__ */ jsx26("p", { role: "status", children: error }),
1946
+ /* @__PURE__ */ jsx26("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Reload receipt" })
1737
1947
  ] }) : null;
1738
1948
  const money = (cents) => new Intl.NumberFormat("en-US", { style: "currency", currency: receipt.currency }).format(cents / 100);
1739
1949
  const email = async () => {
@@ -1750,45 +1960,38 @@ function PaymentReceiptCard({ applicationId }) {
1750
1960
  setError("We could not confirm email delivery. You can print or save your receipt.");
1751
1961
  }
1752
1962
  };
1753
- return /* @__PURE__ */ jsxs23("section", { className: "payment-receipt card", "aria-label": "Payment receipt", children: [
1754
- /* @__PURE__ */ jsx23("h2", { children: "Payment received" }),
1755
- /* @__PURE__ */ jsx23("p", { children: receipt.tierName }),
1756
- /* @__PURE__ */ jsxs23("dl", { className: "journey-summary", children: [
1757
- /* @__PURE__ */ jsx23("dt", { children: "Membership" }),
1758
- /* @__PURE__ */ jsx23("dd", { children: money(receipt.membershipCents) }),
1759
- receipt.namedSeat ? /* @__PURE__ */ jsxs23(Fragment10, { children: [
1760
- /* @__PURE__ */ jsxs23("dt", { children: [
1963
+ return /* @__PURE__ */ jsxs25("section", { className: "payment-receipt card", "aria-label": "Payment receipt", children: [
1964
+ /* @__PURE__ */ jsx26("h2", { children: "Payment received" }),
1965
+ /* @__PURE__ */ jsx26("p", { children: receipt.tierName }),
1966
+ /* @__PURE__ */ jsxs25("dl", { className: "journey-summary", children: [
1967
+ /* @__PURE__ */ jsx26("dt", { children: "Membership" }),
1968
+ /* @__PURE__ */ jsx26("dd", { children: money(receipt.membershipCents) }),
1969
+ receipt.namedSeat ? /* @__PURE__ */ jsxs25(Fragment9, { children: [
1970
+ /* @__PURE__ */ jsxs25("dt", { children: [
1761
1971
  "Additional member \u2014 ",
1762
1972
  receipt.namedSeat.recipientName
1763
1973
  ] }),
1764
- /* @__PURE__ */ jsxs23("dd", { children: [
1974
+ /* @__PURE__ */ jsxs25("dd", { children: [
1765
1975
  receipt.namedSeat.amountCents === 0 ? "Included \u2014 " : "",
1766
1976
  money(receipt.namedSeat.amountCents)
1767
1977
  ] })
1768
1978
  ] }) : null,
1769
- /* @__PURE__ */ jsx23("dt", { children: "Total paid" }),
1770
- /* @__PURE__ */ jsx23("dd", { children: money(receipt.totalCents) })
1979
+ /* @__PURE__ */ jsx26("dt", { children: "Total paid" }),
1980
+ /* @__PURE__ */ jsx26("dd", { children: money(receipt.totalCents) })
1771
1981
  ] }),
1772
- /* @__PURE__ */ jsxs23("div", { className: "journey-actions", children: [
1773
- /* @__PURE__ */ jsx23("a", { className: "apply-link journey-secondary", href: `${endpoint}&format=html`, target: "_blank", rel: "noopener noreferrer", children: "Print / save receipt" }),
1774
- /* @__PURE__ */ jsx23("button", { type: "button", disabled: emailState !== "idle", onClick: () => void email(), children: emailState === "sending" ? "Sending\u2026" : emailState === "sent" ? "Receipt emailed" : "Email me my receipt" })
1982
+ /* @__PURE__ */ jsxs25("div", { className: "journey-actions", children: [
1983
+ /* @__PURE__ */ jsx26("a", { className: "apply-link journey-secondary", href: `${endpoint}&format=html`, target: "_blank", rel: "noopener noreferrer", children: "Print / save receipt" }),
1984
+ /* @__PURE__ */ jsx26("button", { type: "button", disabled: emailState !== "idle", onClick: () => void email(), children: emailState === "sending" ? "Sending\u2026" : emailState === "sent" ? "Receipt emailed" : "Email me my receipt" })
1775
1985
  ] }),
1776
- emailState === "sent" ? /* @__PURE__ */ jsx23("p", { role: "status", children: previewEmail ? "Test receipt sent to the configured test inbox." : "Sent to the email used for your payment." }) : null,
1777
- error ? /* @__PURE__ */ jsx23("p", { role: "alert", children: error }) : null
1986
+ 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,
1987
+ error ? /* @__PURE__ */ jsx26("p", { role: "alert", children: error }) : null
1778
1988
  ] });
1779
1989
  }
1780
1990
 
1781
- // src/ui/join-recipient.ts
1782
- function validJoinRecipient(recipient) {
1783
- if (!recipient) return true;
1784
- const name = recipient.recipientName.trim(), email = recipient.recipientEmail.trim();
1785
- return name.length >= 2 && name.length <= 160 && !/[\u0000-\u001f\u007f]/.test(name) && email.length <= 254 && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
1786
- }
1787
-
1788
1991
  // src/ui/join.tsx
1789
- import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs24 } from "preact/jsx-runtime";
1992
+ import { Fragment as Fragment10, jsx as jsx27, jsxs as jsxs26 } from "preact/jsx-runtime";
1790
1993
  function JoinIsland(props) {
1791
- return /* @__PURE__ */ jsx24(JourneyFrame, { children: /* @__PURE__ */ jsx24(JoinFlow, { ...props }) });
1994
+ return /* @__PURE__ */ jsx27(JourneyFrame, { children: /* @__PURE__ */ jsx27(JoinFlow, { ...props }) });
1792
1995
  }
1793
1996
  function JoinFlow(props) {
1794
1997
  const inheritedCopy = useChapterCopy();
@@ -1809,14 +2012,14 @@ function JoinFlow(props) {
1809
2012
  const invitation = invitationState.invitation;
1810
2013
  const selectedTierId = invitation?.skuId ?? tierSelection.selectedTierId;
1811
2014
  const selectedTier = tiers.find((tier) => tier.id === selectedTierId) ?? tierSelection.selected;
1812
- const [state, setState] = useState18(requestedSeat ? { step: "form" } : props.initialState ?? { step: "form" });
1813
- const [error, setError] = useState18(null);
1814
- const [submitting, setSubmitting] = useState18(false);
1815
- const [editingNamedSeat, setEditingNamedSeat] = useState18(false);
1816
- const [giftOfferId, setGiftOfferId] = useState18(props.initialGiftOfferId ?? null);
2015
+ const [state, setState] = useState20(requestedSeat ? { step: "form" } : props.initialState ?? { step: "form" });
2016
+ const [error, setError] = useState20(null);
2017
+ const [submitting, setSubmitting] = useState20(false);
2018
+ const [editingNamedSeat, setEditingNamedSeat] = useState20(false);
2019
+ const [giftOfferId, setGiftOfferId] = useState20(props.initialGiftOfferId ?? null);
1817
2020
  const simulatedPayment = joinSimulationPayment(props.simulation, selectedTier?.priceCents ?? 0);
1818
2021
  const simulatedBooking = props.simulation ? props.simulation.booking ?? { slots: [], timezone: "UTC" } : void 0;
1819
- const submissionId = useRef3(null);
2022
+ const submissionId = useRef7(null);
1820
2023
  useEffect11(() => {
1821
2024
  if (props.simulation) return;
1822
2025
  const resolver = requestedSeat ? () => invitation?.applicationId ? loadJoinResume(invitation.applicationId) : Promise.resolve(null) : props.loadResume ?? (() => {
@@ -1890,13 +2093,13 @@ function JoinFlow(props) {
1890
2093
  setSubmitting(false);
1891
2094
  }
1892
2095
  };
1893
- if (requestedSeat && !invitation) return /* @__PURE__ */ jsxs24("section", { className: "card", "aria-live": "polite", children: [
1894
- /* @__PURE__ */ jsx24("h2", { children: seatCopy.title }),
1895
- /* @__PURE__ */ jsx24("p", { role: invitationState.error ? "alert" : "status", children: invitationState.error || seatCopy.loading }),
1896
- invitationState.error ? /* @__PURE__ */ jsx24("button", { type: "button", onClick: invitationState.retry, children: seatCopy.retry }) : null
2096
+ if (requestedSeat && !invitation) return /* @__PURE__ */ jsxs26("section", { className: "card", "aria-live": "polite", children: [
2097
+ /* @__PURE__ */ jsx27("h2", { children: seatCopy.title }),
2098
+ /* @__PURE__ */ jsx27("p", { role: invitationState.error ? "alert" : "status", children: invitationState.error || seatCopy.loading }),
2099
+ invitationState.error ? /* @__PURE__ */ jsx27("button", { type: "button", onClick: invitationState.retry, children: seatCopy.retry }) : null
1897
2100
  ] });
1898
- const header = /* @__PURE__ */ jsxs24(Fragment11, { children: [
1899
- invitation ? /* @__PURE__ */ jsx24(
2101
+ const header = /* @__PURE__ */ jsxs26(Fragment10, { children: [
2102
+ invitation ? /* @__PURE__ */ jsx27(
1900
2103
  JoinInvitationNotice,
1901
2104
  {
1902
2105
  invitation,
@@ -1910,48 +2113,48 @@ function JoinFlow(props) {
1910
2113
  ...invitation ? { invitation } : {},
1911
2114
  ..."applicationId" in state ? { applicationId: state.applicationId } : {}
1912
2115
  }),
1913
- !props.simulation && !requestedSeat && ["paymentPending", "booking", "accepted", "done"].includes(state.step) && "applicationId" in state ? /* @__PURE__ */ jsx24(PaymentReceiptCard, { applicationId: state.applicationId }) : null
2116
+ !props.simulation && !requestedSeat && ["paymentPending", "booking", "accepted", "done"].includes(state.step) && "applicationId" in state ? /* @__PURE__ */ jsx27(PaymentReceiptCard, { applicationId: state.applicationId }) : null
1914
2117
  ] });
1915
2118
  if (state.step === "closed") {
1916
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2119
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1917
2120
  header,
1918
- /* @__PURE__ */ jsxs24("section", { className: "card", "aria-label": "Application closed", children: [
1919
- /* @__PURE__ */ jsx24("h2", { children: "This application is closed" }),
1920
- /* @__PURE__ */ jsx24("p", { children: "This link cannot resume payment or booking. Sign in to view your application status, or contact the chapter if you need help." }),
1921
- /* @__PURE__ */ jsx24("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
2121
+ /* @__PURE__ */ jsxs26("section", { className: "card", "aria-label": "Application closed", children: [
2122
+ /* @__PURE__ */ jsx27("h2", { children: "This application is closed" }),
2123
+ /* @__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." }),
2124
+ /* @__PURE__ */ jsx27("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1922
2125
  ] })
1923
2126
  ] });
1924
2127
  }
1925
2128
  if (state.step === "done") {
1926
- const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs24("div", { className: "join-done card", children: [
1927
- /* @__PURE__ */ jsx24("div", { className: "card-label", children: copy.done.label }),
1928
- /* @__PURE__ */ jsx24("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
1929
- /* @__PURE__ */ jsx24("p", { className: "meeting-note", children: copy.done.calendarInvite }),
1930
- /* @__PURE__ */ jsx24("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
2129
+ const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs26("div", { className: "join-done card", children: [
2130
+ /* @__PURE__ */ jsx27("div", { className: "card-label", children: copy.done.label }),
2131
+ /* @__PURE__ */ jsx27("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
2132
+ /* @__PURE__ */ jsx27("p", { className: "meeting-note", children: copy.done.calendarInvite }),
2133
+ /* @__PURE__ */ jsx27("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1931
2134
  ] });
1932
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2135
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1933
2136
  header,
1934
2137
  body
1935
2138
  ] });
1936
2139
  }
1937
2140
  if (state.step === "accepted") {
1938
- const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx24(JoinAccepted, { copy, membersHref });
1939
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2141
+ const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx27(JoinAccepted, { copy, membersHref });
2142
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1940
2143
  header,
1941
2144
  body
1942
2145
  ] });
1943
2146
  }
1944
2147
  if (state.step === "gift") {
1945
- const body = renderGift?.(state) ?? /* @__PURE__ */ jsx24(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
1946
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2148
+ const body = renderGift?.(state) ?? /* @__PURE__ */ jsx27(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
2149
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1947
2150
  header,
1948
2151
  body
1949
2152
  ] });
1950
2153
  }
1951
2154
  if (state.step === "booking") {
1952
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2155
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1953
2156
  header,
1954
- /* @__PURE__ */ jsx24(
2157
+ /* @__PURE__ */ jsx27(
1955
2158
  JoinBooking,
1956
2159
  {
1957
2160
  applicationId: state.applicationId,
@@ -1966,9 +2169,9 @@ function JoinFlow(props) {
1966
2169
  }
1967
2170
  if (state.step === "payment") {
1968
2171
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation && !state.giftPurchase) {
1969
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2172
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1970
2173
  header,
1971
- /* @__PURE__ */ jsx24(
2174
+ /* @__PURE__ */ jsx27(
1972
2175
  JoinNamedSeat,
1973
2176
  {
1974
2177
  api: props.namedSeatClaimApi,
@@ -1981,9 +2184,9 @@ function JoinFlow(props) {
1981
2184
  ] });
1982
2185
  }
1983
2186
  const Payment = state.giftPurchase || props.simulation ? PaymentStep : MembershipPaymentStep;
1984
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2187
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
1985
2188
  header,
1986
- props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx24(
2189
+ props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx27(
1987
2190
  JoinGiftClaim,
1988
2191
  {
1989
2192
  api: props.giftClaimApi,
@@ -1994,7 +2197,7 @@ function JoinFlow(props) {
1994
2197
  onError: () => setError(copy.form.unexpectedFailure)
1995
2198
  }
1996
2199
  ) : null,
1997
- /* @__PURE__ */ jsx24(
2200
+ /* @__PURE__ */ jsx27(
1998
2201
  Payment,
1999
2202
  {
2000
2203
  applicationId: state.applicationId,
@@ -2022,9 +2225,9 @@ function JoinFlow(props) {
2022
2225
  }
2023
2226
  if (state.step === "paymentPending") {
2024
2227
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation) {
2025
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2228
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
2026
2229
  header,
2027
- /* @__PURE__ */ jsx24(
2230
+ /* @__PURE__ */ jsx27(
2028
2231
  JoinNamedSeat,
2029
2232
  {
2030
2233
  api: props.namedSeatClaimApi,
@@ -2036,14 +2239,14 @@ function JoinFlow(props) {
2036
2239
  )
2037
2240
  ] });
2038
2241
  }
2039
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2242
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
2040
2243
  header,
2041
- /* @__PURE__ */ jsx24("p", { className: "pay-status", role: "status", children: copy.payment.pending })
2244
+ /* @__PURE__ */ jsx27("p", { className: "pay-status", role: "status", children: copy.payment.pending })
2042
2245
  ] });
2043
2246
  }
2044
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
2247
+ return /* @__PURE__ */ jsxs26(Fragment10, { children: [
2045
2248
  header,
2046
- /* @__PURE__ */ jsx24(
2249
+ /* @__PURE__ */ jsx27(
2047
2250
  JoinApplicationForm,
2048
2251
  {
2049
2252
  props,
@@ -2233,7 +2436,7 @@ function membershipPageDocument(input, options = {}) {
2233
2436
  }
2234
2437
 
2235
2438
  // src/ui/membership-cards.tsx
2236
- import { jsx as jsx25, jsxs as jsxs25 } from "preact/jsx-runtime";
2439
+ import { jsx as jsx28, jsxs as jsxs27 } from "preact/jsx-runtime";
2237
2440
  var MEMBERSHIP_CARD_TOKEN_DEFAULTS = Object.freeze({
2238
2441
  "--ui-membership-grid-gap": "1.4rem",
2239
2442
  "--ui-membership-card-padding": "2rem 1.7rem 1.8rem",
@@ -2302,67 +2505,67 @@ function membershipApplicationHref(applicationPath, tierId) {
2302
2505
  }
2303
2506
  function TierAction(props) {
2304
2507
  if (props.mode === "select") {
2305
- return /* @__PURE__ */ jsxs25("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
2508
+ return /* @__PURE__ */ jsxs27("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
2306
2509
  props.label,
2307
- /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u2192" })
2510
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u2192" })
2308
2511
  ] });
2309
2512
  }
2310
2513
  if (props.mode === "preview") {
2311
- return /* @__PURE__ */ jsxs25("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
2514
+ return /* @__PURE__ */ jsxs27("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
2312
2515
  props.label,
2313
- /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u2192" })
2516
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u2192" })
2314
2517
  ] });
2315
2518
  }
2316
- return /* @__PURE__ */ jsxs25("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
2519
+ return /* @__PURE__ */ jsxs27("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
2317
2520
  props.label,
2318
- /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u2192" })
2521
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u2192" })
2319
2522
  ] });
2320
2523
  }
2321
2524
  function TierCard(props) {
2322
2525
  const { tier, presentation } = props;
2323
2526
  const summary = presentation.summaryMarkdown ?? tier.blurb;
2324
2527
  const action = presentation.ctaLabel ?? (tier.free ? "Join for free" : `Choose ${tier.name}`);
2325
- return /* @__PURE__ */ jsxs25("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
2326
- presentation.badge ? /* @__PURE__ */ jsx25("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
2327
- /* @__PURE__ */ jsx25("h3", { className: "chapter-membership-name", children: tier.name }),
2328
- /* @__PURE__ */ jsxs25("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
2329
- presentation.capacity ? /* @__PURE__ */ jsxs25("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
2330
- /* @__PURE__ */ jsx25("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
2331
- /* @__PURE__ */ jsx25("small", { children: presentation.capacity.label ?? "available" })
2528
+ return /* @__PURE__ */ jsxs27("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
2529
+ presentation.badge ? /* @__PURE__ */ jsx28("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
2530
+ /* @__PURE__ */ jsx28("h3", { className: "chapter-membership-name", children: tier.name }),
2531
+ /* @__PURE__ */ jsxs27("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
2532
+ presentation.capacity ? /* @__PURE__ */ jsxs27("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
2533
+ /* @__PURE__ */ jsx28("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
2534
+ /* @__PURE__ */ jsx28("small", { children: presentation.capacity.label ?? "available" })
2332
2535
  ] }) : null,
2333
- /* @__PURE__ */ jsxs25("div", { className: "chapter-membership-price-stack", children: [
2334
- presentation.priceEyebrow ? /* @__PURE__ */ jsx25("p", { className: "chapter-membership-price-eyebrow", children: presentation.priceEyebrow }) : tier.founding ? /* @__PURE__ */ jsx25("p", { className: "chapter-membership-price-eyebrow", children: "Founding member rate" }) : null,
2335
- /* @__PURE__ */ jsxs25("div", { className: "chapter-membership-price-row", children: [
2336
- tier.founding ? /* @__PURE__ */ jsx25("span", { className: "chapter-membership-compare", children: fmtMoney(tier.priceCents) }) : presentation.compareAtPriceCents !== void 0 ? /* @__PURE__ */ jsx25("span", { className: "chapter-membership-compare", children: fmtMoney(presentation.compareAtPriceCents) }) : null,
2337
- /* @__PURE__ */ jsx25("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.founding ? tier.founding.dueTodayCents : tier.priceCents) }),
2338
- !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx25("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
2536
+ /* @__PURE__ */ jsxs27("div", { className: "chapter-membership-price-stack", children: [
2537
+ 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,
2538
+ /* @__PURE__ */ jsxs27("div", { className: "chapter-membership-price-row", children: [
2539
+ 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,
2540
+ /* @__PURE__ */ jsx28("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.founding ? tier.founding.dueTodayCents : tier.priceCents) }),
2541
+ !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx28("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
2339
2542
  ] })
2340
2543
  ] })
2341
2544
  ] }),
2342
- presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx25(Markdown2, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
2343
- summary ? /* @__PURE__ */ jsx25(Markdown2, { className: "chapter-membership-summary", source: summary }) : null,
2344
- presentation.benefits?.length ? /* @__PURE__ */ jsx25("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs25("li", { children: [
2345
- /* @__PURE__ */ jsx25("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
2346
- /* @__PURE__ */ jsx25(Markdown2, { className: "chapter-membership-benefit", source: benefit })
2545
+ presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx28(Markdown2, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
2546
+ summary ? /* @__PURE__ */ jsx28(Markdown2, { className: "chapter-membership-summary", source: summary }) : null,
2547
+ presentation.benefits?.length ? /* @__PURE__ */ jsx28("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs27("li", { children: [
2548
+ /* @__PURE__ */ jsx28("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
2549
+ /* @__PURE__ */ jsx28(Markdown2, { className: "chapter-membership-benefit", source: benefit })
2347
2550
  ] }, `${tier.id}-benefit-${index}`)) }) : null,
2348
- presentation.callout ? /* @__PURE__ */ jsxs25("aside", { className: "chapter-membership-callout", children: [
2349
- presentation.callout.label ? /* @__PURE__ */ jsx25("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
2350
- /* @__PURE__ */ jsx25(Markdown2, { source: presentation.callout.bodyMarkdown })
2551
+ presentation.callout ? /* @__PURE__ */ jsxs27("aside", { className: "chapter-membership-callout", children: [
2552
+ presentation.callout.label ? /* @__PURE__ */ jsx28("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
2553
+ /* @__PURE__ */ jsx28(Markdown2, { source: presentation.callout.bodyMarkdown })
2351
2554
  ] }) : null,
2352
- /* @__PURE__ */ jsx25(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
2555
+ /* @__PURE__ */ jsx28(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
2353
2556
  ] });
2354
2557
  }
2355
2558
  function MembershipCards(props) {
2356
2559
  const document2 = membershipPageDocument(props.document, { tiers: props.tiers });
2357
2560
  const byId = new Map(props.tiers.map((tier) => [tier.id, tier]));
2358
2561
  const mode = props.mode ?? "link";
2359
- return /* @__PURE__ */ jsxs25("section", { className: ["chapter-membership", props.className].filter(Boolean).join(" "), "data-chapter-membership": MEMBERSHIP_PAGE_RENDERER, "aria-label": props.ariaLabel ?? "Membership options", children: [
2360
- /* @__PURE__ */ jsx25("style", { children: MEMBERSHIP_CARDS_CSS }),
2361
- /* @__PURE__ */ jsx25("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
2562
+ 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: [
2563
+ /* @__PURE__ */ jsx28("style", { children: MEMBERSHIP_CARDS_CSS }),
2564
+ /* @__PURE__ */ jsx28("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
2362
2565
  const tier = byId.get(presentation.tierId);
2363
- return /* @__PURE__ */ jsx25(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
2566
+ return /* @__PURE__ */ jsx28(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
2364
2567
  }) }),
2365
- document2.footnoteMarkdown ? /* @__PURE__ */ jsx25(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
2568
+ document2.footnoteMarkdown ? /* @__PURE__ */ jsx28(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
2366
2569
  ] });
2367
2570
  }
2368
2571
 
@@ -2376,6 +2579,11 @@ export {
2376
2579
  fmtDate,
2377
2580
  groupSlotsByDay,
2378
2581
  SlotPicker,
2582
+ ADDITIONAL_MEMBER_COPY,
2583
+ additionalMemberSelection,
2584
+ AdditionalMemberFields,
2585
+ JoinSubmitButton,
2586
+ AdditionalMemberEditor,
2379
2587
  PaymentStep,
2380
2588
  JourneyFrame,
2381
2589
  MemberRestart,
@@ -2398,4 +2606,4 @@ export {
2398
2606
  membershipApplicationHref,
2399
2607
  MembershipCards
2400
2608
  };
2401
- //# sourceMappingURL=chunk-NK267YQZ.js.map
2609
+ //# sourceMappingURL=chunk-CKC6X5H2.js.map