@odla-ai/chapter 0.48.3 → 0.50.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-JXX4BFDP.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";
114
-
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";
113
+ // src/ui/member-restart.tsx
114
+ import { useEffect as useEffect2, useState as useState4 } from "preact/hooks";
199
115
 
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,353 @@ 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
+ quote.founding ? /* @__PURE__ */ jsx3("p", { className: "payment-founding", role: "note", "aria-label": "Founding member", children: copy.foundingMember }) : null,
324
+ props.renderPriceLines ? props.renderPriceLines(quote.lineItems) : /* @__PURE__ */ jsxs3("dl", { children: [
325
+ /* @__PURE__ */ jsx3("dt", { children: "Membership price" }),
326
+ /* @__PURE__ */ jsxs3("dd", { children: [
327
+ amount(quote.lineItems.standardCents),
328
+ " per ",
329
+ quote.interval
330
+ ] }),
331
+ quote.lineItems.discountCents > 0 ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
332
+ /* @__PURE__ */ jsx3("dt", { children: quote.founding || quote.presentation ? copy.foundingDiscount : "Discount" }),
333
+ /* @__PURE__ */ jsx3("dd", { children: amount(quote.lineItems.discountCents) })
334
+ ] }) : null,
335
+ /* @__PURE__ */ jsx3("dt", { children: "Due today" }),
336
+ /* @__PURE__ */ jsx3("dd", { children: amount(quote.lineItems.dueTodayCents) })
337
+ ] }),
338
+ quote.commercial ? /* @__PURE__ */ jsxs3("div", { role: "note", "aria-label": "Renewal terms", children: [
339
+ quote.commercial.initialTermEndsAt !== void 0 ? /* @__PURE__ */ jsxs3("p", { children: [
340
+ "First renewal: ",
341
+ new Intl.DateTimeFormat("en-US", {
342
+ dateStyle: "long",
343
+ timeZone: "UTC"
344
+ }).format(quote.commercial.initialTermEndsAt),
345
+ "."
346
+ ] }) : null,
347
+ /* @__PURE__ */ jsxs3("p", { children: [
348
+ "Renews automatically at ",
349
+ amount(quote.commercial.renewalCents),
350
+ " per ",
351
+ quote.interval,
352
+ " unless canceled before renewal."
353
+ ] }),
354
+ quote.commercial.discountDuration === "first_term" ? /* @__PURE__ */ jsx3("p", { children: "The discount applies to your first paid term only." }) : null,
355
+ quote.commercial.discountDuration === "while_active" ? /* @__PURE__ */ jsx3("p", { children: "The discounted rate continues while this membership stays active." }) : null
356
+ ] }) : null,
357
+ quote.trustCopy ? /* @__PURE__ */ jsx3("p", { className: "pay-trust", children: quote.trustCopy }) : null
358
+ ] }),
359
+ /* @__PURE__ */ jsx3(
360
+ PaymentStep,
361
+ {
362
+ ...props,
363
+ refundPolicyText: quote.refundPolicyText,
364
+ merchantDisclosureText: quote.merchantDisclosureText,
365
+ renderPriceLines: void 0,
366
+ startCheckout
367
+ }
368
+ )
369
+ ] });
370
+ }
371
+
372
+ // src/ui/journey-frame.tsx
373
+ import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
374
+ var styles = `
375
+ .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}
376
+ .chapter-journey *{box-sizing:border-box}
377
+ .chapter-journey :where(h2,h3,p){margin:0 0 1rem}
378
+ .chapter-journey :where(h2,h3){font-family:var(--ui-font-display,inherit);line-height:1.2;letter-spacing:-.025em;text-transform:none}
379
+ .chapter-journey h2{font-size:clamp(1.6rem,3vw,2.15rem)}
380
+ .chapter-journey h3{font-size:1.3rem}
381
+ .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}
382
+ .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}
383
+ .chapter-journey :where(form,.journey-fields){display:grid;gap:1.25rem}
384
+ .chapter-journey :where(label){display:block;font-size:.95rem;font-weight:500;letter-spacing:normal;text-transform:none;line-height:1.55}
385
+ .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}
386
+ .chapter-journey :where(input,textarea,button,a,select):focus-visible{outline:3px solid var(--ui-accent,#3d6a60);outline-offset:3px}
387
+ .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)}
388
+ .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}
389
+ .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)}
390
+ .chapter-journey :where(button.secondary,.journey-secondary){background:transparent;color:var(--ui-text,#202827);border-color:var(--ui-border,#dfe3df)}
391
+ .chapter-journey :is(.slot-time,.slot-day){background:var(--ui-surface,#fff);color:var(--ui-text,#202827);border-color:var(--ui-border,#dfe3df)}
392
+ .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)}
393
+ .chapter-journey :is(.slot-time,.slot-day)[aria-pressed=true]{background:var(--ui-accent,#285c50);color:var(--ui-on-accent,#fff)}
394
+ .chapter-journey button:disabled{opacity:.55;cursor:default}
395
+ .chapter-journey :where(.compliance-box,.journey-note){padding:1.2rem;background:var(--ui-surface-2,#f3f5f2);border-radius:10px;margin-block:1.2rem}
396
+ .chapter-journey :where(.compliance-box p:last-child,.journey-note p:last-child,.card p:last-child){margin-bottom:0}
397
+ .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}
398
+ .chapter-journey .journey-invitation p{margin:0}
399
+ .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}
400
+ .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)}
401
+ .chapter-journey :where(dd){margin:0;text-align:right;font-variant-numeric:tabular-nums}
402
+ .chapter-journey :where(.pay-status,.meeting-note,.slots-status){color:var(--ui-text-muted,#536360)}
403
+ .chapter-journey .journey-actions{display:flex;gap:.75rem;flex-wrap:wrap;margin-top:1.25rem}
404
+ .chapter-journey fieldset{min-width:0;border:0;padding:0;margin:0}
405
+ @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}}
406
+ `;
407
+ function JourneyFrame({ children }) {
408
+ return /* @__PURE__ */ jsxs4("div", { className: "chapter-journey", children: [
409
+ /* @__PURE__ */ jsx4("style", { children: styles }),
410
+ children
411
+ ] });
412
+ }
413
+
414
+ // src/ui/member-restart.tsx
372
415
  import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
