@odla-ai/chapter 0.48.3 → 0.49.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.
@@ -1,7 +1,8 @@
1
1
  import {
2
+ DEFAULT_JOIN_COPY,
2
3
  formatChapterCopy,
3
4
  useChapterCopy
4
- } from "./chunk-4HS2JPE2.js";
5
+ } from "./chunk-N2JIKINY.js";
5
6
  import {
6
7
  collectFormFields
7
8
  } from "./chunk-IVXVECKR.js";
@@ -109,113 +110,15 @@ function SlotPicker(props) {
109
110
  ] });
110
111
  }
111
112
 
112
- // src/ui/members.tsx
113
- import { useEffect as useEffect4, useState as useState7 } from "preact/hooks";
113
+ // src/ui/member-restart.tsx
114
+ import { useEffect as useEffect2, useState as useState4 } from "preact/hooks";
114
115
 
115
- // src/ui/reschedule.tsx
116
- import { useState as useState2 } from "preact/hooks";
117
- import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
118
- function Rescheduler(props) {
119
- const inheritedCopy = useChapterCopy();
120
- const copy = props.copy ?? inheritedCopy.members.reschedule;
121
- const { api, applicationId, timezone, onRescheduled, label = copy.changeTime } = props;
122
- const [open, setOpen] = useState2(false);
123
- const [slots, setSlots] = useState2(null);
124
- const [tz, setTz] = useState2(timezone);
125
- const [busy, setBusy] = useState2(false);
126
- const [msg, setMsg] = useState2(null);
127
- const start = async () => {
128
- setOpen(true);
129
- setSlots(null);
130
- setMsg(null);
131
- try {
132
- const r = await api("/api/schedule/slots");
133
- if (r.schedulingReady === false) {
134
- setSlots([]);
135
- setMsg(copy.unavailable);
136
- return;
137
- }
138
- setSlots(r.slots ?? []);
139
- if (r.timezone) setTz(r.timezone);
140
- } catch {
141
- setSlots([]);
142
- setMsg(copy.loadFailed);
143
- }
144
- };
145
- const pick = async (slot) => {
146
- setBusy(true);
147
- setMsg(null);
148
- try {
149
- await api("/api/schedule/book", { method: "POST", body: JSON.stringify({ applicationId, startAt: slot.startAt }) });
150
- setOpen(false);
151
- await onRescheduled();
152
- } catch (e) {
153
- setMsg(e instanceof Error ? e.message : copy.slotGone);
154
- } finally {
155
- setBusy(false);
156
- }
157
- };
158
- if (!open) {
159
- return /* @__PURE__ */ jsx2("p", { className: "meeting-note", children: /* @__PURE__ */ jsx2(
160
- "a",
161
- {
162
- href: "#",
163
- onClick: (e) => {
164
- e.preventDefault();
165
- void start();
166
- },
167
- children: label
168
- }
169
- ) });
170
- }
171
- return /* @__PURE__ */ jsxs2("div", { className: "msched", children: [
172
- slots === null ? /* @__PURE__ */ jsx2("p", { className: "meeting-note", children: copy.loading }) : slots.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "meeting-note", children: msg ?? copy.noTimes }) : /* @__PURE__ */ jsx2(
173
- SlotPicker,
174
- {
175
- slots,
176
- timezone: tz,
177
- classes: { days: "msched-days", day: "msched-day", times: "msched-times", time: "msched-time" },
178
- onPick: (s) => void pick(s)
179
- }
180
- ),
181
- busy ? /* @__PURE__ */ jsx2("p", { className: "meeting-note", children: copy.rescheduling }) : null,
182
- msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx2("p", { className: "meeting-note error", children: msg }) : null,
183
- /* @__PURE__ */ jsx2("p", { className: "meeting-note", children: /* @__PURE__ */ jsx2(
184
- "a",
185
- {
186
- href: "#",
187
- onClick: (e) => {
188
- e.preventDefault();
189
- setOpen(false);
190
- },
191
- children: copy.keepTime
192
- }
193
- ) })
194
- ] });
195
- }
196
-
197
- // src/ui/member-gifts.tsx
198
- import { useEffect, useRef as useRef2, useState as useState4 } from "preact/hooks";
199
-
200
- // src/ui/gift-offer-cards.tsx
201
- import { jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
202
- var price = (cents, currency) => currency === "usd" ? fmtMoney(cents) : new Intl.NumberFormat(void 0, { style: "currency", currency: currency.toUpperCase() }).format(cents / 100);
203
- function GiftOfferCards(props) {
204
- const mode = props.mode ?? "preview";
205
- return /* @__PURE__ */ jsx3("section", { className: "chapter-gift-offers", "aria-label": props.ariaLabel ?? "Gift options", children: props.offers.map((offer) => /* @__PURE__ */ jsxs3("article", { className: "card chapter-gift-offer", "data-offer-id": offer.id, children: [
206
- /* @__PURE__ */ jsx3("div", { className: "card-label", children: offer.title }),
207
- /* @__PURE__ */ jsx3("p", { children: offer.description }),
208
- /* @__PURE__ */ jsxs3("p", { className: "meeting-date", children: [
209
- price(offer.priceCents, offer.currency),
210
- offer.billingMode === "subscription" ? " recurring" : ""
211
- ] }),
212
- mode === "select" ? /* @__PURE__ */ jsx3("button", { type: "button", className: "btn secondary", "aria-pressed": props.selectedOfferId === offer.id, onClick: () => props.onSelect?.(offer.id), children: offer.ctaLabel }) : /* @__PURE__ */ jsx3("span", { className: "btn secondary", "aria-disabled": "true", children: offer.ctaLabel })
213
- ] }, offer.id)) });
214
- }
116
+ // src/ui/membership-payment-step.tsx
117
+ import { useEffect, useState as useState3 } from "preact/hooks";
215
118
 
216
119
  // src/ui/payment-step.tsx
217
- import { useRef, useState as useState3 } from "preact/hooks";
218
- import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
120
+ import { useRef, useState as useState2 } from "preact/hooks";
121
+ import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
219
122
  var loader = null;
220
123
  function loadStripe() {
221
124
  const existing = globalThis.Stripe;
@@ -249,9 +152,9 @@ function PaymentStep(props) {
249
152
  simulation
250
153
  } = props;
251
154
  const copy = props.copy ?? inheritedCopy.join.payment;
252
- const [status, setStatus] = useState3("idle");
253
- const [error, setError] = useState3(null);
254
- const [lineItems, setLineItems] = useState3(null);
155
+ const [status, setStatus] = useState2("idle");
156
+ const [error, setError] = useState2(null);
157
+ const [lineItems, setLineItems] = useState2(null);
255
158
  const mountRef = useRef(null);
256
159
  const stripeRef = useRef(null);
257
160
  const elementsRef = useRef(null);
@@ -325,12 +228,12 @@ function PaymentStep(props) {
325
228
  setStatus("ready");
326
229
  }
327
230
  };
328
- return /* @__PURE__ */ jsxs4("div", { className: "join-pay", children: [
329
- /* @__PURE__ */ jsxs4("div", { className: "compliance-box", children: [
330
- merchantDisclosureText ? /* @__PURE__ */ jsx4("p", { className: "compliance-merchant", children: merchantDisclosureText }) : null,
331
- /* @__PURE__ */ jsx4("p", { className: "compliance-policy", children: refundPolicyText }),
332
- /* @__PURE__ */ jsxs4("label", { className: "compliance-check", children: [
333
- /* @__PURE__ */ jsx4(
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(
334
237
  "input",
335
238
  {
336
239
  type: "checkbox",
@@ -340,44 +243,352 @@ function PaymentStep(props) {
340
243
  }
341
244
  }
342
245
  ),
343
- copy.consent ? /* @__PURE__ */ jsx4("span", { children: copy.consent }) : null
246
+ copy.consent ? /* @__PURE__ */ jsx2("span", { children: copy.consent }) : null
344
247
  ] })
345
248
  ] }),
346
- status === "loading" ? /* @__PURE__ */ jsx4("p", { className: "pay-status", children: copy.preparing }) : null,
249
+ status === "loading" ? /* @__PURE__ */ jsx2("p", { className: "pay-status", children: copy.preparing }) : null,
347
250
  lineItems && renderPriceLines ? renderPriceLines(lineItems) : null,
348
251
  children,
349
- simulation && (status === "ready" || status === "confirming") ? /* @__PURE__ */ jsxs4("fieldset", { className: "join-pay-simulation", children: [
350
- /* @__PURE__ */ jsx4("legend", { children: "Payment details" }),
351
- /* @__PURE__ */ jsxs4("label", { 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: [
352
255
  "Card number",
353
- /* @__PURE__ */ jsx4("input", { name: "simulationCardNumber", inputMode: "numeric", autoComplete: "off", defaultValue: "4242 4242 4242 4242" })
256
+ /* @__PURE__ */ jsx2("input", { name: "simulationCardNumber", inputMode: "numeric", autoComplete: "off", defaultValue: "4242 4242 4242 4242" })
354
257
  ] }),
355
- /* @__PURE__ */ jsxs4("label", { children: [
258
+ /* @__PURE__ */ jsxs2("label", { children: [
356
259
  "Expiration",
357
- /* @__PURE__ */ jsx4("input", { name: "simulationCardExpiry", inputMode: "numeric", autoComplete: "off", defaultValue: "12 / 34" })
260
+ /* @__PURE__ */ jsx2("input", { name: "simulationCardExpiry", inputMode: "numeric", autoComplete: "off", defaultValue: "12 / 34" })
358
261
  ] }),
359
- /* @__PURE__ */ jsxs4("label", { children: [
262
+ /* @__PURE__ */ jsxs2("label", { children: [
360
263
  "Security code",
361
- /* @__PURE__ */ jsx4("input", { name: "simulationCardCvc", inputMode: "numeric", autoComplete: "off", defaultValue: "123" })
264
+ /* @__PURE__ */ jsx2("input", { name: "simulationCardCvc", inputMode: "numeric", autoComplete: "off", defaultValue: "123" })
362
265
  ] }),
363
- /* @__PURE__ */ jsx4("p", { children: "This is a simulation. No card data is sent and no charge is created." })
266
+ /* @__PURE__ */ jsx2("p", { children: "This is a simulation. No card data is sent and no charge is created." })
364
267
  ] }) : null,
365
- /* @__PURE__ */ jsx4("div", { ref: mountRef, hidden: status === "idle" || status === "loading" }),
366
- status === "ready" || status === "confirming" ? /* @__PURE__ */ jsx4("button", { className: "submit-btn", disabled: status === "confirming", onClick: () => void pay(), children: status === "confirming" ? copy.processing : copy.payAndContinue }) : null,
367
- error ? /* @__PURE__ */ jsx4("p", { className: "pay-error", children: error }) : 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
368
271
  ] });
369
272
  }
370
273
 
371
- // src/ui/member-gifts.tsx
274
+ // src/ui/membership-payment-step.tsx
275
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
276
+ function MembershipPaymentStep(props) {
277
+ const inherited = useChapterCopy();
278
+ const copy = props.copy ?? inherited.join.payment;
279
+ const [quote, setQuote] = useState3(null);
280
+ const [error, setError] = useState3(false);
281
+ const [revision, setRevision] = useState3(0);
282
+ useEffect(() => {
283
+ let live = true;
284
+ setQuote(null);
285
+ setError(false);
286
+ void fetch(`/api/payments/quote?application=${encodeURIComponent(props.applicationId)}`, { method: "POST", cache: "no-store" }).then(async (response) => {
287
+ if (!response.ok) throw new Error("quote unavailable");
288
+ const value = await response.json();
289
+ 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");
290
+ if (live) setQuote(value);
291
+ }).catch(() => {
292
+ if (live) setError(true);
293
+ });
294
+ return () => {
295
+ live = false;
296
+ };
297
+ }, [props.applicationId, revision]);
298
+ const refresh = () => {
299
+ setQuote(null);
300
+ setRevision((value) => value + 1);
301
+ };
302
+ if (!quote || quote.applicationId !== props.applicationId) return /* @__PURE__ */ jsxs3("div", { className: "join-pay", role: "status", children: [
303
+ props.merchantDisclosureText ? /* @__PURE__ */ jsx3("p", { className: "compliance-merchant", children: props.merchantDisclosureText }) : null,
304
+ /* @__PURE__ */ jsx3("p", { children: error ? copy.setupFailed : copy.preparing }),
305
+ error ? /* @__PURE__ */ jsx3("button", { type: "button", onClick: refresh, children: "Reload payment terms" }) : null
306
+ ] });
307
+ const startCheckout = async () => {
308
+ const response = await fetch("/api/payments/subscription", {
309
+ method: "POST",
310
+ headers: { "content-type": "application/json" },
311
+ body: JSON.stringify({ applicationId: props.applicationId, refundPolicyAck: true, quoteDigest: quote.digest })
312
+ });
313
+ if (!response.ok) {
314
+ if (response.status === 409) refresh();
315
+ throw new Error(copy.setupFailed);
316
+ }
317
+ return response.json();
318
+ };
319
+ const amount = (cents) => new Intl.NumberFormat("en-US", { style: "currency", currency: quote.currency }).format(cents / 100);
320
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
321
+ /* @__PURE__ */ jsxs3("section", { className: "payment-quote", "aria-label": "Membership price", children: [
322
+ /* @__PURE__ */ jsx3("h3", { children: quote.tierName }),
323
+ props.renderPriceLines ? props.renderPriceLines(quote.lineItems) : /* @__PURE__ */ jsxs3("dl", { children: [
324
+ /* @__PURE__ */ jsx3("dt", { children: "Membership price" }),
325
+ /* @__PURE__ */ jsxs3("dd", { children: [
326
+ amount(quote.lineItems.standardCents),
327
+ " per ",
328
+ quote.interval
329
+ ] }),
330
+ quote.lineItems.discountCents > 0 ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
331
+ /* @__PURE__ */ jsx3("dt", { children: "Discount" }),
332
+ /* @__PURE__ */ jsx3("dd", { children: amount(quote.lineItems.discountCents) })
333
+ ] }) : null,
334
+ /* @__PURE__ */ jsx3("dt", { children: "Due today" }),
335
+ /* @__PURE__ */ jsx3("dd", { children: amount(quote.lineItems.dueTodayCents) })
336
+ ] }),
337
+ quote.commercial ? /* @__PURE__ */ jsxs3("div", { role: "note", "aria-label": "Renewal terms", children: [
338
+ quote.commercial.initialTermEndsAt !== void 0 ? /* @__PURE__ */ jsxs3("p", { children: [
339
+ "First renewal: ",
340
+ new Intl.DateTimeFormat("en-US", {
341
+ dateStyle: "long",
342
+ timeZone: "UTC"
343
+ }).format(quote.commercial.initialTermEndsAt),
344
+ "."
345
+ ] }) : null,
346
+ /* @__PURE__ */ jsxs3("p", { children: [
347
+ "Renews automatically at ",
348
+ amount(quote.commercial.renewalCents),
349
+ " per ",
350
+ quote.interval,
351
+ " unless canceled before renewal."
352
+ ] }),
353
+ quote.commercial.discountDuration === "first_term" ? /* @__PURE__ */ jsx3("p", { children: "The discount applies to your first paid term only." }) : null,
354
+ quote.commercial.discountDuration === "while_active" ? /* @__PURE__ */ jsx3("p", { children: "The discounted rate continues while this membership stays active." }) : null
355
+ ] }) : null,
356
+ quote.trustCopy ? /* @__PURE__ */ jsx3("p", { className: "pay-trust", children: quote.trustCopy }) : null
357
+ ] }),
358
+ /* @__PURE__ */ jsx3(
359
+ PaymentStep,
360
+ {
361
+ ...props,
362
+ refundPolicyText: quote.refundPolicyText,
363
+ merchantDisclosureText: quote.merchantDisclosureText,
364
+ renderPriceLines: void 0,
365
+ startCheckout
366
+ }
367
+ )
368
+ ] });
369
+ }
370
+
371
+ // src/ui/journey-frame.tsx
372
+ import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
373
+ var styles = `
374
+ .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}
375
+ .chapter-journey *{box-sizing:border-box}
376
+ .chapter-journey :where(h2,h3,p){margin:0 0 1rem}
377
+ .chapter-journey :where(h2,h3){font-family:var(--ui-font-display,inherit);line-height:1.2;letter-spacing:-.025em;text-transform:none}
378
+ .chapter-journey h2{font-size:clamp(1.6rem,3vw,2.15rem)}
379
+ .chapter-journey h3{font-size:1.3rem}
380
+ .chapter-journey :where(.card,.join-form,.payment-quote,.join-book){padding:clamp(1.25rem,4vw,2.4rem);border:1px solid var(--ui-border,#dfe3df);border-radius:var(--ui-radius-lg,16px);background:var(--ui-surface,#fff);margin-block:1.2rem}
381
+ .chapter-journey :where(.card-label,.journey-eyebrow){font-size:.75rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ui-text-muted,#536360);margin-bottom:.7rem;font-weight:600}
382
+ .chapter-journey :where(form,.journey-fields){display:grid;gap:1.25rem}
383
+ .chapter-journey :where(label){display:block;font-size:.95rem;font-weight:500;letter-spacing:normal;text-transform:none;line-height:1.55}
384
+ .chapter-journey :where(input:not([type=checkbox]):not([type=radio]),select,textarea){display:block;width:100%;margin-top:.45rem;padding:.8rem 1rem;border:1px solid var(--ui-border-strong,#aeb9b5);border-radius:8px;background:var(--ui-bg,#fff);color:inherit;font:inherit;min-height:48px}
385
+ .chapter-journey :where(input,textarea,button,a,select):focus-visible{outline:3px solid var(--ui-accent,#3d6a60);outline-offset:3px}
386
+ .chapter-journey :where(input[type=checkbox],input[type=radio]){width:1.15rem;height:1.15rem;margin:.2rem 0 0;flex:none;accent-color:var(--ui-accent,#285c50)}
387
+ .chapter-journey :where(.journey-consent,.compliance-check,.check){display:flex;align-items:flex-start;gap:.8rem;text-align:left;letter-spacing:normal;text-transform:none;font-size:.95rem;font-weight:400}
388
+ .chapter-journey :where(button,.submit-btn,.apply-link){display:inline-flex;align-items:center;justify-content:center;min-height:46px;padding:.75rem 1.2rem;border:1px solid transparent;border-radius:8px;font:inherit;font-size:.95rem;font-weight:600;letter-spacing:normal;line-height:1.35;text-decoration:none;text-transform:none;cursor:pointer;background:var(--ui-accent,#285c50);color:var(--ui-on-accent,#fff)}
389
+ .chapter-journey :where(button.secondary,.journey-secondary){background:transparent;color:var(--ui-text,#202827);border-color:var(--ui-border,#dfe3df)}
390
+ .chapter-journey :is(.slot-time,.slot-day){background:var(--ui-surface,#fff);color:var(--ui-text,#202827);border-color:var(--ui-border,#dfe3df)}
391
+ .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)}
392
+ .chapter-journey :is(.slot-time,.slot-day)[aria-pressed=true]{background:var(--ui-accent,#285c50);color:var(--ui-on-accent,#fff)}
393
+ .chapter-journey button:disabled{opacity:.55;cursor:default}
394
+ .chapter-journey :where(.compliance-box,.journey-note){padding:1.2rem;background:var(--ui-surface-2,#f3f5f2);border-radius:10px;margin-block:1.2rem}
395
+ .chapter-journey :where(.compliance-box p:last-child,.journey-note p:last-child,.card p:last-child){margin-bottom:0}
396
+ .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}
397
+ .chapter-journey .journey-invitation p{margin:0}
398
+ .chapter-journey :where(.journey-error,.join-error,.pay-error,[role=alert]){padding:.9rem 1rem;border:1px solid var(--ui-danger,#9c3636);border-radius:8px;color:var(--ui-danger,#9c3636);font-size:.95rem}
399
+ .chapter-journey :where(.payment-quote dl,.journey-summary){display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.65rem 1rem;margin:1.3rem 0;padding:1rem 0;border-block:1px solid var(--ui-border,#dfe3df)}
400
+ .chapter-journey :where(dd){margin:0;text-align:right;font-variant-numeric:tabular-nums}
401
+ .chapter-journey :where(.pay-status,.meeting-note,.slots-status){color:var(--ui-text-muted,#536360)}
402
+ .chapter-journey .journey-actions{display:flex;gap:.75rem;flex-wrap:wrap;margin-top:1.25rem}
403
+ .chapter-journey fieldset{min-width:0;border:0;padding:0;margin:0}
404
+ @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}}
405
+ `;
406
+ function JourneyFrame({ children }) {
407
+ return /* @__PURE__ */ jsxs4("div", { className: "chapter-journey", children: [
408
+ /* @__PURE__ */ jsx4("style", { children: styles }),
409
+ children
410
+ ] });
411
+ }
412
+
413
+ // src/ui/member-restart.tsx
372
414
  import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
415
+ function MemberRestart(props) {
416
+ const [id, setId] = useState4(props.applicationId);
417
+ const [busy, setBusy] = useState4(false);
418
+ const [error, setError] = useState4("");
419
+ const [paid, setPaid] = useState4(false);
420
+ const [checking, setChecking] = useState4(false);
421
+ const [revision, setRevision] = useState4(0);
422
+ useEffect2(() => {
423
+ if (!paid) return;
424
+ let live = true, timer, attempt = 0;
425
+ setChecking(true);
426
+ const check = async () => {
427
+ try {
428
+ const me = await props.api("/api/me");
429
+ if (!live) return;
430
+ if (me.memberAccess === true) {
431
+ await props.onComplete();
432
+ return;
433
+ }
434
+ } catch {
435
+ }
436
+ if (!live) return;
437
+ if (++attempt < 12) timer = setTimeout(() => void check(), 2500);
438
+ else setChecking(false);
439
+ };
440
+ void check();
441
+ return () => {
442
+ live = false;
443
+ clearTimeout(timer);
444
+ };
445
+ }, [paid, revision]);
446
+ const start = async () => {
447
+ setBusy(true);
448
+ setError("");
449
+ try {
450
+ const result = await props.api("/api/membership/restart", { method: "POST" });
451
+ if (!result.applicationId) throw new Error("We could not prepare your subscription. Please retry.");
452
+ setId(result.applicationId);
453
+ } catch (cause) {
454
+ setError(cause instanceof Error ? cause.message.slice(0, 300) : "We could not prepare your subscription. Please retry.");
455
+ } finally {
456
+ setBusy(false);
457
+ }
458
+ };
459
+ return /* @__PURE__ */ jsx5(JourneyFrame, { children: /* @__PURE__ */ jsxs5("section", { className: "card", "aria-label": "Restart membership", children: [
460
+ /* @__PURE__ */ jsx5("p", { className: "card-label", children: "Welcome back" }),
461
+ /* @__PURE__ */ jsx5("h2", { children: "Restart your membership" }),
462
+ /* @__PURE__ */ jsx5("p", { children: "Your account is ready, and your previous approval still counts. No new application or interview is needed." }),
463
+ /* @__PURE__ */ jsxs5("dl", { className: "journey-summary", children: [
464
+ /* @__PURE__ */ jsx5("dt", { children: "Name" }),
465
+ /* @__PURE__ */ jsx5("dd", { children: [props.application.firstName, props.application.lastName].filter(Boolean).join(" ") }),
466
+ /* @__PURE__ */ jsx5("dt", { children: "Email" }),
467
+ /* @__PURE__ */ jsx5("dd", { children: props.application.email })
468
+ ] }),
469
+ paid ? /* @__PURE__ */ jsxs5("div", { role: "status", children: [
470
+ /* @__PURE__ */ jsx5("h3", { children: "Confirming your membership" }),
471
+ /* @__PURE__ */ jsx5("p", { children: "We\u2019re waiting for payment confirmation. Your membership will open here as soon as it arrives." }),
472
+ !checking ? /* @__PURE__ */ jsx5("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Check membership" }) : null
473
+ ] }) : id ? /* @__PURE__ */ jsx5(MembershipPaymentStep, { applicationId: id, refundPolicyText: "", onPaid: () => setPaid(true) }) : /* @__PURE__ */ jsx5("button", { type: "button", disabled: busy, onClick: () => void start(), children: busy ? "Preparing your membership\u2026" : "Review membership and payment" }),
474
+ error ? /* @__PURE__ */ jsx5("p", { role: "alert", children: error }) : null
475
+ ] }) });
476
+ }
477
+
478
+ // src/ui/members.tsx
479
+ import { useEffect as useEffect6, useState as useState9 } from "preact/hooks";
480
+
481
+ // src/ui/reschedule.tsx
482
+ import { useState as useState5 } from "preact/hooks";
483
+ import { jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
484
+ function Rescheduler(props) {
485
+ const inheritedCopy = useChapterCopy();
486
+ const copy = props.copy ?? inheritedCopy.members.reschedule;
487
+ const { api, applicationId, timezone, onRescheduled, label = copy.changeTime } = props;
488
+ const [open, setOpen] = useState5(false);
489
+ const [slots, setSlots] = useState5(null);
490
+ const [tz, setTz] = useState5(timezone);
491
+ const [busy, setBusy] = useState5(false);
492
+ const [msg, setMsg] = useState5(null);
493
+ const start = async () => {
494
+ setOpen(true);
495
+ setSlots(null);
496
+ setMsg(null);
497
+ try {
498
+ const r = await api("/api/schedule/slots");
499
+ if (r.schedulingReady === false) {
500
+ setSlots([]);
501
+ setMsg(copy.unavailable);
502
+ return;
503
+ }
504
+ setSlots(r.slots ?? []);
505
+ if (r.timezone) setTz(r.timezone);
506
+ } catch {
507
+ setSlots([]);
508
+ setMsg(copy.loadFailed);
509
+ }
510
+ };
511
+ const pick = async (slot) => {
512
+ setBusy(true);
513
+ setMsg(null);
514
+ try {
515
+ await api("/api/schedule/book", { method: "POST", body: JSON.stringify({ applicationId, startAt: slot.startAt }) });
516
+ setOpen(false);
517
+ await onRescheduled();
518
+ } catch (e) {
519
+ setMsg(e instanceof Error ? e.message : copy.slotGone);
520
+ } finally {
521
+ setBusy(false);
522
+ }
523
+ };
524
+ if (!open) {
525
+ return /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: /* @__PURE__ */ jsx6(
526
+ "a",
527
+ {
528
+ href: "#",
529
+ onClick: (e) => {
530
+ e.preventDefault();
531
+ void start();
532
+ },
533
+ children: label
534
+ }
535
+ ) });
536
+ }
537
+ return /* @__PURE__ */ jsxs6("div", { className: "msched", children: [
538
+ slots === null ? /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: copy.loading }) : slots.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: msg ?? copy.noTimes }) : /* @__PURE__ */ jsx6(
539
+ SlotPicker,
540
+ {
541
+ slots,
542
+ timezone: tz,
543
+ classes: { days: "msched-days", day: "msched-day", times: "msched-times", time: "msched-time" },
544
+ onPick: (s) => void pick(s)
545
+ }
546
+ ),
547
+ busy ? /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: copy.rescheduling }) : null,
548
+ msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx6("p", { className: "meeting-note error", children: msg }) : null,
549
+ /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: /* @__PURE__ */ jsx6(
550
+ "a",
551
+ {
552
+ href: "#",
553
+ onClick: (e) => {
554
+ e.preventDefault();
555
+ setOpen(false);
556
+ },
557
+ children: copy.keepTime
558
+ }
559
+ ) })
560
+ ] });
561
+ }
562
+
563
+ // src/ui/member-gifts.tsx
564
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState6 } from "preact/hooks";
565
+
566
+ // src/ui/gift-offer-cards.tsx
567
+ import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
568
+ var price = (cents, currency) => currency === "usd" ? fmtMoney(cents) : new Intl.NumberFormat(void 0, { style: "currency", currency: currency.toUpperCase() }).format(cents / 100);
569
+ function GiftOfferCards(props) {
570
+ const mode = props.mode ?? "preview";
571
+ return /* @__PURE__ */ jsx7("section", { className: "chapter-gift-offers", "aria-label": props.ariaLabel ?? "Gift options", children: props.offers.map((offer) => /* @__PURE__ */ jsxs7("article", { className: "card chapter-gift-offer", "data-offer-id": offer.id, children: [
572
+ /* @__PURE__ */ jsx7("div", { className: "card-label", children: offer.title }),
573
+ /* @__PURE__ */ jsx7("p", { children: offer.description }),
574
+ /* @__PURE__ */ jsxs7("p", { className: "meeting-date", children: [
575
+ price(offer.priceCents, offer.currency),
576
+ offer.billingMode === "subscription" ? " recurring" : ""
577
+ ] }),
578
+ mode === "select" ? /* @__PURE__ */ jsx7("button", { type: "button", className: "btn secondary", "aria-pressed": props.selectedOfferId === offer.id, onClick: () => props.onSelect?.(offer.id), children: offer.ctaLabel }) : /* @__PURE__ */ jsx7("span", { className: "btn secondary", "aria-disabled": "true", children: offer.ctaLabel })
579
+ ] }, offer.id)) });
580
+ }
581
+
582
+ // src/ui/member-gifts.tsx
583
+ import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
373
584
  function GiftPurchases(props) {
374
- const [data, setData] = useState4(null);
375
- const [selected, setSelected] = useState4(null);
376
- const [code, setCode] = useState4("");
377
- const [message, setMessage] = useState4("");
585
+ const [data, setData] = useState6(null);
586
+ const [selected, setSelected] = useState6(null);
587
+ const [code, setCode] = useState6("");
588
+ const [message, setMessage] = useState6("");
378
589
  const purchaseId = useRef2(crypto.randomUUID());
379
590
  const load = async () => setData(await props.api("/api/gifts"));
380
- useEffect(() => {
591
+ useEffect3(() => {
381
592
  void load().catch(() => setData({ offers: [], claims: [] }));
382
593
  }, []);
383
594
  const rotate = async (claimId) => {
@@ -392,16 +603,16 @@ function GiftPurchases(props) {
392
603
  if (!data) return null;
393
604
  if (data.offers.length === 0 && data.claims.length === 0) return null;
394
605
  const offer = data.offers.find((item) => item.id === selected);
395
- return /* @__PURE__ */ jsxs5("section", { className: "member-gifts", children: [
396
- data.offers.length ? /* @__PURE__ */ jsxs5("div", { className: "card", children: [
397
- /* @__PURE__ */ jsx5("div", { className: "card-label", children: props.copy.title }),
398
- /* @__PURE__ */ jsx5("p", { children: props.copy.body }),
399
- /* @__PURE__ */ jsx5(GiftOfferCards, { offers: data.offers, mode: "select", selectedOfferId: selected, onSelect: (id) => {
606
+ return /* @__PURE__ */ jsxs8("section", { className: "member-gifts", children: [
607
+ data.offers.length ? /* @__PURE__ */ jsxs8("div", { className: "card", children: [
608
+ /* @__PURE__ */ jsx8("div", { className: "card-label", children: props.copy.title }),
609
+ /* @__PURE__ */ jsx8("p", { children: props.copy.body }),
610
+ /* @__PURE__ */ jsx8(GiftOfferCards, { offers: data.offers, mode: "select", selectedOfferId: selected, onSelect: (id) => {
400
611
  setSelected(id);
401
612
  setCode("");
402
613
  purchaseId.current = crypto.randomUUID();
403
614
  } }),
404
- offer ? /* @__PURE__ */ jsx5(
615
+ offer ? /* @__PURE__ */ jsx8(
405
616
  PaymentStep,
406
617
  {
407
618
  applicationId: purchaseId.current,
@@ -421,31 +632,31 @@ function GiftPurchases(props) {
421
632
  }
422
633
  }
423
634
  ) : null,
424
- code ? /* @__PURE__ */ jsxs5("div", { className: "gift-code-once", children: [
425
- /* @__PURE__ */ jsx5("output", { children: code }),
426
- /* @__PURE__ */ jsx5("p", { children: props.copy.saveCode })
635
+ code ? /* @__PURE__ */ jsxs8("div", { className: "gift-code-once", children: [
636
+ /* @__PURE__ */ jsx8("output", { children: code }),
637
+ /* @__PURE__ */ jsx8("p", { children: props.copy.saveCode })
427
638
  ] }) : null
428
639
  ] }) : null,
429
- /* @__PURE__ */ jsxs5("div", { className: "card", children: [
430
- /* @__PURE__ */ jsx5("div", { className: "card-label", children: props.copy.purchases }),
431
- data.claims.length ? data.claims.map((claim) => /* @__PURE__ */ jsxs5("div", { className: "member-row", children: [
432
- /* @__PURE__ */ jsx5("span", { children: claim.offerId }),
433
- /* @__PURE__ */ jsx5("span", { children: props.copy[claim.status] }),
434
- claim.status === "pending" ? /* @__PURE__ */ jsx5("button", { className: "btn secondary mini", onClick: () => void rotate(claim.id), children: props.copy.rotate }) : null
435
- ] }, claim.id)) : /* @__PURE__ */ jsx5("p", { children: props.copy.noPurchases }),
436
- message ? /* @__PURE__ */ jsx5("p", { role: "status", children: message }) : null
640
+ /* @__PURE__ */ jsxs8("div", { className: "card", children: [
641
+ /* @__PURE__ */ jsx8("div", { className: "card-label", children: props.copy.purchases }),
642
+ data.claims.length ? data.claims.map((claim) => /* @__PURE__ */ jsxs8("div", { className: "member-row", children: [
643
+ /* @__PURE__ */ jsx8("span", { children: claim.offerId }),
644
+ /* @__PURE__ */ jsx8("span", { children: props.copy[claim.status] }),
645
+ claim.status === "pending" ? /* @__PURE__ */ jsx8("button", { className: "btn secondary mini", onClick: () => void rotate(claim.id), children: props.copy.rotate }) : null
646
+ ] }, claim.id)) : /* @__PURE__ */ jsx8("p", { children: props.copy.noPurchases }),
647
+ message ? /* @__PURE__ */ jsx8("p", { role: "status", children: message }) : null
437
648
  ] })
438
649
  ] });
439
650
  }
440
651
  var MemberGifts = GiftPurchases;
441
652
 
442
653
  // src/ui/member-named-seat.tsx
443
- import { useEffect as useEffect2, useState as useState5 } from "preact/hooks";
444
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
654
+ import { useEffect as useEffect4, useState as useState7 } from "preact/hooks";
655
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
445
656
  function NamedSeatPurchase({ api }) {
446
- const [data, setData] = useState5(null), [error, setError] = useState5("");
447
- const [name, setName] = useState5(""), [email, setEmail] = useState5(""), [reviewing, setReviewing] = useState5(false);
448
- const [terms, setTerms] = useState5(false), [pending, setPending] = useState5(false);
657
+ const [data, setData] = useState7(null), [error, setError] = useState7("");
658
+ const [name, setName] = useState7(""), [email, setEmail] = useState7(""), [reviewing, setReviewing] = useState7(false);
659
+ const [terms, setTerms] = useState7(false), [pending, setPending] = useState7(false);
449
660
  const load = async () => {
450
661
  try {
451
662
  const value = await api("/api/named-seat");
@@ -459,23 +670,23 @@ function NamedSeatPurchase({ api }) {
459
670
  setError("The named-seat service is unavailable. Refresh to check your purchase before trying again.");
460
671
  }
461
672
  };
462
- useEffect2(() => {
673
+ useEffect4(() => {
463
674
  void load();
464
675
  }, []);
465
676
  const money = data ? new Intl.NumberFormat("en-US", { style: "currency", currency: data.currency }).format(data.amountCents / 100) : "";
466
- return /* @__PURE__ */ jsxs6("section", { className: "card member-named-seat", "aria-label": "Named additional seat", children: [
467
- /* @__PURE__ */ jsx6("h2", { children: "Named additional seat" }),
468
- error ? /* @__PURE__ */ jsx6("p", { role: "alert", children: error }) : null,
469
- data ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
470
- data.seat ? /* @__PURE__ */ jsxs6("p", { children: [
677
+ return /* @__PURE__ */ jsx9(JourneyFrame, { children: /* @__PURE__ */ jsxs9("section", { className: "card member-named-seat", "aria-label": "Named additional seat", children: [
678
+ /* @__PURE__ */ jsx9("h2", { children: "Named additional seat" }),
679
+ error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: error }) : null,
680
+ data ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
681
+ data.seat ? /* @__PURE__ */ jsxs9("p", { children: [
471
682
  data.seat.recipientName,
472
683
  " \xB7 ",
473
684
  data.seat.recipientEmail,
474
685
  " \xB7 ",
475
686
  data.seat.status.replaceAll("_", " ")
476
687
  ] }) : null,
477
- data.eligible ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
478
- /* @__PURE__ */ jsxs6("p", { children: [
688
+ data.eligible ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
689
+ /* @__PURE__ */ jsxs9("p", { children: [
479
690
  money,
480
691
  " paid in full now. This seat ",
481
692
  data.autoRenew ? "renews with your primary membership" : "ends with your primary membership",
@@ -486,38 +697,38 @@ function NamedSeatPurchase({ api }) {
486
697
  " ",
487
698
  "It belongs to one named person and cannot be reassigned."
488
699
  ] }),
489
- /* @__PURE__ */ jsx6("p", { children: "The recipient must accept and complete their own application and any required interview. An unaccepted seat is fully refunded after 30 days." }),
490
- !reviewing ? /* @__PURE__ */ jsxs6("form", { onSubmit: (event) => {
700
+ /* @__PURE__ */ jsx9("p", { children: "The recipient must accept and complete their own application and any required interview. An unaccepted seat is fully refunded after 30 days." }),
701
+ !reviewing ? /* @__PURE__ */ jsxs9("form", { onSubmit: (event) => {
491
702
  event.preventDefault();
492
703
  setReviewing(true);
493
704
  setTerms(false);
494
705
  }, children: [
495
- /* @__PURE__ */ jsxs6("label", { children: [
706
+ /* @__PURE__ */ jsxs9("label", { children: [
496
707
  "Recipient name",
497
- /* @__PURE__ */ jsx6("input", { required: true, maxLength: 160, value: name, disabled: Boolean(data.seat), onInput: (event) => setName(event.currentTarget.value) })
708
+ /* @__PURE__ */ jsx9("input", { required: true, maxLength: 160, value: name, disabled: Boolean(data.seat), onInput: (event) => setName(event.currentTarget.value) })
498
709
  ] }),
499
- /* @__PURE__ */ jsxs6("label", { children: [
710
+ /* @__PURE__ */ jsxs9("label", { children: [
500
711
  "Recipient email",
501
- /* @__PURE__ */ jsx6("input", { required: true, type: "email", maxLength: 254, value: email, disabled: Boolean(data.seat), onInput: (event) => setEmail(event.currentTarget.value) })
712
+ /* @__PURE__ */ jsx9("input", { required: true, type: "email", maxLength: 254, value: email, disabled: Boolean(data.seat), onInput: (event) => setEmail(event.currentTarget.value) })
502
713
  ] }),
503
- /* @__PURE__ */ jsx6("button", { className: "btn primary", type: "submit", children: "Review named seat" })
504
- ] }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
505
- /* @__PURE__ */ jsxs6("p", { children: [
714
+ /* @__PURE__ */ jsx9("button", { className: "btn primary", type: "submit", children: "Review named seat" })
715
+ ] }) : /* @__PURE__ */ jsxs9(Fragment3, { children: [
716
+ /* @__PURE__ */ jsxs9("p", { children: [
506
717
  "For ",
507
718
  name,
508
719
  " \xB7 ",
509
720
  email
510
721
  ] }),
511
- /* @__PURE__ */ jsx6("button", { className: "btn secondary", type: "button", onClick: () => {
722
+ /* @__PURE__ */ jsx9("button", { className: "btn secondary", type: "button", onClick: () => {
512
723
  setReviewing(false);
513
724
  setTerms(false);
514
725
  void load();
515
726
  }, children: "Back to recipient" }),
516
- /* @__PURE__ */ jsxs6("label", { children: [
517
- /* @__PURE__ */ jsx6("input", { type: "checkbox", checked: terms, onChange: (event) => setTerms(event.currentTarget.checked) }),
518
- 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."
727
+ /* @__PURE__ */ jsxs9("label", { className: "journey-consent", children: [
728
+ /* @__PURE__ */ jsx9("input", { type: "checkbox", checked: terms, onChange: (event) => setTerms(event.currentTarget.checked) }),
729
+ /* @__PURE__ */ jsx9("span", { children: 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." })
519
730
  ] }),
520
- terms ? /* @__PURE__ */ jsx6(
731
+ terms ? /* @__PURE__ */ jsx9(
521
732
  PaymentStep,
522
733
  {
523
734
  applicationId: data.membershipId,
@@ -536,17 +747,17 @@ function NamedSeatPurchase({ api }) {
536
747
  }
537
748
  ) : null
538
749
  ] })
539
- ] }) : !data.seat ? /* @__PURE__ */ jsx6("p", { children: "Additional seats are offered only during the first three months of a paid membership." }) : null,
540
- pending ? /* @__PURE__ */ jsx6("p", { role: "status", children: "Payment confirmation is being reconciled. The recipient\u2019s access begins only after their own acceptance and approval." }) : null
541
- ] }) : !error ? /* @__PURE__ */ jsx6("p", { children: "Loading your seat terms\u2026" }) : null,
542
- /* @__PURE__ */ jsx6("button", { className: "btn secondary", onClick: () => void load(), children: "Refresh seat status" })
543
- ] });
750
+ ] }) : !data.seat ? /* @__PURE__ */ jsx9("p", { children: "Additional seats are offered only during the first three months of a paid membership." }) : null,
751
+ pending ? /* @__PURE__ */ jsx9("p", { role: "status", children: "Payment confirmation is being reconciled. The recipient\u2019s access begins only after their own acceptance and approval." }) : null
752
+ ] }) : !error ? /* @__PURE__ */ jsx9("p", { children: "Loading your seat terms\u2026" }) : null,
753
+ /* @__PURE__ */ jsx9("button", { className: "btn secondary", onClick: () => void load(), children: "Refresh seat status" })
754
+ ] }) });
544
755
  }
545
756
 
546
757
  // src/ui/member-content.tsx
547
- import { useEffect as useEffect3, useMemo as useMemo2, useState as useState6 } from "preact/hooks";
758
+ import { useEffect as useEffect5, useMemo as useMemo2, useState as useState8 } from "preact/hooks";
548
759
  import { Markdown } from "@odla-ai/ui/components";
549
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
760
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
550
761
  var pageLabel = (page) => `${page.title}${page.access === "locked" ? " \xB7 Locked" : ""}`;
551
762
  function MemberContentArea(props) {
552
763
  const {
@@ -554,15 +765,15 @@ function MemberContentArea(props) {
554
765
  path = "/api/member-content",
555
766
  upsellHref,
556
767
  onUpsell,
557
- loadingContent = /* @__PURE__ */ jsx7("p", { className: "meeting-note", children: "Loading member pages\u2026" }),
558
- emptyContent = /* @__PURE__ */ jsx7("p", { className: "meeting-note", children: "No member pages have been published yet." })
768
+ loadingContent = /* @__PURE__ */ jsx10("p", { className: "meeting-note", children: "Loading member pages\u2026" }),
769
+ emptyContent = /* @__PURE__ */ jsx10("p", { className: "meeting-note", children: "No member pages have been published yet." })
559
770
  } = props;
560
- const [data, setData] = useState6(props.initialData ?? null);
561
- const [error, setError] = useState6(null);
562
- const [selectedId, setSelectedId] = useState6(
771
+ const [data, setData] = useState8(props.initialData ?? null);
772
+ const [error, setError] = useState8(null);
773
+ const [selectedId, setSelectedId] = useState8(
563
774
  props.initialPageId ?? props.initialData?.pages[0]?.id ?? ""
564
775
  );
565
- useEffect3(() => {
776
+ useEffect5(() => {
566
777
  if (props.initialData) return;
567
778
  let live = true;
568
779
  setError(null);
@@ -584,18 +795,18 @@ function MemberContentArea(props) {
584
795
  }
585
796
  return [...result.entries()];
586
797
  }, [data]);
587
- if (error) return /* @__PURE__ */ jsxs7("p", { className: "meeting-note", role: "alert", children: [
798
+ if (error) return /* @__PURE__ */ jsxs10("p", { className: "meeting-note", role: "alert", children: [
588
799
  "Member pages could not be loaded: ",
589
800
  error
590
801
  ] });
591
- if (!data) return /* @__PURE__ */ jsx7(Fragment3, { children: loadingContent });
592
- if (data.pages.length === 0) return /* @__PURE__ */ jsx7(Fragment3, { children: emptyContent });
802
+ if (!data) return /* @__PURE__ */ jsx10(Fragment4, { children: loadingContent });
803
+ if (data.pages.length === 0) return /* @__PURE__ */ jsx10(Fragment4, { children: emptyContent });
593
804
  const selected = data.pages.find((page) => page.id === selectedId) ?? data.pages[0];
594
805
  const href = selected.upsell && upsellHref ? upsellHref(selected.upsell.tierId, selected) : void 0;
595
- return /* @__PURE__ */ jsxs7("div", { className: "chapter-member-content", "data-member-content": true, children: [
596
- /* @__PURE__ */ jsx7("nav", { className: "chapter-member-content-nav", "aria-label": "Member pages", children: sections.map(([section, pages]) => /* @__PURE__ */ jsxs7("section", { className: "chapter-member-content-section", children: [
597
- /* @__PURE__ */ jsx7("h2", { children: section }),
598
- /* @__PURE__ */ jsx7("ul", { children: pages.map((page) => /* @__PURE__ */ jsx7("li", { "data-parent-page": page.navigation.parentId ?? void 0, children: /* @__PURE__ */ jsx7(
806
+ return /* @__PURE__ */ jsxs10("div", { className: "chapter-member-content", "data-member-content": true, children: [
807
+ /* @__PURE__ */ jsx10("nav", { className: "chapter-member-content-nav", "aria-label": "Member pages", children: sections.map(([section, pages]) => /* @__PURE__ */ jsxs10("section", { className: "chapter-member-content-section", children: [
808
+ /* @__PURE__ */ jsx10("h2", { children: section }),
809
+ /* @__PURE__ */ jsx10("ul", { children: pages.map((page) => /* @__PURE__ */ jsx10("li", { "data-parent-page": page.navigation.parentId ?? void 0, children: /* @__PURE__ */ jsx10(
599
810
  "button",
600
811
  {
601
812
  type: "button",
@@ -605,23 +816,23 @@ function MemberContentArea(props) {
605
816
  }
606
817
  ) }, page.id)) })
607
818
  ] }, section)) }),
608
- /* @__PURE__ */ jsxs7("article", { className: "chapter-member-content-page", "data-access": selected.access, children: [
609
- /* @__PURE__ */ jsxs7("header", { children: [
610
- /* @__PURE__ */ jsxs7("div", { className: "card-label", children: [
819
+ /* @__PURE__ */ jsxs10("article", { className: "chapter-member-content-page", "data-access": selected.access, children: [
820
+ /* @__PURE__ */ jsxs10("header", { children: [
821
+ /* @__PURE__ */ jsxs10("div", { className: "card-label", children: [
611
822
  selected.source.type === "parent" ? "Parent" : "Chapter",
612
823
  " \xB7 ",
613
824
  selected.access
614
825
  ] }),
615
- /* @__PURE__ */ jsx7("h1", { children: selected.title }),
616
- selected.summary ? /* @__PURE__ */ jsx7("p", { children: selected.summary }) : null
826
+ /* @__PURE__ */ jsx10("h1", { children: selected.title }),
827
+ selected.summary ? /* @__PURE__ */ jsx10("p", { children: selected.summary }) : null
617
828
  ] }),
618
- selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx7(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs7("div", { className: "chapter-member-content-locked", children: [
619
- selected.teaserMarkdown ? /* @__PURE__ */ jsx7(Markdown, { source: selected.teaserMarkdown }) : /* @__PURE__ */ jsx7("p", { children: "This page is not included in your current membership." }),
620
- selected.upsell ? /* @__PURE__ */ jsxs7("aside", { className: "chapter-member-content-upsell", "aria-label": selected.upsell.label ?? "Membership option", children: [
621
- /* @__PURE__ */ jsx7("h2", { children: selected.upsell.label ?? selected.upsell.tierName }),
622
- /* @__PURE__ */ jsx7("p", { children: fmtMoney(selected.upsell.priceCents) }),
623
- selected.upsell.bodyMarkdown ? /* @__PURE__ */ jsx7(Markdown, { source: selected.upsell.bodyMarkdown }) : null,
624
- href ? /* @__PURE__ */ jsx7("a", { className: "btn primary", href, children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : onUpsell ? /* @__PURE__ */ jsx7("button", { className: "btn primary", type: "button", onClick: () => onUpsell(selected.upsell.tierId, selected), children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : null
829
+ selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx10(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs10("div", { className: "chapter-member-content-locked", children: [
830
+ selected.teaserMarkdown ? /* @__PURE__ */ jsx10(Markdown, { source: selected.teaserMarkdown }) : /* @__PURE__ */ jsx10("p", { children: "This page is not included in your current membership." }),
831
+ selected.upsell ? /* @__PURE__ */ jsxs10("aside", { className: "chapter-member-content-upsell", "aria-label": selected.upsell.label ?? "Membership option", children: [
832
+ /* @__PURE__ */ jsx10("h2", { children: selected.upsell.label ?? selected.upsell.tierName }),
833
+ /* @__PURE__ */ jsx10("p", { children: fmtMoney(selected.upsell.priceCents) }),
834
+ selected.upsell.bodyMarkdown ? /* @__PURE__ */ jsx10(Markdown, { source: selected.upsell.bodyMarkdown }) : null,
835
+ href ? /* @__PURE__ */ jsx10("a", { className: "btn primary", href, children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : onUpsell ? /* @__PURE__ */ jsx10("button", { className: "btn primary", type: "button", onClick: () => onUpsell(selected.upsell.tierId, selected), children: selected.upsell.ctaLabel ?? `Explore ${selected.upsell.tierName}` }) : null
625
836
  ] }) : null
626
837
  ] })
627
838
  ] })
@@ -629,12 +840,12 @@ function MemberContentArea(props) {
629
840
  }
630
841
 
631
842
  // src/ui/members.tsx
632
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
843
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
633
844
  function card(copy, kicker, body) {
634
- return /* @__PURE__ */ jsxs8("div", { className: "card", children: [
635
- /* @__PURE__ */ jsx8("div", { className: "card-label", children: copy.provisional.cardLabel }),
636
- /* @__PURE__ */ jsxs8("div", { className: "meeting-block", children: [
637
- /* @__PURE__ */ jsx8("div", { className: "meeting-kicker", children: kicker }),
845
+ return /* @__PURE__ */ jsxs11("div", { className: "card", children: [
846
+ /* @__PURE__ */ jsx11("div", { className: "card-label", children: copy.provisional.cardLabel }),
847
+ /* @__PURE__ */ jsxs11("div", { className: "meeting-block", children: [
848
+ /* @__PURE__ */ jsx11("div", { className: "meeting-kicker", children: kicker }),
638
849
  body
639
850
  ] })
640
851
  ] });
@@ -645,9 +856,9 @@ function ProvisionalCard(props) {
645
856
  return card(
646
857
  copy,
647
858
  copy.provisional.applicationNeeded,
648
- /* @__PURE__ */ jsxs8(Fragment4, { children: [
649
- /* @__PURE__ */ jsx8("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
650
- /* @__PURE__ */ jsx8("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
859
+ /* @__PURE__ */ jsxs11(Fragment5, { children: [
860
+ /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
861
+ /* @__PURE__ */ jsx11("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
651
862
  ] })
652
863
  );
653
864
  }
@@ -655,19 +866,19 @@ function ProvisionalCard(props) {
655
866
  return card(
656
867
  copy,
657
868
  copy.provisional.refunded,
658
- /* @__PURE__ */ jsx8("div", { className: "meeting-note", children: copy.provisional.refundedBody })
869
+ /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: copy.provisional.refundedBody })
659
870
  );
660
871
  }
661
- const membership = application.paid ? /* @__PURE__ */ jsx8("div", { className: "meeting-note", children: application.renewalAt ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) }) : copy.provisional.active }) : null;
872
+ const membership = application.paid ? /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: application.renewalAt ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) }) : copy.provisional.active }) : null;
662
873
  if (application.meetingAt) {
663
874
  return card(
664
875
  copy,
665
876
  copy.provisional.introductionCall,
666
- /* @__PURE__ */ jsxs8(Fragment4, { children: [
667
- /* @__PURE__ */ jsx8("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
668
- /* @__PURE__ */ jsx8("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
669
- application.meetUrl ? /* @__PURE__ */ jsx8("div", { className: "meeting-note", children: /* @__PURE__ */ jsx8("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
670
- /* @__PURE__ */ jsx8(
877
+ /* @__PURE__ */ jsxs11(Fragment5, { children: [
878
+ /* @__PURE__ */ jsx11("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
879
+ /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
880
+ application.meetUrl ? /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: /* @__PURE__ */ jsx11("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
881
+ /* @__PURE__ */ jsx11(
671
882
  Rescheduler,
672
883
  {
673
884
  api,
@@ -684,9 +895,9 @@ function ProvisionalCard(props) {
684
895
  return card(
685
896
  copy,
686
897
  copy.provisional.bookCall,
687
- /* @__PURE__ */ jsxs8(Fragment4, { children: [
688
- /* @__PURE__ */ jsx8("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
689
- /* @__PURE__ */ jsx8(
898
+ /* @__PURE__ */ jsxs11(Fragment5, { children: [
899
+ /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
900
+ /* @__PURE__ */ jsx11(
690
901
  Rescheduler,
691
902
  {
692
903
  api,
@@ -713,8 +924,8 @@ function MembersArea(props) {
713
924
  renderProvisional
714
925
  } = props;
715
926
  const copy = props.copy ?? inheritedCopy.members;
716
- const [me, setMe] = useState7(null);
717
- const [error, setError] = useState7(false);
927
+ const [me, setMe] = useState9(null);
928
+ const [error, setError] = useState9(false);
718
929
  const reload = async () => {
719
930
  try {
720
931
  setMe(await api("/api/me"));
@@ -722,25 +933,25 @@ function MembersArea(props) {
722
933
  setError(true);
723
934
  }
724
935
  };
725
- useEffect4(() => {
936
+ useEffect6(() => {
726
937
  void reload();
727
938
  }, []);
728
- if (error) return /* @__PURE__ */ jsx8("p", { className: "meeting-note", children: copy.loadFailed });
729
- if (!me) return /* @__PURE__ */ jsx8("p", { className: "meeting-note", children: inheritedCopy.common.loading });
939
+ if (error) return /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: copy.loadFailed });
940
+ if (!me) return /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: inheritedCopy.common.loading });
730
941
  const role = me.role || "provisional";
731
- return /* @__PURE__ */ jsxs8(Fragment4, { children: [
732
- /* @__PURE__ */ jsxs8("div", { className: "card", children: [
733
- /* @__PURE__ */ jsxs8("div", { className: "member-row", children: [
734
- /* @__PURE__ */ jsx8("span", { className: "member-email", children: me.email }),
735
- /* @__PURE__ */ jsx8("span", { className: "role-badge " + role, children: role })
942
+ return /* @__PURE__ */ jsxs11(Fragment5, { children: [
943
+ /* @__PURE__ */ jsxs11("div", { className: "card", children: [
944
+ /* @__PURE__ */ jsxs11("div", { className: "member-row", children: [
945
+ /* @__PURE__ */ jsx11("span", { className: "member-email", children: me.email }),
946
+ /* @__PURE__ */ jsx11("span", { className: "role-badge " + role, children: role })
736
947
  ] }),
737
- /* @__PURE__ */ jsxs8("div", { className: "account-actions", children: [
738
- /* @__PURE__ */ jsx8("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
739
- me.authorized || me.superAdmin ? /* @__PURE__ */ jsx8("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
948
+ /* @__PURE__ */ jsxs11("div", { className: "account-actions", children: [
949
+ /* @__PURE__ */ jsx11("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
950
+ me.authorized || me.superAdmin ? /* @__PURE__ */ jsx11("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
740
951
  ] })
741
952
  ] }),
742
- me.memberAccess !== true ? (() => {
743
- const defaultContent = /* @__PURE__ */ jsx8(
953
+ me.memberAccess !== true && me.membershipRestart?.eligible && me.application ? /* @__PURE__ */ jsx11(MemberRestart, { api, application: me.application, applicationId: me.membershipRestart.applicationId, onComplete: reload }) : me.memberAccess !== true ? (() => {
954
+ const defaultContent = /* @__PURE__ */ jsx11(
744
955
  ProvisionalCard,
745
956
  {
746
957
  api,
@@ -757,128 +968,30 @@ function MembersArea(props) {
757
968
  reload,
758
969
  defaultContent
759
970
  }) ?? defaultContent;
760
- })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx8(
971
+ })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx11(
761
972
  MemberContentArea,
762
973
  {
763
974
  api,
764
975
  ...memberPages === true ? {} : memberPages
765
976
  }
766
- ) : /* @__PURE__ */ jsx8("div", { className: "card", children: /* @__PURE__ */ jsx8("div", { className: "card-label", children: copy.full.welcome }) })),
767
- /* @__PURE__ */ jsx8(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
768
- me.namedSeatsEnabled ? /* @__PURE__ */ jsx8(NamedSeatPurchase, { api }) : null
977
+ ) : /* @__PURE__ */ jsx11("div", { className: "card", children: /* @__PURE__ */ jsx11("div", { className: "card-label", children: copy.full.welcome }) })),
978
+ /* @__PURE__ */ jsx11(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
979
+ me.namedSeatsEnabled ? /* @__PURE__ */ jsx11(NamedSeatPurchase, { api }) : null
769
980
  ] });
770
981
  }
771
982
 
772
983
  // src/ui/join.tsx
773
- import { useEffect as useEffect8, useRef as useRef3, useState as useState14 } from "preact/hooks";
774
-
775
- // src/ui/membership-payment-step.tsx
776
- import { useEffect as useEffect5, useState as useState8 } from "preact/hooks";
777
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
778
- function MembershipPaymentStep(props) {
779
- const inherited = useChapterCopy();
780
- const copy = props.copy ?? inherited.join.payment;
781
- const [quote, setQuote] = useState8(null);
782
- const [error, setError] = useState8(false);
783
- const [revision, setRevision] = useState8(0);
784
- useEffect5(() => {
785
- let live = true;
786
- setQuote(null);
787
- setError(false);
788
- void fetch(`/api/payments/quote?application=${encodeURIComponent(props.applicationId)}`, { method: "POST", cache: "no-store" }).then(async (response) => {
789
- if (!response.ok) throw new Error("quote unavailable");
790
- const value = await response.json();
791
- 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");
792
- if (live) setQuote(value);
793
- }).catch(() => {
794
- if (live) setError(true);
795
- });
796
- return () => {
797
- live = false;
798
- };
799
- }, [props.applicationId, revision]);
800
- const refresh = () => {
801
- setQuote(null);
802
- setRevision((value) => value + 1);
803
- };
804
- if (!quote || quote.applicationId !== props.applicationId) return /* @__PURE__ */ jsxs9("div", { className: "join-pay", role: "status", children: [
805
- props.merchantDisclosureText ? /* @__PURE__ */ jsx9("p", { className: "compliance-merchant", children: props.merchantDisclosureText }) : null,
806
- /* @__PURE__ */ jsx9("p", { children: error ? copy.setupFailed : copy.preparing }),
807
- error ? /* @__PURE__ */ jsx9("button", { type: "button", onClick: refresh, children: "Reload payment terms" }) : null
808
- ] });
809
- const startCheckout = async () => {
810
- const response = await fetch("/api/payments/subscription", {
811
- method: "POST",
812
- headers: { "content-type": "application/json" },
813
- body: JSON.stringify({ applicationId: props.applicationId, refundPolicyAck: true, quoteDigest: quote.digest })
814
- });
815
- if (!response.ok) {
816
- if (response.status === 409) refresh();
817
- throw new Error(copy.setupFailed);
818
- }
819
- return response.json();
820
- };
821
- const amount = (cents) => new Intl.NumberFormat("en-US", { style: "currency", currency: quote.currency }).format(cents / 100);
822
- return /* @__PURE__ */ jsxs9(Fragment5, { children: [
823
- /* @__PURE__ */ jsxs9("section", { className: "payment-quote", "aria-label": "Membership price", children: [
824
- /* @__PURE__ */ jsx9("h3", { children: quote.tierName }),
825
- props.renderPriceLines ? props.renderPriceLines(quote.lineItems) : /* @__PURE__ */ jsxs9("dl", { children: [
826
- /* @__PURE__ */ jsx9("dt", { children: "Membership price" }),
827
- /* @__PURE__ */ jsxs9("dd", { children: [
828
- amount(quote.lineItems.standardCents),
829
- " per ",
830
- quote.interval
831
- ] }),
832
- quote.lineItems.discountCents > 0 ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
833
- /* @__PURE__ */ jsx9("dt", { children: "Discount" }),
834
- /* @__PURE__ */ jsx9("dd", { children: amount(quote.lineItems.discountCents) })
835
- ] }) : null,
836
- /* @__PURE__ */ jsx9("dt", { children: "Due today" }),
837
- /* @__PURE__ */ jsx9("dd", { children: amount(quote.lineItems.dueTodayCents) })
838
- ] }),
839
- quote.commercial ? /* @__PURE__ */ jsxs9("div", { role: "note", "aria-label": "Renewal terms", children: [
840
- quote.commercial.initialTermEndsAt !== void 0 ? /* @__PURE__ */ jsxs9("p", { children: [
841
- "First renewal: ",
842
- new Intl.DateTimeFormat("en-US", {
843
- dateStyle: "long",
844
- timeZone: "UTC"
845
- }).format(quote.commercial.initialTermEndsAt),
846
- "."
847
- ] }) : null,
848
- /* @__PURE__ */ jsxs9("p", { children: [
849
- "Renews automatically at ",
850
- amount(quote.commercial.renewalCents),
851
- " per ",
852
- quote.interval,
853
- " unless canceled before renewal."
854
- ] }),
855
- quote.commercial.discountDuration === "first_term" ? /* @__PURE__ */ jsx9("p", { children: "The discount applies to your first paid term only." }) : null,
856
- quote.commercial.discountDuration === "while_active" ? /* @__PURE__ */ jsx9("p", { children: "The discounted rate continues while this membership stays active." }) : null
857
- ] }) : null,
858
- quote.trustCopy ? /* @__PURE__ */ jsx9("p", { className: "pay-trust", children: quote.trustCopy }) : null
859
- ] }),
860
- /* @__PURE__ */ jsx9(
861
- PaymentStep,
862
- {
863
- ...props,
864
- refundPolicyText: quote.refundPolicyText,
865
- merchantDisclosureText: quote.merchantDisclosureText,
866
- renderPriceLines: void 0,
867
- startCheckout
868
- }
869
- )
870
- ] });
871
- }
984
+ import { useEffect as useEffect10, useRef as useRef3, useState as useState16 } from "preact/hooks";
872
985
 
873
986
  // src/ui/join-booking.tsx
874
- import { useEffect as useEffect6, useState as useState9 } from "preact/hooks";
875
- import { jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
987
+ import { useEffect as useEffect7, useState as useState10 } from "preact/hooks";
988
+ import { jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
876
989
  function JoinBooking(props) {
877
990
  const { applicationId, copy, onBooked, simulation } = props;
878
- const [state, setState] = useState9(null);
879
- const [msg, setMsg] = useState9(null);
880
- const [busy, setBusy] = useState9(false);
881
- const [selected, setSelected] = useState9(null);
991
+ const [state, setState] = useState10(null);
992
+ const [msg, setMsg] = useState10(null);
993
+ const [busy, setBusy] = useState10(false);
994
+ const [selected, setSelected] = useState10(null);
882
995
  const load = async () => {
883
996
  setMsg(null);
884
997
  try {
@@ -903,7 +1016,7 @@ function JoinBooking(props) {
903
1016
  setMsg(copy.loadFailed);
904
1017
  }
905
1018
  };
906
- useEffect6(() => {
1019
+ useEffect7(() => {
907
1020
  void load();
908
1021
  }, [simulation]);
909
1022
  const book = async () => {
@@ -935,9 +1048,11 @@ function JoinBooking(props) {
935
1048
  setBusy(false);
936
1049
  }
937
1050
  };
938
- if (!state) return /* @__PURE__ */ jsx10("p", { className: "slots-status", children: msg ?? copy.loading });
939
- return /* @__PURE__ */ jsxs10("div", { className: "join-book", children: [
940
- /* @__PURE__ */ jsx10(
1051
+ if (!state) return /* @__PURE__ */ jsx12("p", { className: "slots-status", children: msg ?? copy.loading });
1052
+ return /* @__PURE__ */ jsxs12("div", { className: "join-book", children: [
1053
+ /* @__PURE__ */ jsx12("h2", { children: copy.title ?? DEFAULT_JOIN_COPY.booking.title }),
1054
+ /* @__PURE__ */ jsx12("p", { children: copy.intro ?? DEFAULT_JOIN_COPY.booking.intro }),
1055
+ /* @__PURE__ */ jsx12(
941
1056
  SlotPicker,
942
1057
  {
943
1058
  slots: state.slots,
@@ -947,29 +1062,29 @@ function JoinBooking(props) {
947
1062
  onDayChange: () => setSelected(null)
948
1063
  }
949
1064
  ),
950
- /* @__PURE__ */ jsxs10("div", { className: "slot-confirm", hidden: !selected, children: [
951
- /* @__PURE__ */ jsx10("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
952
- msg ? /* @__PURE__ */ jsx10("p", { className: "step2-note", children: msg }) : null
1065
+ /* @__PURE__ */ jsxs12("div", { className: "slot-confirm", hidden: !selected, children: [
1066
+ /* @__PURE__ */ jsx12("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
1067
+ msg ? /* @__PURE__ */ jsx12("p", { className: "step2-note", children: msg }) : null
953
1068
  ] })
954
1069
  ] });
955
1070
  }
956
1071
 
957
1072
  // src/ui/join-accepted.tsx
958
- import { jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
1073
+ import { jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
959
1074
  function JoinAccepted(props) {
960
- return /* @__PURE__ */ jsxs11("div", { className: "join-done card", children: [
961
- /* @__PURE__ */ jsx11("div", { className: "card-label", children: props.copy.accepted.label }),
962
- /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: props.copy.accepted.body }),
963
- /* @__PURE__ */ jsx11("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
1075
+ return /* @__PURE__ */ jsxs13("div", { className: "join-done card", children: [
1076
+ /* @__PURE__ */ jsx13("div", { className: "card-label", children: props.copy.accepted.label }),
1077
+ /* @__PURE__ */ jsx13("p", { className: "meeting-note", children: props.copy.accepted.body }),
1078
+ /* @__PURE__ */ jsx13("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
964
1079
  ] });
965
1080
  }
966
1081
 
967
1082
  // src/ui/gift-claim-card.tsx
968
- import { useState as useState10 } from "preact/hooks";
969
- import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
1083
+ import { useState as useState11 } from "preact/hooks";
1084
+ import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
970
1085
  function GiftClaimCard(props) {
971
- const [code, setCode] = useState10(props.initialCode ?? "");
972
- const [error, setError] = useState10(false);
1086
+ const [code, setCode] = useState11(props.initialCode ?? "");
1087
+ const [error, setError] = useState11(false);
973
1088
  const rotate = async () => {
974
1089
  if (!props.claimId) return;
975
1090
  setError(false);
@@ -982,15 +1097,72 @@ function GiftClaimCard(props) {
982
1097
  if (response.ok && body.code) setCode(body.code);
983
1098
  else setError(true);
984
1099
  };
985
- return /* @__PURE__ */ jsxs12("div", { className: "join-done card", children: [
986
- /* @__PURE__ */ jsx12("div", { className: "card-label", children: props.copy.label }),
987
- /* @__PURE__ */ jsx12("p", { className: "meeting-note", children: props.copy.body }),
988
- code ? /* @__PURE__ */ jsxs12(Fragment6, { children: [
989
- /* @__PURE__ */ jsx12("output", { className: "gift-code", children: code }),
990
- /* @__PURE__ */ jsx12("p", { children: props.copy.save })
1100
+ return /* @__PURE__ */ jsxs14("div", { className: "join-done card", children: [
1101
+ /* @__PURE__ */ jsx14("div", { className: "card-label", children: props.copy.label }),
1102
+ /* @__PURE__ */ jsx14("p", { className: "meeting-note", children: props.copy.body }),
1103
+ code ? /* @__PURE__ */ jsxs14(Fragment6, { children: [
1104
+ /* @__PURE__ */ jsx14("output", { className: "gift-code", children: code }),
1105
+ /* @__PURE__ */ jsx14("p", { children: props.copy.save })
991
1106
  ] }) : null,
992
- !code && props.claimId ? /* @__PURE__ */ jsx12("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
993
- error ? /* @__PURE__ */ jsx12("p", { role: "alert", children: props.copy.rotateFailed }) : null
1107
+ !code && props.claimId ? /* @__PURE__ */ jsx14("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
1108
+ error ? /* @__PURE__ */ jsx14("p", { role: "alert", children: props.copy.rotateFailed }) : null
1109
+ ] });
1110
+ }
1111
+
1112
+ // src/ui/join-tiers.tsx
1113
+ import { useState as useState12 } from "preact/hooks";
1114
+ import { jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
1115
+ function useTierSelection(tiers, initialTierId) {
1116
+ const [selectedTierId, setSelectedTierId] = useState12(() => {
1117
+ const preset = tiers.find((tier) => tier.id === initialTierId);
1118
+ if (preset) return preset.id;
1119
+ return tiers.length === 1 ? tiers[0].id : null;
1120
+ });
1121
+ return {
1122
+ tiers,
1123
+ selected: tiers.find((tier) => tier.id === selectedTierId) ?? null,
1124
+ selectedTierId,
1125
+ selectTier: (tierId) => {
1126
+ if (tiers.some((tier) => tier.id === tierId)) setSelectedTierId(tierId);
1127
+ }
1128
+ };
1129
+ }
1130
+ function TierPlaceholder(context) {
1131
+ return /* @__PURE__ */ jsxs15("fieldset", { "data-chapter-placeholder": "tiers", children: [
1132
+ /* @__PURE__ */ jsx15("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
1133
+ context.tiers.map((tier) => /* @__PURE__ */ jsxs15("label", { children: [
1134
+ /* @__PURE__ */ jsx15(
1135
+ "input",
1136
+ {
1137
+ type: "radio",
1138
+ name: "__chapterTier",
1139
+ value: tier.id,
1140
+ checked: context.selectedTierId === tier.id,
1141
+ onChange: () => context.selectTier(tier.id)
1142
+ }
1143
+ ),
1144
+ tier.name
1145
+ ] }, tier.id))
1146
+ ] });
1147
+ }
1148
+ function tierNeedsPayment(selected, paymentsReady) {
1149
+ return selected ? !selected.free && paymentsReady : paymentsReady;
1150
+ }
1151
+
1152
+ // src/ui/join-application-form.tsx
1153
+ import { jsx as jsx16, jsxs as jsxs16 } from "preact/jsx-runtime";
1154
+ function JoinApplicationForm({ props, copy, invitation, requestedSeat, tierSelection, giftOfferId, selectGift, submitting, error, submit }) {
1155
+ const disabled = submitting || props.submitDisabled === true;
1156
+ return /* @__PURE__ */ jsxs16("form", { className: "join-form", onSubmit: (event) => void submit(event), children: [
1157
+ !requestedSeat && (props.config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs16("fieldset", { children: [
1158
+ /* @__PURE__ */ jsx16("legend", { children: copy.gift.optionsLabel }),
1159
+ /* @__PURE__ */ jsx16("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => selectGift(null), children: copy.gift.chooseSelf }),
1160
+ /* @__PURE__ */ jsx16(GiftOfferCards, { offers: props.config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: selectGift })
1161
+ ] }) : null,
1162
+ !requestedSeat && (props.config.tiers?.length ?? 0) > 1 ? props.renderTiers?.(tierSelection) ?? TierPlaceholder(tierSelection) : null,
1163
+ typeof props.children === "function" ? props.children({ invitation }) : props.children,
1164
+ error ? /* @__PURE__ */ jsx16("p", { className: "join-error", role: "alert", children: error }) : null,
1165
+ props.renderSubmit?.({ submitting, disabled }) ?? /* @__PURE__ */ jsx16("button", { className: "submit-btn", type: "submit", disabled, children: submitting ? copy.form.submitting : copy.form.submit })
994
1166
  ] });
995
1167
  }
996
1168
 
@@ -1015,8 +1187,8 @@ function joinStateFromApplicationResponse(data, fallbackNeedsPayment, submittedG
1015
1187
 
1016
1188
  // src/ui/gift-code-redeemer.tsx
1017
1189
  import { InlineActionField } from "@odla-ai/ui/components";
1018
- import { useState as useState11 } from "preact/hooks";
1019
- import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
1190
+ import { useState as useState13 } from "preact/hooks";
1191
+ import { Fragment as Fragment7, jsx as jsx17, jsxs as jsxs17 } from "preact/jsx-runtime";
1020
1192
  var GIFT_CODE_REDEEMER_CSS = `
1021
1193
  .gift-code-redeemer{container-type:inline-size;display:grid;gap:var(--ui-space-1,.35rem);margin:0}
1022
1194
  .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}
@@ -1031,10 +1203,10 @@ var GIFT_CODE_REDEEMER_CSS = `
1031
1203
  @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%}}
1032
1204
  `;
1033
1205
  function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1034
- const [code, setCode] = useState11("");
1035
- const [loading, setLoading] = useState11(false);
1036
- const [message, setMessage] = useState11("");
1037
- const [messageTone, setMessageTone] = useState11("neutral");
1206
+ const [code, setCode] = useState13("");
1207
+ const [loading, setLoading] = useState13(false);
1208
+ const [message, setMessage] = useState13("");
1209
+ const [messageTone, setMessageTone] = useState13("neutral");
1038
1210
  const normalizedCode = code.trim();
1039
1211
  const redeem = async () => {
1040
1212
  if (!normalizedCode || loading) return;
@@ -1061,9 +1233,9 @@ function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1061
1233
  setLoading(false);
1062
1234
  }
1063
1235
  };
1064
- return /* @__PURE__ */ jsxs13(Fragment7, { children: [
1065
- /* @__PURE__ */ jsx13("style", { children: GIFT_CODE_REDEEMER_CSS }),
1066
- /* @__PURE__ */ jsx13(
1236
+ return /* @__PURE__ */ jsxs17(Fragment7, { children: [
1237
+ /* @__PURE__ */ jsx17("style", { children: GIFT_CODE_REDEEMER_CSS }),
1238
+ /* @__PURE__ */ jsx17(
1067
1239
  InlineActionField,
1068
1240
  {
1069
1241
  id,
@@ -1119,12 +1291,12 @@ async function loadJoinResume(applicationId, fetcher = fetch) {
1119
1291
  }
1120
1292
 
1121
1293
  // src/ui/join-gift-claim.tsx
1122
- import { jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
1294
+ import { jsx as jsx18, jsxs as jsxs18 } from "preact/jsx-runtime";
1123
1295
  function JoinGiftClaim(props) {
1124
- return /* @__PURE__ */ jsxs14("section", { className: "join-gift-claim card", children: [
1125
- /* @__PURE__ */ jsx14("div", { className: "card-label", children: props.copy.claimTitle }),
1126
- /* @__PURE__ */ jsx14("p", { children: props.copy.claimBody }),
1127
- /* @__PURE__ */ jsx14(
1296
+ return /* @__PURE__ */ jsxs18("section", { className: "join-gift-claim card", children: [
1297
+ /* @__PURE__ */ jsx18("div", { className: "card-label", children: props.copy.claimTitle }),
1298
+ /* @__PURE__ */ jsx18("p", { children: props.copy.claimBody }),
1299
+ /* @__PURE__ */ jsx18(
1128
1300
  GiftCodeRedeemer,
1129
1301
  {
1130
1302
  api: props.api,
@@ -1148,24 +1320,21 @@ function JoinGiftClaim(props) {
1148
1320
  }
1149
1321
 
1150
1322
  // src/ui/join-named-seat.tsx
1151
- import { useState as useState12 } from "preact/hooks";
1152
- import { jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
1323
+ import { useState as useState14 } from "preact/hooks";
1324
+ import { jsx as jsx19, jsxs as jsxs19 } from "preact/jsx-runtime";
1153
1325
  function JoinNamedSeat(props) {
1154
- const [seatId, setSeatId] = useState12(props.seatId ?? "");
1155
- const [accepted, setAccepted] = useState12(false), [busy, setBusy] = useState12(false), [error, setError] = useState12("");
1156
- return /* @__PURE__ */ jsxs15("section", { className: "join-named-seat card", children: [
1157
- /* @__PURE__ */ jsx15("h2", { children: "Accept your named seat" }),
1158
- /* @__PURE__ */ jsx15("p", { children: "Sign in with the email address on your invitation. Your membership is paid for; complete your own application and any required interview." }),
1159
- /* @__PURE__ */ jsxs15("label", { children: [
1160
- "Invitation reference",
1161
- /* @__PURE__ */ jsx15("input", { value: seatId, disabled: busy, onInput: (event) => setSeatId(event.currentTarget.value) })
1326
+ const seatId = props.seatId ?? "";
1327
+ const inherited = useChapterCopy(), copy = props.copy ?? inherited.join.namedSeat;
1328
+ const [accepted, setAccepted] = useState14(false), [busy, setBusy] = useState14(false), [error, setError] = useState14("");
1329
+ return /* @__PURE__ */ jsxs19("section", { className: "join-named-seat card", children: [
1330
+ /* @__PURE__ */ jsx19("h2", { children: copy.title }),
1331
+ /* @__PURE__ */ jsx19("p", { children: copy.body }),
1332
+ /* @__PURE__ */ jsxs19("label", { className: "journey-consent", children: [
1333
+ /* @__PURE__ */ jsx19("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
1334
+ /* @__PURE__ */ jsx19("span", { children: copy.consent })
1162
1335
  ] }),
1163
- /* @__PURE__ */ jsxs15("label", { children: [
1164
- /* @__PURE__ */ jsx15("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
1165
- "I accept this seat for myself. It cannot be reassigned and lasts only while the purchaser\u2019s membership and my seat are current."
1166
- ] }),
1167
- error ? /* @__PURE__ */ jsx15("p", { role: "alert", children: error }) : null,
1168
- /* @__PURE__ */ jsx15("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
1336
+ error ? /* @__PURE__ */ jsx19("p", { role: "alert", children: error }) : null,
1337
+ /* @__PURE__ */ jsx19("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
1169
1338
  setBusy(true);
1170
1339
  setError("");
1171
1340
  try {
@@ -1175,59 +1344,54 @@ function JoinNamedSeat(props) {
1175
1344
  });
1176
1345
  if (!result.ok || result.applicationId !== props.applicationId || !["booking", "accepted", "paymentPending"].includes(result.state)) throw new Error("acceptance pending");
1177
1346
  props.onState({ step: result.state, applicationId: result.applicationId });
1178
- } catch {
1179
- setError("We could not confirm acceptance. Check that you are signed in with the invited email, then retry this invitation. Do not start another paid checkout.");
1347
+ } catch (error2) {
1348
+ setError(error2 instanceof Error && error2.message && error2.message !== "acceptance pending" ? error2.message.slice(0, 400) : copy.failure);
1180
1349
  } finally {
1181
1350
  setBusy(false);
1182
1351
  }
1183
- }, children: busy ? "Confirming your seat\u2026" : "Accept seat \u2014 no payment" })
1352
+ }, children: busy ? copy.accepting : copy.accept })
1184
1353
  ] });
1185
1354
  }
1186
1355
 
1187
- // src/ui/join-tiers.tsx
1188
- import { useState as useState13 } from "preact/hooks";
1189
- import { jsx as jsx16, jsxs as jsxs16 } from "preact/jsx-runtime";
1190
- function useTierSelection(tiers, initialTierId) {
1191
- const [selectedTierId, setSelectedTierId] = useState13(() => {
1192
- const preset = tiers.find((tier) => tier.id === initialTierId);
1193
- if (preset) return preset.id;
1194
- return tiers.length === 1 ? tiers[0].id : null;
1195
- });
1196
- return {
1197
- tiers,
1198
- selected: tiers.find((tier) => tier.id === selectedTierId) ?? null,
1199
- selectedTierId,
1200
- selectTier: (tierId) => {
1201
- if (tiers.some((tier) => tier.id === tierId)) setSelectedTierId(tierId);
1356
+ // src/ui/join-invitation.tsx
1357
+ import { useEffect as useEffect8, useState as useState15 } from "preact/hooks";
1358
+ import { jsx as jsx20, jsxs as jsxs20 } from "preact/jsx-runtime";
1359
+ function useJoinInvitation(seatId, api, fallback) {
1360
+ const [invitation, setInvitation] = useState15(null);
1361
+ const [error, setError] = useState15("");
1362
+ const [revision, setRevision] = useState15(0);
1363
+ useEffect8(() => {
1364
+ let live = true;
1365
+ setInvitation(null);
1366
+ setError("");
1367
+ if (!seatId) return;
1368
+ if (!api) {
1369
+ setError(fallback);
1370
+ return;
1202
1371
  }
1203
- };
1372
+ void api(`/api/named-seat/invitation?seat=${encodeURIComponent(seatId)}`).then((value) => {
1373
+ if (value.seatId !== seatId || !value.skuId || !value.purchaserName) throw new Error(fallback);
1374
+ if (live) setInvitation(value);
1375
+ }).catch((error2) => {
1376
+ if (live) setError(error2 instanceof Error ? error2.message.slice(0, 400) : fallback);
1377
+ });
1378
+ return () => {
1379
+ live = false;
1380
+ };
1381
+ }, [seatId, api, revision]);
1382
+ return { invitation: invitation?.seatId === seatId ? invitation : null, error, retry: () => setRevision((value) => value + 1) };
1204
1383
  }
1205
- function TierPlaceholder(context) {
1206
- return /* @__PURE__ */ jsxs16("fieldset", { "data-chapter-placeholder": "tiers", children: [
1207
- /* @__PURE__ */ jsx16("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
1208
- context.tiers.map((tier) => /* @__PURE__ */ jsxs16("label", { children: [
1209
- /* @__PURE__ */ jsx16(
1210
- "input",
1211
- {
1212
- type: "radio",
1213
- name: "__chapterTier",
1214
- value: tier.id,
1215
- checked: context.selectedTierId === tier.id,
1216
- onChange: () => context.selectTier(tier.id)
1217
- }
1218
- ),
1219
- tier.name
1220
- ] }, tier.id))
1384
+ function JoinInvitationNotice(props) {
1385
+ return /* @__PURE__ */ jsxs20("aside", { className: "journey-invitation", "aria-label": props.copy.title, children: [
1386
+ /* @__PURE__ */ jsx20("p", { children: /* @__PURE__ */ jsx20("strong", { children: formatChapterCopy(props.copy.from, { purchaserName: props.invitation.purchaserName }) }) }),
1387
+ /* @__PURE__ */ jsx20("p", { children: formatChapterCopy(props.copy.prepaid, { tierName: props.tierName }) })
1221
1388
  ] });
1222
1389
  }
1223
- function tierNeedsPayment(selected, paymentsReady) {
1224
- return selected ? !selected.free && paymentsReady : paymentsReady;
1225
- }
1226
1390
 
1227
1391
  // src/ui/join-pending.ts
1228
- import { useEffect as useEffect7 } from "preact/hooks";
1392
+ import { useEffect as useEffect9 } from "preact/hooks";
1229
1393
  function useJoinPending(state, loadResume, setState) {
1230
- useEffect7(() => {
1394
+ useEffect9(() => {
1231
1395
  if (state.step !== "paymentPending") return;
1232
1396
  let live = true;
1233
1397
  let timer;
@@ -1250,27 +1414,33 @@ function useJoinPending(state, loadResume, setState) {
1250
1414
  }
1251
1415
 
1252
1416
  // src/ui/join.tsx
1253
- import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs17 } from "preact/jsx-runtime";
1417
+ import { Fragment as Fragment8, jsx as jsx21, jsxs as jsxs21 } from "preact/jsx-runtime";
1254
1418
  function JoinIsland(props) {
1419
+ return /* @__PURE__ */ jsx21(JourneyFrame, { children: /* @__PURE__ */ jsx21(JoinFlow, { ...props }) });
1420
+ }
1421
+ function JoinFlow(props) {
1255
1422
  const inheritedCopy = useChapterCopy();
1256
1423
  const {
1257
1424
  config,
1258
- children,
1259
1425
  membersHref = "/members/",
1260
1426
  renderStepHeader,
1261
1427
  renderDone,
1262
1428
  renderAccepted,
1263
- renderGift,
1264
- renderSubmit
1429
+ renderGift
1265
1430
  } = props;
1266
1431
  const copy = props.copy ?? config.copy ?? inheritedCopy.join;
1432
+ const seatCopy = copy.namedSeat ?? inheritedCopy.join.namedSeat;
1267
1433
  const tiers = config.tiers ?? [];
1268
1434
  const tierSelection = useTierSelection(tiers, props.initialTierId);
1269
- const { selectedTierId, selected: selectedTier } = tierSelection;
1270
- const [state, setState] = useState14(props.initialState ?? { step: "form" });
1271
- const [error, setError] = useState14(null);
1272
- const [submitting, setSubmitting] = useState14(false);
1273
- const [giftOfferId, setGiftOfferId] = useState14(props.initialGiftOfferId ?? null);
1435
+ const requestedSeat = props.simulation ? void 0 : props.initialNamedSeatId;
1436
+ const invitationState = useJoinInvitation(requestedSeat, props.namedSeatClaimApi, seatCopy.unavailable);
1437
+ const invitation = invitationState.invitation;
1438
+ const selectedTierId = invitation?.skuId ?? tierSelection.selectedTierId;
1439
+ const selectedTier = tiers.find((tier) => tier.id === selectedTierId) ?? tierSelection.selected;
1440
+ const [state, setState] = useState16(requestedSeat ? { step: "form" } : props.initialState ?? { step: "form" });
1441
+ const [error, setError] = useState16(null);
1442
+ const [submitting, setSubmitting] = useState16(false);
1443
+ const [giftOfferId, setGiftOfferId] = useState16(props.initialGiftOfferId ?? null);
1274
1444
  const simulatedPayment = props.simulation ? props.simulation.payment ?? {
1275
1445
  lineItems: {
1276
1446
  standardCents: selectedTier?.priceCents ?? 0,
@@ -1280,9 +1450,9 @@ function JoinIsland(props) {
1280
1450
  } : void 0;
1281
1451
  const simulatedBooking = props.simulation ? props.simulation.booking ?? { slots: [], timezone: "UTC" } : void 0;
1282
1452
  const submissionId = useRef3(null);
1283
- useEffect8(() => {
1453
+ useEffect10(() => {
1284
1454
  if (props.simulation) return;
1285
- const resolver = props.loadResume ?? (() => {
1455
+ const resolver = requestedSeat ? () => invitation?.applicationId ? loadJoinResume(invitation.applicationId) : Promise.resolve(null) : props.loadResume ?? (() => {
1286
1456
  if (typeof window === "undefined") return Promise.resolve(null);
1287
1457
  const applicationId = new URL(window.location.href).searchParams.get("application");
1288
1458
  return applicationId ? loadJoinResume(applicationId) : Promise.resolve(null);
@@ -1296,17 +1466,21 @@ function JoinIsland(props) {
1296
1466
  return () => {
1297
1467
  live = false;
1298
1468
  };
1299
- }, [props.loadResume, props.simulation]);
1300
- useJoinPending(state, props.loadResume, setState);
1469
+ }, [props.loadResume, props.simulation, requestedSeat, invitation?.applicationId]);
1470
+ useJoinPending(state, requestedSeat ? void 0 : props.loadResume, setState);
1301
1471
  const submit = async (e) => {
1302
1472
  e.preventDefault();
1303
- if (submitting || props.submitDisabled === true) return;
1473
+ if (submitting || props.submitDisabled === true || requestedSeat && !invitation) return;
1304
1474
  setSubmitting(true);
1305
1475
  setError(null);
1306
1476
  try {
1307
1477
  const fields = collectFormFields(new FormData(e.currentTarget));
1308
1478
  fields.submissionId = submissionId.current ??= crypto.randomUUID();
1309
- if (giftOfferId) fields.giftOfferId = giftOfferId;
1479
+ if (invitation) {
1480
+ fields.namedSeatId = invitation.seatId;
1481
+ fields.tierId = invitation.skuId;
1482
+ fields.email = invitation.recipientEmail;
1483
+ } else if (giftOfferId) fields.giftOfferId = giftOfferId;
1310
1484
  else if (selectedTierId) fields.tierId = selectedTierId;
1311
1485
  if (props.simulation) {
1312
1486
  setState({
@@ -1317,6 +1491,16 @@ function JoinIsland(props) {
1317
1491
  });
1318
1492
  return;
1319
1493
  }
1494
+ if (invitation && props.namedSeatClaimApi) {
1495
+ const result = await props.namedSeatClaimApi("/api/applications", {
1496
+ method: "POST",
1497
+ headers: { "content-type": "application/json" },
1498
+ body: JSON.stringify(fields)
1499
+ });
1500
+ if (!result.id) throw new Error(copy.form.submitFailed);
1501
+ setState(joinStateFromApplicationResponse(result, true, false));
1502
+ return;
1503
+ }
1320
1504
  const res = await fetch("/api/applications", {
1321
1505
  method: "POST",
1322
1506
  headers: { "content-type": "application/json" },
@@ -1335,50 +1519,66 @@ function JoinIsland(props) {
1335
1519
  setSubmitting(false);
1336
1520
  }
1337
1521
  };
1338
- const header = renderStepHeader?.({
1339
- state,
1340
- ..."applicationId" in state ? { applicationId: state.applicationId } : {}
1341
- });
1522
+ if (requestedSeat && !invitation) return /* @__PURE__ */ jsxs21("section", { className: "card", "aria-live": "polite", children: [
1523
+ /* @__PURE__ */ jsx21("h2", { children: seatCopy.title }),
1524
+ /* @__PURE__ */ jsx21("p", { role: invitationState.error ? "alert" : "status", children: invitationState.error || seatCopy.loading }),
1525
+ invitationState.error ? /* @__PURE__ */ jsx21("button", { type: "button", onClick: invitationState.retry, children: seatCopy.retry }) : null
1526
+ ] });
1527
+ const header = /* @__PURE__ */ jsxs21(Fragment8, { children: [
1528
+ invitation ? /* @__PURE__ */ jsx21(
1529
+ JoinInvitationNotice,
1530
+ {
1531
+ invitation,
1532
+ copy: seatCopy,
1533
+ tierName: selectedTier?.name ?? ""
1534
+ }
1535
+ ) : null,
1536
+ renderStepHeader?.({
1537
+ state,
1538
+ ...invitation ? { invitation } : {},
1539
+ ..."applicationId" in state ? { applicationId: state.applicationId } : {}
1540
+ })
1541
+ ] });
1342
1542
  if (state.step === "closed") {
1343
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1543
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1344
1544
  header,
1345
- /* @__PURE__ */ jsxs17("section", { className: "card", "aria-label": "Application closed", children: [
1346
- /* @__PURE__ */ jsx17("h2", { children: "This application is closed" }),
1347
- /* @__PURE__ */ jsx17("p", { children: "This link cannot resume payment or booking. Sign in to view your application status, or contact the chapter if you need help." }),
1348
- /* @__PURE__ */ jsx17("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1545
+ /* @__PURE__ */ jsxs21("section", { className: "card", "aria-label": "Application closed", children: [
1546
+ /* @__PURE__ */ jsx21("h2", { children: "This application is closed" }),
1547
+ /* @__PURE__ */ jsx21("p", { children: "This link cannot resume payment or booking. Sign in to view your application status, or contact the chapter if you need help." }),
1548
+ /* @__PURE__ */ jsx21("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1349
1549
  ] })
1350
1550
  ] });
1351
1551
  }
1352
1552
  if (state.step === "done") {
1353
- const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs17("div", { className: "join-done card", children: [
1354
- /* @__PURE__ */ jsx17("div", { className: "card-label", children: copy.done.label }),
1355
- /* @__PURE__ */ jsx17("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
1356
- /* @__PURE__ */ jsx17("p", { className: "meeting-note", children: copy.done.calendarInvite }),
1357
- /* @__PURE__ */ jsx17("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1553
+ const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs21("div", { className: "join-done card", children: [
1554
+ /* @__PURE__ */ jsx21("div", { className: "card-label", children: copy.done.label }),
1555
+ /* @__PURE__ */ jsx21("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
1556
+ /* @__PURE__ */ jsx21("p", { className: "meeting-note", children: copy.done.calendarInvite }),
1557
+ /* @__PURE__ */ jsx21("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1358
1558
  ] });
1359
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1559
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1360
1560
  header,
1361
1561
  body
1362
1562
  ] });
1363
1563
  }
1364
1564
  if (state.step === "accepted") {
1365
- const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx17(JoinAccepted, { copy, membersHref });
1366
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1565
+ const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx21(JoinAccepted, { copy, membersHref });
1566
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1367
1567
  header,
1368
1568
  body
1369
1569
  ] });
1370
1570
  }
1371
1571
  if (state.step === "gift") {
1372
- const body = renderGift?.(state) ?? /* @__PURE__ */ jsx17(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
1373
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1572
+ const body = renderGift?.(state) ?? /* @__PURE__ */ jsx21(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
1573
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1374
1574
  header,
1375
1575
  body
1376
1576
  ] });
1377
1577
  }
1378
1578
  if (state.step === "booking") {
1379
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1579
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1380
1580
  header,
1381
- /* @__PURE__ */ jsx17(
1581
+ /* @__PURE__ */ jsx21(
1382
1582
  JoinBooking,
1383
1583
  {
1384
1584
  applicationId: state.applicationId,
@@ -1393,23 +1593,24 @@ function JoinIsland(props) {
1393
1593
  }
1394
1594
  if (state.step === "payment") {
1395
1595
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation && !state.giftPurchase) {
1396
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1596
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1397
1597
  header,
1398
- /* @__PURE__ */ jsx17(
1598
+ /* @__PURE__ */ jsx21(
1399
1599
  JoinNamedSeat,
1400
1600
  {
1401
1601
  api: props.namedSeatClaimApi,
1402
1602
  applicationId: state.applicationId,
1403
1603
  seatId: props.initialNamedSeatId,
1604
+ copy: seatCopy,
1404
1605
  onState: setState
1405
1606
  }
1406
1607
  )
1407
1608
  ] });
1408
1609
  }
1409
1610
  const Payment = state.giftPurchase || props.simulation ? PaymentStep : MembershipPaymentStep;
1410
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1611
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1411
1612
  header,
1412
- props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx17(
1613
+ props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx21(
1413
1614
  JoinGiftClaim,
1414
1615
  {
1415
1616
  api: props.giftClaimApi,
@@ -1420,7 +1621,7 @@ function JoinIsland(props) {
1420
1621
  onError: () => setError(copy.form.unexpectedFailure)
1421
1622
  }
1422
1623
  ) : null,
1423
- /* @__PURE__ */ jsx17(
1624
+ /* @__PURE__ */ jsx21(
1424
1625
  Payment,
1425
1626
  {
1426
1627
  applicationId: state.applicationId,
@@ -1449,39 +1650,42 @@ function JoinIsland(props) {
1449
1650
  }
1450
1651
  if (state.step === "paymentPending") {
1451
1652
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation) {
1452
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1653
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1453
1654
  header,
1454
- /* @__PURE__ */ jsx17(
1655
+ /* @__PURE__ */ jsx21(
1455
1656
  JoinNamedSeat,
1456
1657
  {
1457
1658
  api: props.namedSeatClaimApi,
1458
1659
  applicationId: state.applicationId,
1459
1660
  seatId: props.initialNamedSeatId,
1661
+ copy: seatCopy,
1460
1662
  onState: setState
1461
1663
  }
1462
1664
  )
1463
1665
  ] });
1464
1666
  }
1465
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1667
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1466
1668
  header,
1467
- /* @__PURE__ */ jsx17("p", { className: "pay-status", role: "status", children: copy.payment.pending })
1669
+ /* @__PURE__ */ jsx21("p", { className: "pay-status", role: "status", children: copy.payment.pending })
1468
1670
  ] });
1469
1671
  }
1470
- const submitDisabled = submitting || props.submitDisabled === true;
1471
- const tierChooser = tiers.length > 1 ? props.renderTiers?.(tierSelection) ?? TierPlaceholder(tierSelection) : null;
1472
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1672
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
1473
1673
  header,
1474
- /* @__PURE__ */ jsxs17("form", { className: "join-form", onSubmit: (e) => void submit(e), children: [
1475
- (config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs17("fieldset", { children: [
1476
- /* @__PURE__ */ jsx17("legend", { children: copy.gift.optionsLabel }),
1477
- /* @__PURE__ */ jsx17("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => setGiftOfferId(null), children: copy.gift.chooseSelf }),
1478
- /* @__PURE__ */ jsx17(GiftOfferCards, { offers: config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: setGiftOfferId })
1479
- ] }) : null,
1480
- tierChooser,
1481
- children,
1482
- error ? /* @__PURE__ */ jsx17("p", { className: "join-error", children: error }) : null,
1483
- renderSubmit?.({ submitting, disabled: submitDisabled }) ?? /* @__PURE__ */ jsx17("button", { className: "submit-btn", type: "submit", disabled: submitDisabled, children: submitting ? copy.form.submitting : copy.form.submit })
1484
- ] })
1674
+ /* @__PURE__ */ jsx21(
1675
+ JoinApplicationForm,
1676
+ {
1677
+ props,
1678
+ copy,
1679
+ invitation,
1680
+ requestedSeat,
1681
+ tierSelection,
1682
+ giftOfferId,
1683
+ selectGift: setGiftOfferId,
1684
+ submitting,
1685
+ error,
1686
+ submit
1687
+ }
1688
+ )
1485
1689
  ] });
1486
1690
  }
1487
1691
 
@@ -1657,7 +1861,7 @@ function membershipPageDocument(input, options = {}) {
1657
1861
  }
1658
1862
 
1659
1863
  // src/ui/membership-cards.tsx
1660
- import { jsx as jsx18, jsxs as jsxs18 } from "preact/jsx-runtime";
1864
+ import { jsx as jsx22, jsxs as jsxs22 } from "preact/jsx-runtime";
1661
1865
  var MEMBERSHIP_CARD_TOKEN_DEFAULTS = Object.freeze({
1662
1866
  "--ui-membership-grid-gap": "1.4rem",
1663
1867
  "--ui-membership-card-padding": "2rem 1.7rem 1.8rem",
@@ -1726,67 +1930,67 @@ function membershipApplicationHref(applicationPath, tierId) {
1726
1930
  }
1727
1931
  function TierAction(props) {
1728
1932
  if (props.mode === "select") {
1729
- return /* @__PURE__ */ jsxs18("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
1933
+ return /* @__PURE__ */ jsxs22("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
1730
1934
  props.label,
1731
- /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", children: "\u2192" })
1935
+ /* @__PURE__ */ jsx22("span", { "aria-hidden": "true", children: "\u2192" })
1732
1936
  ] });
1733
1937
  }
1734
1938
  if (props.mode === "preview") {
1735
- return /* @__PURE__ */ jsxs18("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
1939
+ return /* @__PURE__ */ jsxs22("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
1736
1940
  props.label,
1737
- /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", children: "\u2192" })
1941
+ /* @__PURE__ */ jsx22("span", { "aria-hidden": "true", children: "\u2192" })
1738
1942
  ] });
1739
1943
  }
1740
- return /* @__PURE__ */ jsxs18("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
1944
+ return /* @__PURE__ */ jsxs22("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
1741
1945
  props.label,
1742
- /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", children: "\u2192" })
1946
+ /* @__PURE__ */ jsx22("span", { "aria-hidden": "true", children: "\u2192" })
1743
1947
  ] });
1744
1948
  }
1745
1949
  function TierCard(props) {
1746
1950
  const { tier, presentation } = props;
1747
1951
  const summary = presentation.summaryMarkdown ?? tier.blurb;
1748
1952
  const action = presentation.ctaLabel ?? (tier.free ? "Join for free" : `Choose ${tier.name}`);
1749
- return /* @__PURE__ */ jsxs18("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
1750
- presentation.badge ? /* @__PURE__ */ jsx18("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
1751
- /* @__PURE__ */ jsx18("h3", { className: "chapter-membership-name", children: tier.name }),
1752
- /* @__PURE__ */ jsxs18("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
1753
- presentation.capacity ? /* @__PURE__ */ jsxs18("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
1754
- /* @__PURE__ */ jsx18("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
1755
- /* @__PURE__ */ jsx18("small", { children: presentation.capacity.label ?? "available" })
1953
+ return /* @__PURE__ */ jsxs22("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
1954
+ presentation.badge ? /* @__PURE__ */ jsx22("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
1955
+ /* @__PURE__ */ jsx22("h3", { className: "chapter-membership-name", children: tier.name }),
1956
+ /* @__PURE__ */ jsxs22("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
1957
+ presentation.capacity ? /* @__PURE__ */ jsxs22("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
1958
+ /* @__PURE__ */ jsx22("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
1959
+ /* @__PURE__ */ jsx22("small", { children: presentation.capacity.label ?? "available" })
1756
1960
  ] }) : null,
1757
- /* @__PURE__ */ jsxs18("div", { className: "chapter-membership-price-stack", children: [
1758
- presentation.priceEyebrow ? /* @__PURE__ */ jsx18("p", { className: "chapter-membership-price-eyebrow", children: presentation.priceEyebrow }) : null,
1759
- /* @__PURE__ */ jsxs18("div", { className: "chapter-membership-price-row", children: [
1760
- presentation.compareAtPriceCents !== void 0 ? /* @__PURE__ */ jsx18("span", { className: "chapter-membership-compare", children: fmtMoney(presentation.compareAtPriceCents) }) : null,
1761
- /* @__PURE__ */ jsx18("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.priceCents) }),
1762
- !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx18("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
1961
+ /* @__PURE__ */ jsxs22("div", { className: "chapter-membership-price-stack", children: [
1962
+ presentation.priceEyebrow ? /* @__PURE__ */ jsx22("p", { className: "chapter-membership-price-eyebrow", children: presentation.priceEyebrow }) : null,
1963
+ /* @__PURE__ */ jsxs22("div", { className: "chapter-membership-price-row", children: [
1964
+ presentation.compareAtPriceCents !== void 0 ? /* @__PURE__ */ jsx22("span", { className: "chapter-membership-compare", children: fmtMoney(presentation.compareAtPriceCents) }) : null,
1965
+ /* @__PURE__ */ jsx22("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.priceCents) }),
1966
+ !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx22("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
1763
1967
  ] })
1764
1968
  ] })
1765
1969
  ] }),
1766
- presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx18(Markdown2, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
1767
- summary ? /* @__PURE__ */ jsx18(Markdown2, { className: "chapter-membership-summary", source: summary }) : null,
1768
- presentation.benefits?.length ? /* @__PURE__ */ jsx18("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs18("li", { children: [
1769
- /* @__PURE__ */ jsx18("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
1770
- /* @__PURE__ */ jsx18(Markdown2, { className: "chapter-membership-benefit", source: benefit })
1970
+ presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx22(Markdown2, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
1971
+ summary ? /* @__PURE__ */ jsx22(Markdown2, { className: "chapter-membership-summary", source: summary }) : null,
1972
+ presentation.benefits?.length ? /* @__PURE__ */ jsx22("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs22("li", { children: [
1973
+ /* @__PURE__ */ jsx22("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
1974
+ /* @__PURE__ */ jsx22(Markdown2, { className: "chapter-membership-benefit", source: benefit })
1771
1975
  ] }, `${tier.id}-benefit-${index}`)) }) : null,
1772
- presentation.callout ? /* @__PURE__ */ jsxs18("aside", { className: "chapter-membership-callout", children: [
1773
- presentation.callout.label ? /* @__PURE__ */ jsx18("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
1774
- /* @__PURE__ */ jsx18(Markdown2, { source: presentation.callout.bodyMarkdown })
1976
+ presentation.callout ? /* @__PURE__ */ jsxs22("aside", { className: "chapter-membership-callout", children: [
1977
+ presentation.callout.label ? /* @__PURE__ */ jsx22("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
1978
+ /* @__PURE__ */ jsx22(Markdown2, { source: presentation.callout.bodyMarkdown })
1775
1979
  ] }) : null,
1776
- /* @__PURE__ */ jsx18(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
1980
+ /* @__PURE__ */ jsx22(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
1777
1981
  ] });
1778
1982
  }
1779
1983
  function MembershipCards(props) {
1780
1984
  const document2 = membershipPageDocument(props.document, { tiers: props.tiers });
1781
1985
  const byId = new Map(props.tiers.map((tier) => [tier.id, tier]));
1782
1986
  const mode = props.mode ?? "link";
1783
- return /* @__PURE__ */ jsxs18("section", { className: ["chapter-membership", props.className].filter(Boolean).join(" "), "data-chapter-membership": MEMBERSHIP_PAGE_RENDERER, "aria-label": props.ariaLabel ?? "Membership options", children: [
1784
- /* @__PURE__ */ jsx18("style", { children: MEMBERSHIP_CARDS_CSS }),
1785
- /* @__PURE__ */ jsx18("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
1987
+ return /* @__PURE__ */ jsxs22("section", { className: ["chapter-membership", props.className].filter(Boolean).join(" "), "data-chapter-membership": MEMBERSHIP_PAGE_RENDERER, "aria-label": props.ariaLabel ?? "Membership options", children: [
1988
+ /* @__PURE__ */ jsx22("style", { children: MEMBERSHIP_CARDS_CSS }),
1989
+ /* @__PURE__ */ jsx22("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
1786
1990
  const tier = byId.get(presentation.tierId);
1787
- return /* @__PURE__ */ jsx18(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
1991
+ return /* @__PURE__ */ jsx22(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
1788
1992
  }) }),
1789
- document2.footnoteMarkdown ? /* @__PURE__ */ jsx18(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
1993
+ document2.footnoteMarkdown ? /* @__PURE__ */ jsx22(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
1790
1994
  ] });
1791
1995
  }
1792
1996
 
@@ -1800,21 +2004,23 @@ export {
1800
2004
  fmtDate,
1801
2005
  groupSlotsByDay,
1802
2006
  SlotPicker,
2007
+ PaymentStep,
2008
+ JourneyFrame,
2009
+ MemberRestart,
1803
2010
  Rescheduler,
1804
2011
  GiftOfferCards,
1805
- PaymentStep,
1806
2012
  GiftPurchases,
1807
2013
  MemberGifts,
1808
2014
  MemberContentArea,
1809
2015
  MembersArea,
1810
- GiftCodeRedeemer,
1811
- loadJoinResume,
1812
2016
  useTierSelection,
1813
2017
  TierPlaceholder,
1814
2018
  tierNeedsPayment,
2019
+ GiftCodeRedeemer,
2020
+ loadJoinResume,
1815
2021
  JoinIsland,
1816
2022
  MEMBERSHIP_CARD_TOKEN_DEFAULTS,
1817
2023
  membershipApplicationHref,
1818
2024
  MembershipCards
1819
2025
  };
1820
- //# sourceMappingURL=chunk-KZ2AXEZW.js.map
2026
+ //# sourceMappingURL=chunk-KHDO2QFA.js.map