416
+ function MemberRestart(props) {
417
+ const [id, setId] = useState4(props.applicationId);
418
+ const [busy, setBusy] = useState4(false);
419
+ const [error, setError] = useState4("");
420
+ const [paid, setPaid] = useState4(false);
421
+ const [checking, setChecking] = useState4(false);
422
+ const [revision, setRevision] = useState4(0);
423
+ useEffect2(() => {
424
+ if (!paid) return;
425
+ let live = true, timer, attempt = 0;
426
+ setChecking(true);
427
+ const check = async () => {
428
+ try {
429
+ const me = await props.api("/api/me");
430
+ if (!live) return;
431
+ if (me.memberAccess === true) {
432
+ await props.onComplete();
433
+ return;
434
+ }
435
+ } catch {
436
+ }
437
+ if (!live) return;
438
+ if (++attempt < 12) timer = setTimeout(() => void check(), 2500);
439
+ else setChecking(false);
440
+ };
441
+ void check();
442
+ return () => {
443
+ live = false;
444
+ clearTimeout(timer);
445
+ };
446
+ }, [paid, revision]);
447
+ const start = async () => {
448
+ setBusy(true);
449
+ setError("");
450
+ try {
451
+ const result = await props.api("/api/membership/restart", { method: "POST" });
452
+ if (!result.applicationId) throw new Error("We could not prepare your subscription. Please retry.");
453
+ setId(result.applicationId);
454
+ } catch (cause) {
455
+ setError(cause instanceof Error ? cause.message.slice(0, 300) : "We could not prepare your subscription. Please retry.");
456
+ } finally {
457
+ setBusy(false);
458
+ }
459
+ };
460
+ return /* @__PURE__ */ jsx5(JourneyFrame, { children: /* @__PURE__ */ jsxs5("section", { className: "card", "aria-label": "Restart membership", children: [
461
+ /* @__PURE__ */ jsx5("p", { className: "card-label", children: "Welcome back" }),
462
+ /* @__PURE__ */ jsx5("h2", { children: "Restart your membership" }),
463
+ /* @__PURE__ */ jsx5("p", { children: "Your account is ready, and your previous approval still counts. No new application or interview is needed." }),
464
+ /* @__PURE__ */ jsxs5("dl", { className: "journey-summary", children: [
465
+ /* @__PURE__ */ jsx5("dt", { children: "Name" }),
466
+ /* @__PURE__ */ jsx5("dd", { children: [props.application.firstName, props.application.lastName].filter(Boolean).join(" ") }),
467
+ /* @__PURE__ */ jsx5("dt", { children: "Email" }),
468
+ /* @__PURE__ */ jsx5("dd", { children: props.application.email })
469
+ ] }),
470
+ paid ? /* @__PURE__ */ jsxs5("div", { role: "status", children: [
471
+ /* @__PURE__ */ jsx5("h3", { children: "Confirming your membership" }),
472
+ /* @__PURE__ */ jsx5("p", { children: "We\u2019re waiting for payment confirmation. Your membership will open here as soon as it arrives." }),
473
+ !checking ? /* @__PURE__ */ jsx5("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Check membership" }) : null
474
+ ] }) : 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" }),
475
+ error ? /* @__PURE__ */ jsx5("p", { role: "alert", children: error }) : null
476
+ ] }) });
477
+ }
478
+
479
+ // src/ui/members.tsx
480
+ import { useEffect as useEffect6, useState as useState9 } from "preact/hooks";
481
+
482
+ // src/ui/reschedule.tsx
483
+ import { useState as useState5 } from "preact/hooks";
484
+ import { jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
485
+ function Rescheduler(props) {
486
+ const inheritedCopy = useChapterCopy();
487
+ const copy = props.copy ?? inheritedCopy.members.reschedule;
488
+ const { api, applicationId, timezone, onRescheduled, label = copy.changeTime } = props;
489
+ const [open, setOpen] = useState5(false);
490
+ const [slots, setSlots] = useState5(null);
491
+ const [tz, setTz] = useState5(timezone);
492
+ const [busy, setBusy] = useState5(false);
493
+ const [msg, setMsg] = useState5(null);
494
+ const start = async () => {
495
+ setOpen(true);
496
+ setSlots(null);
497
+ setMsg(null);
498
+ try {
499
+ const r = await api("/api/schedule/slots");
500
+ if (r.schedulingReady === false) {
501
+ setSlots([]);
502
+ setMsg(copy.unavailable);
503
+ return;
504
+ }
505
+ setSlots(r.slots ?? []);
506
+ if (r.timezone) setTz(r.timezone);
507
+ } catch {
508
+ setSlots([]);
509
+ setMsg(copy.loadFailed);
510
+ }
511
+ };
512
+ const pick = async (slot) => {
513
+ setBusy(true);
514
+ setMsg(null);
515
+ try {
516
+ await api("/api/schedule/book", { method: "POST", body: JSON.stringify({ applicationId, startAt: slot.startAt }) });
517
+ setOpen(false);
518
+ await onRescheduled();
519
+ } catch (e) {
520
+ setMsg(e instanceof Error ? e.message : copy.slotGone);
521
+ } finally {
522
+ setBusy(false);
523
+ }
524
+ };
525
+ if (!open) {
526
+ return /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: /* @__PURE__ */ jsx6(
527
+ "a",
528
+ {
529
+ href: "#",
530
+ onClick: (e) => {
531
+ e.preventDefault();
532
+ void start();
533
+ },
534
+ children: label
535
+ }
536
+ ) });
537
+ }
538
+ return /* @__PURE__ */ jsxs6("div", { className: "msched", children: [
539
+ 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(
540
+ SlotPicker,
541
+ {
542
+ slots,
543
+ timezone: tz,
544
+ classes: { days: "msched-days", day: "msched-day", times: "msched-times", time: "msched-time" },
545
+ onPick: (s) => void pick(s)
546
+ }
547
+ ),
548
+ busy ? /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: copy.rescheduling }) : null,
549
+ msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx6("p", { className: "meeting-note error", children: msg }) : null,
550
+ /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: /* @__PURE__ */ jsx6(
551
+ "a",
552
+ {
553
+ href: "#",
554
+ onClick: (e) => {
555
+ e.preventDefault();
556
+ setOpen(false);
557
+ },
558
+ children: copy.keepTime
559
+ }
560
+ ) })
561
+ ] });
562
+ }
563
+
564
+ // src/ui/member-gifts.tsx
565
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState6 } from "preact/hooks";
566
+
567
+ // src/ui/gift-offer-cards.tsx
568
+ import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
569
+ var price = (cents, currency) => currency === "usd" ? fmtMoney(cents) : new Intl.NumberFormat(void 0, { style: "currency", currency: currency.toUpperCase() }).format(cents / 100);
570
+ function GiftOfferCards(props) {
571
+ const mode = props.mode ?? "preview";
572
+ 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: [
573
+ /* @__PURE__ */ jsx7("div", { className: "card-label", children: offer.title }),
574
+ /* @__PURE__ */ jsx7("p", { children: offer.description }),
575
+ /* @__PURE__ */ jsxs7("p", { className: "meeting-date", children: [
576
+ price(offer.priceCents, offer.currency),
577
+ offer.billingMode === "subscription" ? " recurring" : ""
578
+ ] }),
579
+ 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 })
580
+ ] }, offer.id)) });
581
+ }
582
+
583
+ // src/ui/member-gifts.tsx
584
+ import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
373
585
  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("");
586
+ const [data, setData] = useState6(null);
587
+ const [selected, setSelected] = useState6(null);
588
+ const [code, setCode] = useState6("");
589
+ const [message, setMessage] = useState6("");
378
590
  const purchaseId = useRef2(crypto.randomUUID());
379
591
  const load = async () => setData(await props.api("/api/gifts"));
380
- useEffect(() => {
592
+ useEffect3(() => {
381
593
  void load().catch(() => setData({ offers: [], claims: [] }));
382
594
  }, []);
383
595
  const rotate = async (claimId) => {
@@ -392,16 +604,16 @@ function GiftPurchases(props) {
392
604
  if (!data) return null;
393
605
  if (data.offers.length === 0 && data.claims.length === 0) return null;
394
606
  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) => {
607
+ return /* @__PURE__ */ jsxs8("section", { className: "member-gifts", children: [
608
+ data.offers.length ? /* @__PURE__ */ jsxs8("div", { className: "card", children: [
609
+ /* @__PURE__ */ jsx8("div", { className: "card-label", children: props.copy.title }),
610
+ /* @__PURE__ */ jsx8("p", { children: props.copy.body }),
611
+ /* @__PURE__ */ jsx8(GiftOfferCards, { offers: data.offers, mode: "select", selectedOfferId: selected, onSelect: (id) => {
400
612
  setSelected(id);
401
613
  setCode("");
402
614
  purchaseId.current = crypto.randomUUID();
403
615
  } }),
404
- offer ? /* @__PURE__ */ jsx5(
616
+ offer ? /* @__PURE__ */ jsx8(
405
617
  PaymentStep,
406
618
  {
407
619
  applicationId: purchaseId.current,
@@ -421,31 +633,31 @@ function GiftPurchases(props) {
421
633
  }
422
634
  }
423
635
  ) : 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 })
636
+ code ? /* @__PURE__ */ jsxs8("div", { className: "gift-code-once", children: [
637
+ /* @__PURE__ */ jsx8("output", { children: code }),
638
+ /* @__PURE__ */ jsx8("p", { children: props.copy.saveCode })
427
639
  ] }) : null
428
640
  ] }) : 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
641
+ /* @__PURE__ */ jsxs8("div", { className: "card", children: [
642
+ /* @__PURE__ */ jsx8("div", { className: "card-label", children: props.copy.purchases }),
643
+ data.claims.length ? data.claims.map((claim) => /* @__PURE__ */ jsxs8("div", { className: "member-row", children: [
644
+ /* @__PURE__ */ jsx8("span", { children: claim.offerId }),
645
+ /* @__PURE__ */ jsx8("span", { children: props.copy[claim.status] }),
646
+ claim.status === "pending" ? /* @__PURE__ */ jsx8("button", { className: "btn secondary mini", onClick: () => void rotate(claim.id), children: props.copy.rotate }) : null
647
+ ] }, claim.id)) : /* @__PURE__ */ jsx8("p", { children: props.copy.noPurchases }),
648
+ message ? /* @__PURE__ */ jsx8("p", { role: "status", children: message }) : null
437
649
  ] })
438
650
  ] });
439
651
  }
440
652
  var MemberGifts = GiftPurchases;
441
653
 
442
654
  // 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";
655
+ import { useEffect as useEffect4, useState as useState7 } from "preact/hooks";
656
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
445
657
  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);
658
+ const [data, setData] = useState7(null), [error, setError] = useState7("");
659
+ const [name, setName] = useState7(""), [email, setEmail] = useState7(""), [reviewing, setReviewing] = useState7(false);
660
+ const [terms, setTerms] = useState7(false), [pending, setPending] = useState7(false);
449
661
  const load = async () => {
450
662
  try {
451
663
  const value = await api("/api/named-seat");
@@ -459,23 +671,23 @@ function NamedSeatPurchase({ api }) {
459
671
  setError("The named-seat service is unavailable. Refresh to check your purchase before trying again.");
460
672
  }
461
673
  };
462
- useEffect2(() => {
674
+ useEffect4(() => {
463
675
  void load();
464
676
  }, []);
465
677
  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: [
678
+ return /* @__PURE__ */ jsx9(JourneyFrame, { children: /* @__PURE__ */ jsxs9("section", { className: "card member-named-seat", "aria-label": "Named additional seat", children: [
679
+ /* @__PURE__ */ jsx9("h2", { children: "Named additional seat" }),
680
+ error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: error }) : null,
681
+ data ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
682
+ data.seat ? /* @__PURE__ */ jsxs9("p", { children: [
471
683
  data.seat.recipientName,
472
684
  " \xB7 ",
473
685
  data.seat.recipientEmail,
474
686
  " \xB7 ",
475
687
  data.seat.status.replaceAll("_", " ")
476
688
  ] }) : null,
477
- data.eligible ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
478
- /* @__PURE__ */ jsxs6("p", { children: [
689
+ data.eligible ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
690
+ /* @__PURE__ */ jsxs9("p", { children: [
479
691
  money,
480
692
  " paid in full now. This seat ",
481
693
  data.autoRenew ? "renews with your primary membership" : "ends with your primary membership",
@@ -486,38 +698,38 @@ function NamedSeatPurchase({ api }) {
486
698
  " ",
487
699
  "It belongs to one named person and cannot be reassigned."
488
700
  ] }),
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) => {
701
+ /* @__PURE__ */ jsx9("p", { children: "The recipient must accept and complete their own application and any required interview. The seat stays open for them while your membership is paid; it is not refunded automatically. Contact the chapter if you need a refund." }),
702
+ !reviewing ? /* @__PURE__ */ jsxs9("form", { onSubmit: (event) => {
491
703
  event.preventDefault();
492
704
  setReviewing(true);
493
705
  setTerms(false);
494
706
  }, children: [
495
- /* @__PURE__ */ jsxs6("label", { children: [
707
+ /* @__PURE__ */ jsxs9("label", { children: [
496
708
  "Recipient name",
497
- /* @__PURE__ */ jsx6("input", { required: true, maxLength: 160, value: name, disabled: Boolean(data.seat), onInput: (event) => setName(event.currentTarget.value) })
709
+ /* @__PURE__ */ jsx9("input", { required: true, maxLength: 160, value: name, disabled: Boolean(data.seat), onInput: (event) => setName(event.currentTarget.value) })
498
710
  ] }),
499
- /* @__PURE__ */ jsxs6("label", { children: [
711
+ /* @__PURE__ */ jsxs9("label", { children: [
500
712
  "Recipient email",
501
- /* @__PURE__ */ jsx6("input", { required: true, type: "email", maxLength: 254, value: email, disabled: Boolean(data.seat), onInput: (event) => setEmail(event.currentTarget.value) })
713
+ /* @__PURE__ */ jsx9("input", { required: true, type: "email", maxLength: 254, value: email, disabled: Boolean(data.seat), onInput: (event) => setEmail(event.currentTarget.value) })
502
714
  ] }),
503
- /* @__PURE__ */ jsx6("button", { className: "btn primary", type: "submit", children: "Review named seat" })
504
- ] }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
505
- /* @__PURE__ */ jsxs6("p", { children: [
715
+ /* @__PURE__ */ jsx9("button", { className: "btn primary", type: "submit", children: "Review named seat" })
716
+ ] }) : /* @__PURE__ */ jsxs9(Fragment3, { children: [
717
+ /* @__PURE__ */ jsxs9("p", { children: [
506
718
  "For ",
507
719
  name,
508
720
  " \xB7 ",
509
721
  email
510
722
  ] }),
511
- /* @__PURE__ */ jsx6("button", { className: "btn secondary", type: "button", onClick: () => {
723
+ /* @__PURE__ */ jsx9("button", { className: "btn secondary", type: "button", onClick: () => {
512
724
  setReviewing(false);
513
725
  setTerms(false);
514
726
  void load();
515
727
  }, 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."
728
+ /* @__PURE__ */ jsxs9("label", { className: "journey-consent", children: [
729
+ /* @__PURE__ */ jsx9("input", { type: "checkbox", checked: terms, onChange: (event) => setTerms(event.currentTarget.checked) }),
730
+ /* @__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
731
  ] }),
520
- terms ? /* @__PURE__ */ jsx6(
732
+ terms ? /* @__PURE__ */ jsx9(
521
733
  PaymentStep,
522
734
  {
523
735
  applicationId: data.membershipId,
@@ -536,17 +748,21 @@ function NamedSeatPurchase({ api }) {
536
748
  }
537
749
  ) : null
538
750
  ] })
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
- ] });
751
+ ] }) : !data.seat ? /* @__PURE__ */ jsxs9("p", { children: [
752
+ "Additional seats can be purchased until ",
753
+ new Date(data.purchaseEndsAt).toLocaleDateString("en-US", { timeZone: "UTC" }),
754
+ "."
755
+ ] }) : null,
756
+ 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
757
+ ] }) : !error ? /* @__PURE__ */ jsx9("p", { children: "Loading your seat terms\u2026" }) : null,
758
+ /* @__PURE__ */ jsx9("button", { className: "btn secondary", onClick: () => void load(), children: "Refresh seat status" })
759
+ ] }) });
544
760
  }
545
761
 
546
762
  // src/ui/member-content.tsx
547
- import { useEffect as useEffect3, useMemo as useMemo2, useState as useState6 } from "preact/hooks";
763
+ import { useEffect as useEffect5, useMemo as useMemo2, useState as useState8 } from "preact/hooks";
548
764
  import { Markdown } from "@odla-ai/ui/components";
549
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
765
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
550
766
  var pageLabel = (page) => `${page.title}${page.access === "locked" ? " \xB7 Locked" : ""}`;
551
767
  function MemberContentArea(props) {
552
768
  const {
@@ -554,15 +770,15 @@ function MemberContentArea(props) {
554
770
  path = "/api/member-content",
555
771
  upsellHref,
556
772
  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." })
773
+ loadingContent = /* @__PURE__ */ jsx10("p", { className: "meeting-note", children: "Loading member pages\u2026" }),
774
+ emptyContent = /* @__PURE__ */ jsx10("p", { className: "meeting-note", children: "No member pages have been published yet." })
559
775
  } = props;
560
- const [data, setData] = useState6(props.initialData ?? null);
561
- const [error, setError] = useState6(null);
562
- const [selectedId, setSelectedId] = useState6(
776
+ const [data, setData] = useState8(props.initialData ?? null);
777
+ const [error, setError] = useState8(null);
778
+ const [selectedId, setSelectedId] = useState8(
563
779
  props.initialPageId ?? props.initialData?.pages[0]?.id ?? ""
564
780
  );
565
- useEffect3(() => {
781
+ useEffect5(() => {
566
782
  if (props.initialData) return;
567
783
  let live = true;
568
784
  setError(null);
@@ -584,18 +800,18 @@ function MemberContentArea(props) {
584
800
  }
585
801
  return [...result.entries()];
586
802
  }, [data]);
587
- if (error) return /* @__PURE__ */ jsxs7("p", { className: "meeting-note", role: "alert", children: [
803
+ if (error) return /* @__PURE__ */ jsxs10("p", { className: "meeting-note", role: "alert", children: [
588
804
  "Member pages could not be loaded: ",
589
805
  error
590
806
  ] });
591
- if (!data) return /* @__PURE__ */ jsx7(Fragment3, { children: loadingContent });
592
- if (data.pages.length === 0) return /* @__PURE__ */ jsx7(Fragment3, { children: emptyContent });
807
+ if (!data) return /* @__PURE__ */ jsx10(Fragment4, { children: loadingContent });
808
+ if (data.pages.length === 0) return /* @__PURE__ */ jsx10(Fragment4, { children: emptyContent });
593
809
  const selected = data.pages.find((page) => page.id === selectedId) ?? data.pages[0];
594
810
  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(
811
+ return /* @__PURE__ */ jsxs10("div", { className: "chapter-member-content", "data-member-content": true, children: [
812
+ /* @__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: [
813
+ /* @__PURE__ */ jsx10("h2", { children: section }),
814
+ /* @__PURE__ */ jsx10("ul", { children: pages.map((page) => /* @__PURE__ */ jsx10("li", { "data-parent-page": page.navigation.parentId ?? void 0, children: /* @__PURE__ */ jsx10(
599
815
  "button",
600
816
  {
601
817
  type: "button",
@@ -605,23 +821,23 @@ function MemberContentArea(props) {
605
821
  }
606
822
  ) }, page.id)) })
607
823
  ] }, 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: [
824
+ /* @__PURE__ */ jsxs10("article", { className: "chapter-member-content-page", "data-access": selected.access, children: [
825
+ /* @__PURE__ */ jsxs10("header", { children: [
826
+ /* @__PURE__ */ jsxs10("div", { className: "card-label", children: [
611
827
  selected.source.type === "parent" ? "Parent" : "Chapter",
612
828
  " \xB7 ",
613
829
  selected.access
614
830
  ] }),
615
- /* @__PURE__ */ jsx7("h1", { children: selected.title }),
616
- selected.summary ? /* @__PURE__ */ jsx7("p", { children: selected.summary }) : null
831
+ /* @__PURE__ */ jsx10("h1", { children: selected.title }),
832
+ selected.summary ? /* @__PURE__ */ jsx10("p", { children: selected.summary }) : null
617
833
  ] }),
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
834
+ selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx10(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs10("div", { className: "chapter-member-content-locked", children: [
835
+ selected.teaserMarkdown ? /* @__PURE__ */ jsx10(Markdown, { source: selected.teaserMarkdown }) : /* @__PURE__ */ jsx10("p", { children: "This page is not included in your current membership." }),
836
+ selected.upsell ? /* @__PURE__ */ jsxs10("aside", { className: "chapter-member-content-upsell", "aria-label": selected.upsell.label ?? "Membership option", children: [
837
+ /* @__PURE__ */ jsx10("h2", { children: selected.upsell.label ?? selected.upsell.tierName }),
838
+ /* @__PURE__ */ jsx10("p", { children: fmtMoney(selected.upsell.priceCents) }),
839
+ selected.upsell.bodyMarkdown ? /* @__PURE__ */ jsx10(Markdown, { source: selected.upsell.bodyMarkdown }) : null,
840
+ 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
841
  ] }) : null
626
842
  ] })
627
843
  ] })
@@ -629,12 +845,12 @@ function MemberContentArea(props) {
629
845
  }
630
846
 
631
847
  // src/ui/members.tsx
632
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
848
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
633
849
  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 }),
850
+ return /* @__PURE__ */ jsxs11("div", { className: "card", children: [
851
+ /* @__PURE__ */ jsx11("div", { className: "card-label", children: copy.provisional.cardLabel }),
852
+ /* @__PURE__ */ jsxs11("div", { className: "meeting-block", children: [
853
+ /* @__PURE__ */ jsx11("div", { className: "meeting-kicker", children: kicker }),
638
854
  body
639
855
  ] })
640
856
  ] });
@@ -645,9 +861,9 @@ function ProvisionalCard(props) {
645
861
  return card(
646
862
  copy,
647
863
  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 })
864
+ /* @__PURE__ */ jsxs11(Fragment5, { children: [
865
+ /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
866
+ /* @__PURE__ */ jsx11("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
651
867
  ] })
652
868
  );
653
869
  }
@@ -655,19 +871,19 @@ function ProvisionalCard(props) {
655
871
  return card(
656
872
  copy,
657
873
  copy.provisional.refunded,
658
- /* @__PURE__ */ jsx8("div", { className: "meeting-note", children: copy.provisional.refundedBody })
874
+ /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: copy.provisional.refundedBody })
659
875
  );
660
876
  }
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;
877
+ 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
878
  if (application.meetingAt) {
663
879
  return card(
664
880
  copy,
665
881
  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(
882
+ /* @__PURE__ */ jsxs11(Fragment5, { children: [
883
+ /* @__PURE__ */ jsx11("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
884
+ /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
885
+ application.meetUrl ? /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: /* @__PURE__ */ jsx11("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
886
+ /* @__PURE__ */ jsx11(
671
887
  Rescheduler,
672
888
  {
673
889
  api,
@@ -684,9 +900,9 @@ function ProvisionalCard(props) {
684
900
  return card(
685
901
  copy,
686
902
  copy.provisional.bookCall,
687
- /* @__PURE__ */ jsxs8(Fragment4, { children: [
688
- /* @__PURE__ */ jsx8("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
689
- /* @__PURE__ */ jsx8(
903
+ /* @__PURE__ */ jsxs11(Fragment5, { children: [
904
+ /* @__PURE__ */ jsx11("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
905
+ /* @__PURE__ */ jsx11(
690
906
  Rescheduler,
691
907
  {
692
908
  api,
@@ -713,8 +929,8 @@ function MembersArea(props) {
713
929
  renderProvisional
714
930
  } = props;
715
931
  const copy = props.copy ?? inheritedCopy.members;
716
- const [me, setMe] = useState7(null);
717
- const [error, setError] = useState7(false);
932
+ const [me, setMe] = useState9(null);
933
+ const [error, setError] = useState9(false);
718
934
  const reload = async () => {
719
935
  try {
720
936
  setMe(await api("/api/me"));
@@ -722,25 +938,25 @@ function MembersArea(props) {
722
938
  setError(true);
723
939
  }
724
940
  };
725
- useEffect4(() => {
941
+ useEffect6(() => {
726
942
  void reload();
727
943
  }, []);
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 });
944
+ if (error) return /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: copy.loadFailed });
945
+ if (!me) return /* @__PURE__ */ jsx11("p", { className: "meeting-note", children: inheritedCopy.common.loading });
730
946
  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 })
947
+ return /* @__PURE__ */ jsxs11(Fragment5, { children: [
948
+ /* @__PURE__ */ jsxs11("div", { className: "card", children: [
949
+ /* @__PURE__ */ jsxs11("div", { className: "member-row", children: [
950
+ /* @__PURE__ */ jsx11("span", { className: "member-email", children: me.email }),
951
+ /* @__PURE__ */ jsx11("span", { className: "role-badge " + role, children: role })
736
952
  ] }),
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
953
+ /* @__PURE__ */ jsxs11("div", { className: "account-actions", children: [
954
+ /* @__PURE__ */ jsx11("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
955
+ me.authorized || me.superAdmin ? /* @__PURE__ */ jsx11("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
740
956
  ] })
741
957
  ] }),
742
- me.memberAccess !== true ? (() => {
743
- const defaultContent = /* @__PURE__ */ jsx8(
958
+ me.memberAccess !== true && me.membershipRestart?.eligible && me.application ? /* @__PURE__ */ jsx11(MemberRestart, { api, application: me.application, applicationId: me.membershipRestart.applicationId, onComplete: reload }) : me.memberAccess !== true ? (() => {
959
+ const defaultContent = /* @__PURE__ */ jsx11(
744
960
  ProvisionalCard,
745
961
  {
746
962
  api,
@@ -757,128 +973,30 @@ function MembersArea(props) {
757
973
  reload,
758
974
  defaultContent
759
975
  }) ?? defaultContent;
760
- })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx8(
976
+ })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx11(
761
977
  MemberContentArea,
762
978
  {
763
979
  api,
764
980
  ...memberPages === true ? {} : memberPages
765
981
  }
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
982
+ ) : /* @__PURE__ */ jsx11("div", { className: "card", children: /* @__PURE__ */ jsx11("div", { className: "card-label", children: copy.full.welcome }) })),
983
+ /* @__PURE__ */ jsx11(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
984
+ me.namedSeatsEnabled ? /* @__PURE__ */ jsx11(NamedSeatPurchase, { api }) : null
769
985
  ] });
770
986
  }
771
987
 
772
988
  // 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
- }
989
+ import { useEffect as useEffect10, useRef as useRef3, useState as useState16 } from "preact/hooks";
872
990
 
873
991
  // 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";
992
+ import { useEffect as useEffect7, useState as useState10 } from "preact/hooks";
993
+ import { jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
876
994
  function JoinBooking(props) {
877
995
  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);
996
+ const [state, setState] = useState10(null);
997
+ const [msg, setMsg] = useState10(null);
998
+ const [busy, setBusy] = useState10(false);
999
+ const [selected, setSelected] = useState10(null);
882
1000
  const load = async () => {
883
1001
  setMsg(null);
884
1002
  try {
@@ -903,7 +1021,7 @@ function JoinBooking(props) {
903
1021
  setMsg(copy.loadFailed);
904
1022
  }
905
1023
  };
906
- useEffect6(() => {
1024
+ useEffect7(() => {
907
1025
  void load();
908
1026
  }, [simulation]);
909
1027
  const book = async () => {
@@ -935,9 +1053,11 @@ function JoinBooking(props) {
935
1053
  setBusy(false);
936
1054
  }
937
1055
  };
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(
1056
+ if (!state) return /* @__PURE__ */ jsx12("p", { className: "slots-status", children: msg ?? copy.loading });
1057
+ return /* @__PURE__ */ jsxs12("div", { className: "join-book", children: [
1058
+ /* @__PURE__ */ jsx12("h2", { children: copy.title ?? DEFAULT_JOIN_COPY.booking.title }),
1059
+ /* @__PURE__ */ jsx12("p", { children: copy.intro ?? DEFAULT_JOIN_COPY.booking.intro }),
1060
+ /* @__PURE__ */ jsx12(
941
1061
  SlotPicker,
942
1062
  {
943
1063
  slots: state.slots,
@@ -947,29 +1067,29 @@ function JoinBooking(props) {
947
1067
  onDayChange: () => setSelected(null)
948
1068
  }
949
1069
  ),
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
1070
+ /* @__PURE__ */ jsxs12("div", { className: "slot-confirm", hidden: !selected, children: [
1071
+ /* @__PURE__ */ jsx12("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
1072
+ msg ? /* @__PURE__ */ jsx12("p", { className: "step2-note", children: msg }) : null
953
1073
  ] })
954
1074
  ] });
955
1075
  }
956
1076
 
957
1077
  // src/ui/join-accepted.tsx
958
- import { jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
1078
+ import { jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
959
1079
  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 })
1080
+ return /* @__PURE__ */ jsxs13("div", { className: "join-done card", children: [
1081
+ /* @__PURE__ */ jsx13("div", { className: "card-label", children: props.copy.accepted.label }),
1082
+ /* @__PURE__ */ jsx13("p", { className: "meeting-note", children: props.copy.accepted.body }),
1083
+ /* @__PURE__ */ jsx13("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
964
1084
  ] });
965
1085
  }
966
1086
 
967
1087
  // 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";
1088
+ import { useState as useState11 } from "preact/hooks";
1089
+ import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
970
1090
  function GiftClaimCard(props) {
971
- const [code, setCode] = useState10(props.initialCode ?? "");
972
- const [error, setError] = useState10(false);
1091
+ const [code, setCode] = useState11(props.initialCode ?? "");
1092
+ const [error, setError] = useState11(false);
973
1093
  const rotate = async () => {
974
1094
  if (!props.claimId) return;
975
1095
  setError(false);
@@ -982,15 +1102,83 @@ function GiftClaimCard(props) {
982
1102
  if (response.ok && body.code) setCode(body.code);
983
1103
  else setError(true);
984
1104
  };
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 })
1105
+ return /* @__PURE__ */ jsxs14("div", { className: "join-done card", children: [
1106
+ /* @__PURE__ */ jsx14("div", { className: "card-label", children: props.copy.label }),
1107
+ /* @__PURE__ */ jsx14("p", { className: "meeting-note", children: props.copy.body }),
1108
+ code ? /* @__PURE__ */ jsxs14(Fragment6, { children: [
1109
+ /* @__PURE__ */ jsx14("output", { className: "gift-code", children: code }),
1110
+ /* @__PURE__ */ jsx14("p", { children: props.copy.save })
991
1111
  ] }) : 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
1112
+ !code && props.claimId ? /* @__PURE__ */ jsx14("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
1113
+ error ? /* @__PURE__ */ jsx14("p", { role: "alert", children: props.copy.rotateFailed }) : null
1114
+ ] });
1115
+ }
1116
+
1117
+ // src/ui/join-tiers.tsx
1118
+ import { useState as useState12 } from "preact/hooks";
1119
+ import { Fragment as Fragment7, jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
1120
+ var joinTierMoney = (cents, currency = "usd") => new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
1121
+ function useTierSelection(tiers, initialTierId) {
1122
+ const [selectedTierId, setSelectedTierId] = useState12(() => {
1123
+ const preset = tiers.find((tier) => tier.id === initialTierId);
1124
+ if (preset) return preset.id;
1125
+ return tiers.length === 1 ? tiers[0].id : null;
1126
+ });
1127
+ return {
1128
+ tiers,
1129
+ selected: tiers.find((tier) => tier.id === selectedTierId) ?? null,
1130
+ selectedTierId,
1131
+ selectTier: (tierId) => {
1132
+ if (tiers.some((tier) => tier.id === tierId)) setSelectedTierId(tierId);
1133
+ }
1134
+ };
1135
+ }
1136
+ function TierPlaceholder(context) {
1137
+ return /* @__PURE__ */ jsxs15("fieldset", { "data-chapter-placeholder": "tiers", children: [
1138
+ /* @__PURE__ */ jsx15("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
1139
+ context.tiers.map((tier) => /* @__PURE__ */ jsxs15("label", { children: [
1140
+ /* @__PURE__ */ jsx15(
1141
+ "input",
1142
+ {
1143
+ type: "radio",
1144
+ name: "__chapterTier",
1145
+ value: tier.id,
1146
+ checked: context.selectedTierId === tier.id,
1147
+ onChange: () => context.selectTier(tier.id)
1148
+ }
1149
+ ),
1150
+ tier.name,
1151
+ tier.founding ? /* @__PURE__ */ jsxs15(Fragment7, { children: [
1152
+ " \u2014 ",
1153
+ joinTierMoney(tier.founding.dueTodayCents),
1154
+ " founding member rate (list ",
1155
+ joinTierMoney(tier.priceCents),
1156
+ ")"
1157
+ ] }) : tier.free ? " \u2014 free" : /* @__PURE__ */ jsxs15(Fragment7, { children: [
1158
+ " \u2014 ",
1159
+ joinTierMoney(tier.priceCents)
1160
+ ] })
1161
+ ] }, tier.id))
1162
+ ] });
1163
+ }
1164
+ function tierNeedsPayment(selected, paymentsReady) {
1165
+ return selected ? !selected.free && paymentsReady : paymentsReady;
1166
+ }
1167
+
1168
+ // src/ui/join-application-form.tsx
1169
+ import { jsx as jsx16, jsxs as jsxs16 } from "preact/jsx-runtime";
1170
+ function JoinApplicationForm({ props, copy, invitation, requestedSeat, tierSelection, giftOfferId, selectGift, submitting, error, submit }) {
1171
+ const disabled = submitting || props.submitDisabled === true;
1172
+ return /* @__PURE__ */ jsxs16("form", { className: "join-form", onSubmit: (event) => void submit(event), children: [
1173
+ !requestedSeat && (props.config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs16("fieldset", { children: [
1174
+ /* @__PURE__ */ jsx16("legend", { children: copy.gift.optionsLabel }),
1175
+ /* @__PURE__ */ jsx16("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => selectGift(null), children: copy.gift.chooseSelf }),
1176
+ /* @__PURE__ */ jsx16(GiftOfferCards, { offers: props.config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: selectGift })
1177
+ ] }) : null,
1178
+ !requestedSeat && (props.config.tiers?.length ?? 0) > 1 ? props.renderTiers?.(tierSelection) ?? TierPlaceholder(tierSelection) : null,
1179
+ typeof props.children === "function" ? props.children({ invitation }) : props.children,
1180
+ error ? /* @__PURE__ */ jsx16("p", { className: "join-error", role: "alert", children: error }) : null,
1181
+ props.renderSubmit?.({ submitting, disabled }) ?? /* @__PURE__ */ jsx16("button", { className: "submit-btn", type: "submit", disabled, children: submitting ? copy.form.submitting : copy.form.submit })
994
1182
  ] });
995
1183
  }
996
1184
 
@@ -1015,8 +1203,8 @@ function joinStateFromApplicationResponse(data, fallbackNeedsPayment, submittedG
1015
1203
 
1016
1204
  // src/ui/gift-code-redeemer.tsx
1017
1205
  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";
1206
+ import { useState as useState13 } from "preact/hooks";
1207
+ import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs17 } from "preact/jsx-runtime";
1020
1208
  var GIFT_CODE_REDEEMER_CSS = `
1021
1209
  .gift-code-redeemer{container-type:inline-size;display:grid;gap:var(--ui-space-1,.35rem);margin:0}
1022
1210
  .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 +1219,10 @@ var GIFT_CODE_REDEEMER_CSS = `
1031
1219
  @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
1220
  `;
1033
1221
  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");
1222
+ const [code, setCode] = useState13("");
1223
+ const [loading, setLoading] = useState13(false);
1224
+ const [message, setMessage] = useState13("");
1225
+ const [messageTone, setMessageTone] = useState13("neutral");
1038
1226
  const normalizedCode = code.trim();
1039
1227
  const redeem = async () => {
1040
1228
  if (!normalizedCode || loading) return;
@@ -1061,9 +1249,9 @@ function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1061
1249
  setLoading(false);
1062
1250
  }
1063
1251
  };
1064
- return /* @__PURE__ */ jsxs13(Fragment7, { children: [
1065
- /* @__PURE__ */ jsx13("style", { children: GIFT_CODE_REDEEMER_CSS }),
1066
- /* @__PURE__ */ jsx13(
1252
+ return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1253
+ /* @__PURE__ */ jsx17("style", { children: GIFT_CODE_REDEEMER_CSS }),
1254
+ /* @__PURE__ */ jsx17(
1067
1255
  InlineActionField,
1068
1256
  {
1069
1257
  id,
@@ -1119,12 +1307,12 @@ async function loadJoinResume(applicationId, fetcher = fetch) {
1119
1307
  }
1120
1308
 
1121
1309
  // src/ui/join-gift-claim.tsx
1122
- import { jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
1310
+ import { jsx as jsx18, jsxs as jsxs18 } from "preact/jsx-runtime";
1123
1311
  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(
1312
+ return /* @__PURE__ */ jsxs18("section", { className: "join-gift-claim card", children: [
1313
+ /* @__PURE__ */ jsx18("div", { className: "card-label", children: props.copy.claimTitle }),
1314
+ /* @__PURE__ */ jsx18("p", { children: props.copy.claimBody }),
1315
+ /* @__PURE__ */ jsx18(
1128
1316
  GiftCodeRedeemer,
1129
1317
  {
1130
1318
  api: props.api,
@@ -1148,24 +1336,21 @@ function JoinGiftClaim(props) {
1148
1336
  }
1149
1337
 
1150
1338
  // 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";
1339
+ import { useState as useState14 } from "preact/hooks";
1340
+ import { jsx as jsx19, jsxs as jsxs19 } from "preact/jsx-runtime";
1153
1341
  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) })
1162
- ] }),
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."
1342
+ const seatId = props.seatId ?? "";
1343
+ const inherited = useChapterCopy(), copy = props.copy ?? inherited.join.namedSeat;
1344
+ const [accepted, setAccepted] = useState14(false), [busy, setBusy] = useState14(false), [error, setError] = useState14("");
1345
+ return /* @__PURE__ */ jsxs19("section", { className: "join-named-seat card", children: [
1346
+ /* @__PURE__ */ jsx19("h2", { children: copy.title }),
1347
+ /* @__PURE__ */ jsx19("p", { children: copy.body }),
1348
+ /* @__PURE__ */ jsxs19("label", { className: "journey-consent", children: [
1349
+ /* @__PURE__ */ jsx19("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
1350
+ /* @__PURE__ */ jsx19("span", { children: copy.consent })
1166
1351
  ] }),
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 () => {
1352
+ error ? /* @__PURE__ */ jsx19("p", { role: "alert", children: error }) : null,
1353
+ /* @__PURE__ */ jsx19("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
1169
1354
  setBusy(true);
1170
1355
  setError("");
1171
1356
  try {
@@ -1175,59 +1360,54 @@ function JoinNamedSeat(props) {
1175
1360
  });
1176
1361
  if (!result.ok || result.applicationId !== props.applicationId || !["booking", "accepted", "paymentPending"].includes(result.state)) throw new Error("acceptance pending");
1177
1362
  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.");
1363
+ } catch (error2) {
1364
+ setError(error2 instanceof Error && error2.message && error2.message !== "acceptance pending" ? error2.message.slice(0, 400) : copy.failure);
1180
1365
  } finally {
1181
1366
  setBusy(false);
1182
1367
  }
1183
- }, children: busy ? "Confirming your seat\u2026" : "Accept seat \u2014 no payment" })
1368
+ }, children: busy ? copy.accepting : copy.accept })
1184
1369
  ] });
1185
1370
  }
1186
1371
 
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);
1372
+ // src/ui/join-invitation.tsx
1373
+ import { useEffect as useEffect8, useState as useState15 } from "preact/hooks";
1374
+ import { jsx as jsx20, jsxs as jsxs20 } from "preact/jsx-runtime";
1375
+ function useJoinInvitation(seatId, api, fallback) {
1376
+ const [invitation, setInvitation] = useState15(null);
1377
+ const [error, setError] = useState15("");
1378
+ const [revision, setRevision] = useState15(0);
1379
+ useEffect8(() => {
1380
+ let live = true;
1381
+ setInvitation(null);
1382
+ setError("");
1383
+ if (!seatId) return;
1384
+ if (!api) {
1385
+ setError(fallback);
1386
+ return;
1202
1387
  }
1203
- };
1388
+ void api(`/api/named-seat/invitation?seat=${encodeURIComponent(seatId)}`).then((value) => {
1389
+ if (value.seatId !== seatId || !value.skuId || !value.purchaserName) throw new Error(fallback);
1390
+ if (live) setInvitation(value);
1391
+ }).catch((error2) => {
1392
+ if (live) setError(error2 instanceof Error ? error2.message.slice(0, 400) : fallback);
1393
+ });
1394
+ return () => {
1395
+ live = false;
1396
+ };
1397
+ }, [seatId, api, revision]);
1398
+ return { invitation: invitation?.seatId === seatId ? invitation : null, error, retry: () => setRevision((value) => value + 1) };
1204
1399
  }
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))
1400
+ function JoinInvitationNotice(props) {
1401
+ return /* @__PURE__ */ jsxs20("aside", { className: "journey-invitation", "aria-label": props.copy.title, children: [
1402
+ /* @__PURE__ */ jsx20("p", { children: /* @__PURE__ */ jsx20("strong", { children: formatChapterCopy(props.copy.from, { purchaserName: props.invitation.purchaserName }) }) }),
1403
+ /* @__PURE__ */ jsx20("p", { children: formatChapterCopy(props.copy.prepaid, { tierName: props.tierName }) })
1221
1404
  ] });
1222
1405
  }
1223
- function tierNeedsPayment(selected, paymentsReady) {
1224
- return selected ? !selected.free && paymentsReady : paymentsReady;
1225
- }
1226
1406
 
1227
1407
  // src/ui/join-pending.ts
1228
- import { useEffect as useEffect7 } from "preact/hooks";
1408
+ import { useEffect as useEffect9 } from "preact/hooks";
1229
1409
  function useJoinPending(state, loadResume, setState) {
1230
- useEffect7(() => {
1410
+ useEffect9(() => {
1231
1411
  if (state.step !== "paymentPending") return;
1232
1412
  let live = true;
1233
1413
  let timer;
@@ -1250,27 +1430,33 @@ function useJoinPending(state, loadResume, setState) {
1250
1430
  }
1251
1431
 
1252
1432
  // src/ui/join.tsx
1253
- import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs17 } from "preact/jsx-runtime";
1433
+ import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs21 } from "preact/jsx-runtime";
1254
1434
  function JoinIsland(props) {
1435
+ return /* @__PURE__ */ jsx21(JourneyFrame, { children: /* @__PURE__ */ jsx21(JoinFlow, { ...props }) });
1436
+ }
1437
+ function JoinFlow(props) {
1255
1438
  const inheritedCopy = useChapterCopy();
1256
1439
  const {
1257
1440
  config,
1258
- children,
1259
1441
  membersHref = "/members/",
1260
1442
  renderStepHeader,
1261
1443
  renderDone,
1262
1444
  renderAccepted,
1263
- renderGift,
1264
- renderSubmit
1445
+ renderGift
1265
1446
  } = props;
1266
1447
  const copy = props.copy ?? config.copy ?? inheritedCopy.join;
1448
+ const seatCopy = copy.namedSeat ?? inheritedCopy.join.namedSeat;
1267
1449
  const tiers = config.tiers ?? [];
1268
1450
  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);
1451
+ const requestedSeat = props.simulation ? void 0 : props.initialNamedSeatId;
1452
+ const invitationState = useJoinInvitation(requestedSeat, props.namedSeatClaimApi, seatCopy.unavailable);
1453
+ const invitation = invitationState.invitation;
1454
+ const selectedTierId = invitation?.skuId ?? tierSelection.selectedTierId;
1455
+ const selectedTier = tiers.find((tier) => tier.id === selectedTierId) ?? tierSelection.selected;
1456
+ const [state, setState] = useState16(requestedSeat ? { step: "form" } : props.initialState ?? { step: "form" });
1457
+ const [error, setError] = useState16(null);
1458
+ const [submitting, setSubmitting] = useState16(false);
1459
+ const [giftOfferId, setGiftOfferId] = useState16(props.initialGiftOfferId ?? null);
1274
1460
  const simulatedPayment = props.simulation ? props.simulation.payment ?? {
1275
1461
  lineItems: {
1276
1462
  standardCents: selectedTier?.priceCents ?? 0,
@@ -1280,9 +1466,9 @@ function JoinIsland(props) {
1280
1466
  } : void 0;
1281
1467
  const simulatedBooking = props.simulation ? props.simulation.booking ?? { slots: [], timezone: "UTC" } : void 0;
1282
1468
  const submissionId = useRef3(null);
1283
- useEffect8(() => {
1469
+ useEffect10(() => {
1284
1470
  if (props.simulation) return;
1285
- const resolver = props.loadResume ?? (() => {
1471
+ const resolver = requestedSeat ? () => invitation?.applicationId ? loadJoinResume(invitation.applicationId) : Promise.resolve(null) : props.loadResume ?? (() => {
1286
1472
  if (typeof window === "undefined") return Promise.resolve(null);
1287
1473
  const applicationId = new URL(window.location.href).searchParams.get("application");
1288
1474
  return applicationId ? loadJoinResume(applicationId) : Promise.resolve(null);
@@ -1296,17 +1482,21 @@ function JoinIsland(props) {
1296
1482
  return () => {
1297
1483
  live = false;
1298
1484
  };
1299
- }, [props.loadResume, props.simulation]);
1300
- useJoinPending(state, props.loadResume, setState);
1485
+ }, [props.loadResume, props.simulation, requestedSeat, invitation?.applicationId]);
1486
+ useJoinPending(state, requestedSeat ? void 0 : props.loadResume, setState);
1301
1487
  const submit = async (e) => {
1302
1488
  e.preventDefault();
1303
- if (submitting || props.submitDisabled === true) return;
1489
+ if (submitting || props.submitDisabled === true || requestedSeat && !invitation) return;
1304
1490
  setSubmitting(true);
1305
1491
  setError(null);
1306
1492
  try {
1307
1493
  const fields = collectFormFields(new FormData(e.currentTarget));
1308
1494
  fields.submissionId = submissionId.current ??= crypto.randomUUID();
1309
- if (giftOfferId) fields.giftOfferId = giftOfferId;
1495
+ if (invitation) {
1496
+ fields.namedSeatId = invitation.seatId;
1497
+ fields.tierId = invitation.skuId;
1498
+ fields.email = invitation.recipientEmail;
1499
+ } else if (giftOfferId) fields.giftOfferId = giftOfferId;
1310
1500
  else if (selectedTierId) fields.tierId = selectedTierId;
1311
1501
  if (props.simulation) {
1312
1502
  setState({
@@ -1317,6 +1507,16 @@ function JoinIsland(props) {
1317
1507
  });
1318
1508
  return;
1319
1509
  }
1510
+ if (invitation && props.namedSeatClaimApi) {
1511
+ const result = await props.namedSeatClaimApi("/api/applications", {
1512
+ method: "POST",
1513
+ headers: { "content-type": "application/json" },
1514
+ body: JSON.stringify(fields)
1515
+ });
1516
+ if (!result.id) throw new Error(copy.form.submitFailed);
1517
+ setState(joinStateFromApplicationResponse(result, true, false));
1518
+ return;
1519
+ }
1320
1520
  const res = await fetch("/api/applications", {
1321
1521
  method: "POST",
1322
1522
  headers: { "content-type": "application/json" },
@@ -1335,50 +1535,66 @@ function JoinIsland(props) {
1335
1535
  setSubmitting(false);
1336
1536
  }
1337
1537
  };
1338
- const header = renderStepHeader?.({
1339
- state,
1340
- ..."applicationId" in state ? { applicationId: state.applicationId } : {}
1341
- });
1538
+ if (requestedSeat && !invitation) return /* @__PURE__ */ jsxs21("section", { className: "card", "aria-live": "polite", children: [
1539
+ /* @__PURE__ */ jsx21("h2", { children: seatCopy.title }),
1540
+ /* @__PURE__ */ jsx21("p", { role: invitationState.error ? "alert" : "status", children: invitationState.error || seatCopy.loading }),
1541
+ invitationState.error ? /* @__PURE__ */ jsx21("button", { type: "button", onClick: invitationState.retry, children: seatCopy.retry }) : null
1542
+ ] });
1543
+ const header = /* @__PURE__ */ jsxs21(Fragment9, { children: [
1544
+ invitation ? /* @__PURE__ */ jsx21(
1545
+ JoinInvitationNotice,
1546
+ {
1547
+ invitation,
1548
+ copy: seatCopy,
1549
+ tierName: selectedTier?.name ?? ""
1550
+ }
1551
+ ) : null,
1552
+ renderStepHeader?.({
1553
+ state,
1554
+ ...invitation ? { invitation } : {},
1555
+ ..."applicationId" in state ? { applicationId: state.applicationId } : {}
1556
+ })
1557
+ ] });
1342
1558
  if (state.step === "closed") {
1343
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1559
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1344
1560
  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 })
1561
+ /* @__PURE__ */ jsxs21("section", { className: "card", "aria-label": "Application closed", children: [
1562
+ /* @__PURE__ */ jsx21("h2", { children: "This application is closed" }),
1563
+ /* @__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." }),
1564
+ /* @__PURE__ */ jsx21("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1349
1565
  ] })
1350
1566
  ] });
1351
1567
  }
1352
1568
  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 })
1569
+ const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs21("div", { className: "join-done card", children: [
1570
+ /* @__PURE__ */ jsx21("div", { className: "card-label", children: copy.done.label }),
1571
+ /* @__PURE__ */ jsx21("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
1572
+ /* @__PURE__ */ jsx21("p", { className: "meeting-note", children: copy.done.calendarInvite }),
1573
+ /* @__PURE__ */ jsx21("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
1358
1574
  ] });
1359
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1575
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1360
1576
  header,
1361
1577
  body
1362
1578
  ] });
1363
1579
  }
1364
1580
  if (state.step === "accepted") {
1365
- const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx17(JoinAccepted, { copy, membersHref });
1366
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1581
+ const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx21(JoinAccepted, { copy, membersHref });
1582
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1367
1583
  header,
1368
1584
  body
1369
1585
  ] });
1370
1586
  }
1371
1587
  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: [
1588
+ const body = renderGift?.(state) ?? /* @__PURE__ */ jsx21(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
1589
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1374
1590
  header,
1375
1591
  body
1376
1592
  ] });
1377
1593
  }
1378
1594
  if (state.step === "booking") {
1379
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1595
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1380
1596
  header,
1381
- /* @__PURE__ */ jsx17(
1597
+ /* @__PURE__ */ jsx21(
1382
1598
  JoinBooking,
1383
1599
  {
1384
1600
  applicationId: state.applicationId,
@@ -1393,23 +1609,24 @@ function JoinIsland(props) {
1393
1609
  }
1394
1610
  if (state.step === "payment") {
1395
1611
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation && !state.giftPurchase) {
1396
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1612
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1397
1613
  header,
1398
- /* @__PURE__ */ jsx17(
1614
+ /* @__PURE__ */ jsx21(
1399
1615
  JoinNamedSeat,
1400
1616
  {
1401
1617
  api: props.namedSeatClaimApi,
1402
1618
  applicationId: state.applicationId,
1403
1619
  seatId: props.initialNamedSeatId,
1620
+ copy: seatCopy,
1404
1621
  onState: setState
1405
1622
  }
1406
1623
  )
1407
1624
  ] });
1408
1625
  }
1409
1626
  const Payment = state.giftPurchase || props.simulation ? PaymentStep : MembershipPaymentStep;
1410
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1627
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1411
1628
  header,
1412
- props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx17(
1629
+ props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx21(
1413
1630
  JoinGiftClaim,
1414
1631
  {
1415
1632
  api: props.giftClaimApi,
@@ -1420,7 +1637,7 @@ function JoinIsland(props) {
1420
1637
  onError: () => setError(copy.form.unexpectedFailure)
1421
1638
  }
1422
1639
  ) : null,
1423
- /* @__PURE__ */ jsx17(
1640
+ /* @__PURE__ */ jsx21(
1424
1641
  Payment,
1425
1642
  {
1426
1643
  applicationId: state.applicationId,
@@ -1449,39 +1666,42 @@ function JoinIsland(props) {
1449
1666
  }
1450
1667
  if (state.step === "paymentPending") {
1451
1668
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation) {
1452
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1669
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1453
1670
  header,
1454
- /* @__PURE__ */ jsx17(
1671
+ /* @__PURE__ */ jsx21(
1455
1672
  JoinNamedSeat,
1456
1673
  {
1457
1674
  api: props.namedSeatClaimApi,
1458
1675
  applicationId: state.applicationId,
1459
1676
  seatId: props.initialNamedSeatId,
1677
+ copy: seatCopy,
1460
1678
  onState: setState
1461
1679
  }
1462
1680
  )
1463
1681
  ] });
1464
1682
  }
1465
- return /* @__PURE__ */ jsxs17(Fragment8, { children: [
1683
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1466
1684
  header,
1467
- /* @__PURE__ */ jsx17("p", { className: "pay-status", role: "status", children: copy.payment.pending })
1685
+ /* @__PURE__ */ jsx21("p", { className: "pay-status", role: "status", children: copy.payment.pending })
1468
1686
  ] });
1469
1687
  }
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: [
1688
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
1473
1689
  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
- ] })
1690
+ /* @__PURE__ */ jsx21(
1691
+ JoinApplicationForm,
1692
+ {
1693
+ props,
1694
+ copy,
1695
+ invitation,
1696
+ requestedSeat,
1697
+ tierSelection,
1698
+ giftOfferId,
1699
+ selectGift: setGiftOfferId,
1700
+ submitting,
1701
+ error,
1702
+ submit
1703
+ }
1704
+ )
1485
1705
  ] });
1486
1706
  }
1487
1707
 
@@ -1657,7 +1877,7 @@ function membershipPageDocument(input, options = {}) {
1657
1877
  }
1658
1878
 
1659
1879
  // src/ui/membership-cards.tsx
1660
- import { jsx as jsx18, jsxs as jsxs18 } from "preact/jsx-runtime";
1880
+ import { jsx as jsx22, jsxs as jsxs22 } from "preact/jsx-runtime";
1661
1881
  var MEMBERSHIP_CARD_TOKEN_DEFAULTS = Object.freeze({
1662
1882
  "--ui-membership-grid-gap": "1.4rem",
1663
1883
  "--ui-membership-card-padding": "2rem 1.7rem 1.8rem",
@@ -1726,67 +1946,67 @@ function membershipApplicationHref(applicationPath, tierId) {
1726
1946
  }
1727
1947
  function TierAction(props) {
1728
1948
  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: [
1949
+ return /* @__PURE__ */ jsxs22("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
1730
1950
  props.label,
1731
- /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", children: "\u2192" })
1951
+ /* @__PURE__ */ jsx22("span", { "aria-hidden": "true", children: "\u2192" })
1732
1952
  ] });
1733
1953
  }
1734
1954
  if (props.mode === "preview") {
1735
- return /* @__PURE__ */ jsxs18("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
1955
+ return /* @__PURE__ */ jsxs22("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
1736
1956
  props.label,
1737
- /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", children: "\u2192" })
1957
+ /* @__PURE__ */ jsx22("span", { "aria-hidden": "true", children: "\u2192" })
1738
1958
  ] });
1739
1959
  }
1740
- return /* @__PURE__ */ jsxs18("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
1960
+ return /* @__PURE__ */ jsxs22("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
1741
1961
  props.label,
1742
- /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", children: "\u2192" })
1962
+ /* @__PURE__ */ jsx22("span", { "aria-hidden": "true", children: "\u2192" })
1743
1963
  ] });
1744
1964
  }
1745
1965
  function TierCard(props) {
1746
1966
  const { tier, presentation } = props;
1747
1967
  const summary = presentation.summaryMarkdown ?? tier.blurb;
1748
1968
  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" })
1969
+ return /* @__PURE__ */ jsxs22("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
1970
+ presentation.badge ? /* @__PURE__ */ jsx22("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
1971
+ /* @__PURE__ */ jsx22("h3", { className: "chapter-membership-name", children: tier.name }),
1972
+ /* @__PURE__ */ jsxs22("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
1973
+ presentation.capacity ? /* @__PURE__ */ jsxs22("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
1974
+ /* @__PURE__ */ jsx22("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
1975
+ /* @__PURE__ */ jsx22("small", { children: presentation.capacity.label ?? "available" })
1756
1976
  ] }) : 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
1977
+ /* @__PURE__ */ jsxs22("div", { className: "chapter-membership-price-stack", children: [
1978
+ presentation.priceEyebrow ? /* @__PURE__ */ jsx22("p", { className: "chapter-membership-price-eyebrow", children: presentation.priceEyebrow }) : tier.founding ? /* @__PURE__ */ jsx22("p", { className: "chapter-membership-price-eyebrow", children: "Founding member rate" }) : null,
1979
+ /* @__PURE__ */ jsxs22("div", { className: "chapter-membership-price-row", children: [
1980
+ tier.founding ? /* @__PURE__ */ jsx22("span", { className: "chapter-membership-compare", children: fmtMoney(tier.priceCents) }) : presentation.compareAtPriceCents !== void 0 ? /* @__PURE__ */ jsx22("span", { className: "chapter-membership-compare", children: fmtMoney(presentation.compareAtPriceCents) }) : null,
1981
+ /* @__PURE__ */ jsx22("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.founding ? tier.founding.dueTodayCents : tier.priceCents) }),
1982
+ !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx22("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
1763
1983
  ] })
1764
1984
  ] })
1765
1985
  ] }),
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 })
1986
+ presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx22(Markdown2, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
1987
+ summary ? /* @__PURE__ */ jsx22(Markdown2, { className: "chapter-membership-summary", source: summary }) : null,
1988
+ presentation.benefits?.length ? /* @__PURE__ */ jsx22("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs22("li", { children: [
1989
+ /* @__PURE__ */ jsx22("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
1990
+ /* @__PURE__ */ jsx22(Markdown2, { className: "chapter-membership-benefit", source: benefit })
1771
1991
  ] }, `${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 })
1992
+ presentation.callout ? /* @__PURE__ */ jsxs22("aside", { className: "chapter-membership-callout", children: [
1993
+ presentation.callout.label ? /* @__PURE__ */ jsx22("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
1994
+ /* @__PURE__ */ jsx22(Markdown2, { source: presentation.callout.bodyMarkdown })
1775
1995
  ] }) : null,
1776
- /* @__PURE__ */ jsx18(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
1996
+ /* @__PURE__ */ jsx22(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
1777
1997
  ] });
1778
1998
  }
1779
1999
  function MembershipCards(props) {
1780
2000
  const document2 = membershipPageDocument(props.document, { tiers: props.tiers });
1781
2001
  const byId = new Map(props.tiers.map((tier) => [tier.id, tier]));
1782
2002
  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) => {
2003
+ 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: [
2004
+ /* @__PURE__ */ jsx22("style", { children: MEMBERSHIP_CARDS_CSS }),
2005
+ /* @__PURE__ */ jsx22("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
1786
2006
  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);
2007
+ return /* @__PURE__ */ jsx22(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
1788
2008
  }) }),
1789
- document2.footnoteMarkdown ? /* @__PURE__ */ jsx18(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
2009
+ document2.footnoteMarkdown ? /* @__PURE__ */ jsx22(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
1790
2010
  ] });
1791
2011
  }
1792
2012
 
@@ -1800,21 +2020,23 @@ export {
1800
2020
  fmtDate,
1801
2021
  groupSlotsByDay,
1802
2022
  SlotPicker,
2023
+ PaymentStep,
2024
+ JourneyFrame,
2025
+ MemberRestart,
1803
2026
  Rescheduler,
1804
2027
  GiftOfferCards,
1805
- PaymentStep,
1806
2028
  GiftPurchases,
1807
2029
  MemberGifts,
1808
2030
  MemberContentArea,
1809
2031
  MembersArea,
1810
- GiftCodeRedeemer,
1811
- loadJoinResume,
1812
2032
  useTierSelection,
1813
2033
  TierPlaceholder,
1814
2034
  tierNeedsPayment,
2035
+ GiftCodeRedeemer,
2036
+ loadJoinResume,
1815
2037
  JoinIsland,
1816
2038
  MEMBERSHIP_CARD_TOKEN_DEFAULTS,
1817
2039
  membershipApplicationHref,
1818
2040
  MembershipCards
1819
2041
  };
1820
- //# sourceMappingURL=chunk-KZ2AXEZW.js.map
2042
+ //# sourceMappingURL=chunk-OS46ZE46.js.map