@odla-ai/chapter 0.61.0 → 0.63.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,69 +1,20 @@
1
1
  import {
2
2
  DEFAULT_JOIN_COPY,
3
+ dayLabel,
4
+ fmtDate,
5
+ fmtMoney,
3
6
  formatChapterCopy,
7
+ fullLabel,
8
+ groupSlotsByDay,
9
+ timeLabel,
4
10
  useChapterCopy
5
- } from "./chunk-PKVHXKGJ.js";
11
+ } from "./chunk-OXYMVK2O.js";
6
12
  import {
7
13
  collectFormFields
8
14
  } from "./chunk-IVXVECKR.js";
9
15
 
10
16
  // src/ui/slot-picker.tsx
11
17
  import { useMemo, useState } from "preact/hooks";
12
-
13
- // src/ui/datetime.ts
14
- function tzShort(tz) {
15
- try {
16
- const parts = new Intl.DateTimeFormat(void 0, { timeZone: tz, timeZoneName: "short" }).formatToParts(
17
- /* @__PURE__ */ new Date()
18
- );
19
- return parts.find((p) => p.type === "timeZoneName")?.value ?? tz;
20
- } catch {
21
- return tz;
22
- }
23
- }
24
- function dayKey(ms, tz) {
25
- return new Date(ms).toLocaleDateString("en-CA", { timeZone: tz });
26
- }
27
- function dayLabel(ms, tz) {
28
- return new Date(ms).toLocaleDateString(void 0, { timeZone: tz, weekday: "short", month: "short", day: "numeric" });
29
- }
30
- function timeLabel(ms, tz) {
31
- return new Date(ms).toLocaleTimeString(void 0, {
32
- timeZone: tz,
33
- hour: "numeric",
34
- minute: "2-digit",
35
- timeZoneName: "short"
36
- });
37
- }
38
- function fullLabel(ms, tz) {
39
- return new Date(ms).toLocaleString(void 0, {
40
- timeZone: tz,
41
- weekday: "long",
42
- month: "long",
43
- day: "numeric",
44
- hour: "numeric",
45
- minute: "2-digit",
46
- timeZoneName: "short"
47
- });
48
- }
49
- function fmtMoney(cents) {
50
- return "$" + Math.round(cents / 100).toLocaleString();
51
- }
52
- function fmtDate(ms) {
53
- return new Date(ms).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" });
54
- }
55
- function groupSlotsByDay(slots, tz) {
56
- const byDay = /* @__PURE__ */ new Map();
57
- for (const s of slots) {
58
- const k = dayKey(s.startAt, tz);
59
- const bucket = byDay.get(k);
60
- if (bucket) bucket.push(s);
61
- else byDay.set(k, [s]);
62
- }
63
- return byDay;
64
- }
65
-
66
- // src/ui/slot-picker.tsx
67
18
  import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
68
19
  var DEFAULT_CLASSES = {
69
20
  days: "slot-days",
@@ -867,7 +818,7 @@ function MemberRestart(props) {
867
818
  }
868
819
 
869
820
  // src/ui/members.tsx
870
- import { useEffect as useEffect6, useState as useState13 } from "preact/hooks";
821
+ import { useEffect as useEffect11, useState as useState18 } from "preact/hooks";
871
822
 
872
823
  // src/ui/reschedule.tsx
873
824
  import { useState as useState8 } from "preact/hooks";
@@ -1316,7 +1267,8 @@ function MemberContentArea(props) {
1316
1267
  list.push(page);
1317
1268
  result.set(page.navigation.section, list);
1318
1269
  }
1319
- return [...result.entries()];
1270
+ const declared = data?.sections ?? [...result.keys()].map((id) => ({ id, label: id }));
1271
+ return declared.filter((section) => result.has(section.id)).map((section) => [section.label ?? "", result.get(section.id)]);
1320
1272
  }, [data]);
1321
1273
  if (error) return /* @__PURE__ */ jsxs15("p", { className: "meeting-note", role: "alert", children: [
1322
1274
  "Member pages could not be loaded: ",
@@ -1327,8 +1279,8 @@ function MemberContentArea(props) {
1327
1279
  const selected = data.pages.find((page) => page.id === selectedId) ?? data.pages[0];
1328
1280
  const href = selected.upsell && upsellHref ? upsellHref(selected.upsell.tierId, selected) : void 0;
1329
1281
  return /* @__PURE__ */ jsxs15("div", { className: "chapter-member-content", "data-member-content": true, children: [
1330
- /* @__PURE__ */ jsx16("nav", { className: "chapter-member-content-nav", "aria-label": "Member pages", children: sections.map(([section, pages]) => /* @__PURE__ */ jsxs15("section", { className: "chapter-member-content-section", children: [
1331
- /* @__PURE__ */ jsx16("h2", { children: section }),
1282
+ /* @__PURE__ */ jsx16("nav", { className: "chapter-member-content-nav", "aria-label": "Member pages", children: sections.map(([section, pages], index) => /* @__PURE__ */ jsxs15("section", { className: "chapter-member-content-section", children: [
1283
+ section ? /* @__PURE__ */ jsx16("h2", { children: section }) : null,
1332
1284
  /* @__PURE__ */ jsx16("ul", { children: pages.map((page) => /* @__PURE__ */ jsx16("li", { "data-parent-page": page.navigation.parentId ?? void 0, children: /* @__PURE__ */ jsx16(
1333
1285
  "button",
1334
1286
  {
@@ -1338,7 +1290,7 @@ function MemberContentArea(props) {
1338
1290
  children: pageLabel(page)
1339
1291
  }
1340
1292
  ) }, page.id)) })
1341
- ] }, section)) }),
1293
+ ] }, `${index}:${section}`)) }),
1342
1294
  /* @__PURE__ */ jsxs15("article", { className: "chapter-member-content-page", "data-access": selected.access, children: [
1343
1295
  /* @__PURE__ */ jsxs15("header", { children: [
1344
1296
  /* @__PURE__ */ jsxs15("div", { className: "card-label", children: [
@@ -1349,7 +1301,7 @@ function MemberContentArea(props) {
1349
1301
  /* @__PURE__ */ jsx16("h1", { children: selected.title }),
1350
1302
  selected.summary ? /* @__PURE__ */ jsx16("p", { children: selected.summary }) : null
1351
1303
  ] }),
1352
- selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx16(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs15("div", { className: "chapter-member-content-locked", children: [
1304
+ selected.access === "allowed" && selected.kind === "feature" ? /* @__PURE__ */ jsx16(Fragment4, { children: props.renderFeature?.(selected) ?? null }) : selected.access === "allowed" && selected.kind === "placeholder" ? /* @__PURE__ */ jsx16(MemberPlaceholderBody, { page: selected }) : selected.access === "allowed" && selected.bodyMarkdown ? /* @__PURE__ */ jsx16(Markdown, { source: selected.bodyMarkdown }) : /* @__PURE__ */ jsxs15("div", { className: "chapter-member-content-locked", children: [
1353
1305
  selected.teaserMarkdown ? /* @__PURE__ */ jsx16(Markdown, { source: selected.teaserMarkdown }) : /* @__PURE__ */ jsx16("p", { children: "This page is not included in your current membership." }),
1354
1306
  selected.upsell ? /* @__PURE__ */ jsxs15("aside", { className: "chapter-member-content-upsell", "aria-label": selected.upsell.label ?? "Membership option", children: [
1355
1307
  /* @__PURE__ */ jsx16("h2", { children: selected.upsell.label ?? selected.upsell.tierName }),
@@ -1361,14 +1313,986 @@ function MemberContentArea(props) {
1361
1313
  ] })
1362
1314
  ] });
1363
1315
  }
1316
+ var placeholderBadge = (page) => page.placeholder?.badge ?? (page.placeholder?.availableYear ? `Coming in ${page.placeholder.availableYear}` : "Coming soon");
1317
+ function MemberPlaceholderBody({ page }) {
1318
+ return /* @__PURE__ */ jsxs15("div", { className: "chapter-member-placeholder", "data-placeholder": true, children: [
1319
+ /* @__PURE__ */ jsx16("div", { className: "card-label", children: placeholderBadge(page) }),
1320
+ page.bodyMarkdown ? /* @__PURE__ */ jsx16(Markdown, { source: page.bodyMarkdown }) : null,
1321
+ page.placeholder?.actionLabel ? /* @__PURE__ */ jsx16("button", { className: "btn secondary", type: "button", disabled: true, children: page.placeholder.actionLabel }) : null
1322
+ ] });
1323
+ }
1324
+
1325
+ // src/ui/member-portal.tsx
1326
+ import { useEffect as useEffect10, useMemo as useMemo3, useState as useState17 } from "preact/hooks";
1327
+
1328
+ // src/ui/member-portal-nav.tsx
1329
+ import { ThemeScope } from "@odla-ai/ui/components";
1330
+
1331
+ // src/ui/member-portal-icons.tsx
1332
+ import { jsx as jsx17 } from "preact/jsx-runtime";
1333
+ var PATHS = {
1334
+ home: ["M3 10.5 12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z"],
1335
+ course: ["M22 10 12 5 2 10l10 5 10-5z", "M6 12v5c3 2 9 2 12 0v-5"],
1336
+ library: ["M4 19.5V4.5A1.5 1.5 0 0 1 5.5 3H20v18H5.5A1.5 1.5 0 0 1 4 19.5z", "M8 7h8"],
1337
+ deals: ["M3 17l6-6 4 4 8-8", "M14 7h7v7"],
1338
+ training: ["M9 11l3 3 8-8", "M20 12v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9"],
1339
+ events: ["M5 4h14a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z", "M16 2v4M8 2v4M3 10h18"],
1340
+ members: ["M9 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8z", "M2 21a7 7 0 0 1 14 0", "M16 4a4 4 0 0 1 0 8", "M22 21a7 7 0 0 0-4-6.3"],
1341
+ share: ["M12 3v12", "m7 8 5-5 5 5", "M5 14v5a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-5"],
1342
+ profile: ["M12 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8z", "M4 21a8 8 0 0 1 16 0"],
1343
+ document: ["M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z", "M14 3v6h6", "M8 13h8M8 17h5"],
1344
+ calendar: ["M5 4h14a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z", "M16 2v4M8 2v4M3 10h18"],
1345
+ chat: ["M21 12a8 8 0 0 1-11.6 7.1L3 21l1.9-6.4A8 8 0 1 1 21 12z"],
1346
+ star: ["m12 3 2.8 5.7 6.2.9-4.5 4.4 1.1 6.2L12 17.3l-5.6 2.9 1.1-6.2L3 9.6l6.2-.9z"],
1347
+ lock: ["M6 11h12a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2z", "M8 11V7a4 4 0 0 1 8 0v4"],
1348
+ menu: ["M4 7h16M4 12h16M4 17h16"],
1349
+ close: ["M6 6l12 12M18 6 6 18"]
1350
+ };
1351
+ function PortalIcon({ name, size = 18, label }) {
1352
+ return /* @__PURE__ */ jsx17(
1353
+ "svg",
1354
+ {
1355
+ width: size,
1356
+ height: size,
1357
+ viewBox: "0 0 24 24",
1358
+ fill: "none",
1359
+ stroke: "currentColor",
1360
+ "stroke-width": "1.8",
1361
+ "stroke-linecap": "round",
1362
+ "stroke-linejoin": "round",
1363
+ "aria-hidden": label ? void 0 : "true",
1364
+ "aria-label": label,
1365
+ role: label ? "img" : void 0,
1366
+ focusable: "false",
1367
+ children: PATHS[name].map((d) => /* @__PURE__ */ jsx17("path", { d }, d))
1368
+ }
1369
+ );
1370
+ }
1371
+
1372
+ // src/ui/member-portal-routing.ts
1373
+ var PROFILE_SEGMENT = "profile";
1374
+ var trimSlashes = (value) => value.replace(/^\/+|\/+$/g, "");
1375
+ function routeFromLocation(location2, routing) {
1376
+ let segment = "";
1377
+ if (routing.mode === "hash") segment = trimSlashes(decodeURIComponent(location2.hash.replace(/^#/, "")));
1378
+ else {
1379
+ const base = `/${trimSlashes(routing.basePath)}`;
1380
+ const path = location2.pathname;
1381
+ if (path === base || path.startsWith(`${base}/`)) segment = trimSlashes(decodeURIComponent(path.slice(base.length)));
1382
+ }
1383
+ if (!segment) return { kind: "home" };
1384
+ if (segment === PROFILE_SEGMENT) return { kind: "profile" };
1385
+ return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(segment) ? { kind: "page", slug: segment } : { kind: "home" };
1386
+ }
1387
+ function hrefFor(route, routing) {
1388
+ const base = `/${trimSlashes(routing.basePath)}`.replace(/^\/$/, "");
1389
+ const segment = route.kind === "home" ? "" : route.kind === "profile" ? PROFILE_SEGMENT : route.slug;
1390
+ if (routing.mode === "hash") return segment ? `${base}/#${segment}` : `${base}/`;
1391
+ return segment ? `${base}/${segment}` : `${base}/`;
1392
+ }
1393
+ var sameRoute = (a, b) => a.kind === b.kind && (a.kind !== "page" || a.slug === b.slug);
1394
+
1395
+ // src/ui/member-portal-nav.tsx
1396
+ import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs16 } from "preact/jsx-runtime";
1397
+ function PortalAvatar({ me, className }) {
1398
+ return /* @__PURE__ */ jsx18("span", { className: `chapter-portal-avatar${className ? ` ${className}` : ""}`, children: me.photoUrl ? /* @__PURE__ */ jsx18("img", { src: me.photoUrl, alt: "" }) : me.initials });
1399
+ }
1400
+ function navTag(page, copy) {
1401
+ if (page.access === "locked") return { kind: "lock", text: copy.shell.locked };
1402
+ if (page.navigation.tag === "new") return { kind: "new", text: copy.pages.tagNew };
1403
+ if (page.kind === "placeholder") {
1404
+ const year = page.placeholder?.availableYear;
1405
+ return { kind: "soon", text: year ? String(year) : copy.pages.tagSoon };
1406
+ }
1407
+ return null;
1408
+ }
1409
+ function Groups({ content, route, copy, onNavigate }) {
1410
+ const sections = content.sections ?? [...new Set(content.pages.map((page) => page.navigation.section))].map((id) => ({ id, label: id }));
1411
+ const item = (target, icon, label, tag) => /* @__PURE__ */ jsxs16("button", { type: "button", className: "chapter-portal-nav-item", "aria-current": sameRoute(route, target) ? "page" : void 0, onClick: () => onNavigate(target), children: [
1412
+ /* @__PURE__ */ jsx18(PortalIcon, { name: icon }),
1413
+ /* @__PURE__ */ jsx18("span", { children: label }),
1414
+ tag?.kind === "lock" ? /* @__PURE__ */ jsx18("span", { className: "chapter-portal-tag", "data-tag": "lock", title: tag.text, children: /* @__PURE__ */ jsx18(PortalIcon, { name: "lock", size: 12, label: tag.text }) }) : tag ? /* @__PURE__ */ jsx18("span", { className: "chapter-portal-tag", "data-tag": tag.kind, children: tag.text }) : null
1415
+ ] });
1416
+ return /* @__PURE__ */ jsxs16(Fragment5, { children: [
1417
+ /* @__PURE__ */ jsx18("div", { children: item({ kind: "home" }, "home", copy.shell.home) }),
1418
+ sections.map((section) => {
1419
+ const pages = content.pages.filter((page) => page.navigation.section === section.id && !page.navigation.parentId);
1420
+ if (!pages.length) return null;
1421
+ return /* @__PURE__ */ jsxs16("div", { children: [
1422
+ section.label ? /* @__PURE__ */ jsx18("div", { className: "chapter-portal-group-label", children: section.label }) : null,
1423
+ pages.map((page) => /* @__PURE__ */ jsx18("div", { children: item({ kind: "page", slug: page.slug }, page.navigation.icon ?? "document", page.title, navTag(page, copy)) }, page.id))
1424
+ ] }, section.id);
1425
+ })
1426
+ ] });
1427
+ }
1428
+ function Account(props) {
1429
+ const { me, email, route, copy, adminHref, onNavigate, onSignOut } = props;
1430
+ return /* @__PURE__ */ jsxs16("div", { className: "chapter-portal-account", children: [
1431
+ /* @__PURE__ */ jsxs16("button", { type: "button", className: "chapter-portal-nav-item", "aria-current": route.kind === "profile" ? "page" : void 0, onClick: () => onNavigate({ kind: "profile" }), children: [
1432
+ /* @__PURE__ */ jsx18(PortalAvatar, { me }),
1433
+ /* @__PURE__ */ jsx18("span", { children: copy.shell.yourProfile })
1434
+ ] }),
1435
+ /* @__PURE__ */ jsx18("div", { children: copy.shell.signedInAs }),
1436
+ /* @__PURE__ */ jsx18("div", { children: email }),
1437
+ /* @__PURE__ */ jsxs16("div", { style: { marginTop: ".5rem", display: "flex", gap: "1rem", flexWrap: "wrap" }, children: [
1438
+ /* @__PURE__ */ jsx18("button", { type: "button", className: "chapter-portal-link", onClick: onSignOut, children: copy.shell.signOut }),
1439
+ adminHref ? /* @__PURE__ */ jsx18("a", { href: adminHref, children: copy.shell.adminConsole }) : null
1440
+ ] })
1441
+ ] });
1442
+ }
1443
+ function PortalNav(props) {
1444
+ const island = props.sidebarTokens ? void 0 : "ui-invert";
1445
+ return /* @__PURE__ */ jsxs16(Fragment5, { children: [
1446
+ /* @__PURE__ */ jsxs16(ThemeScope, { tokens: props.sidebarTokens, className: `chapter-portal-side ${island ?? ""}`, role: "complementary", "aria-label": props.copy.shell.navLabel, children: [
1447
+ /* @__PURE__ */ jsx18("div", { className: "chapter-portal-brand", children: props.brand }),
1448
+ /* @__PURE__ */ jsx18(Groups, { ...props }),
1449
+ /* @__PURE__ */ jsx18(Account, { ...props })
1450
+ ] }),
1451
+ /* @__PURE__ */ jsxs16(ThemeScope, { tokens: props.sidebarTokens, className: `chapter-portal-mobile ${island ?? ""}`, children: [
1452
+ /* @__PURE__ */ jsxs16("div", { className: "chapter-portal-mobile-bar", children: [
1453
+ /* @__PURE__ */ jsx18("div", { className: "chapter-portal-brand", children: props.brand }),
1454
+ /* @__PURE__ */ jsx18(
1455
+ "button",
1456
+ {
1457
+ type: "button",
1458
+ className: "chapter-portal-burger",
1459
+ "aria-expanded": props.menuOpen,
1460
+ "aria-label": props.menuOpen ? props.copy.shell.closeMenu : props.copy.shell.openMenu,
1461
+ onClick: props.onToggleMenu,
1462
+ children: /* @__PURE__ */ jsx18(PortalIcon, { name: props.menuOpen ? "close" : "menu", size: 22 })
1463
+ }
1464
+ )
1465
+ ] }),
1466
+ props.menuOpen ? /* @__PURE__ */ jsxs16("nav", { className: "chapter-portal-mobile-menu", "aria-label": props.copy.shell.navLabel, children: [
1467
+ /* @__PURE__ */ jsx18(Groups, { ...props }),
1468
+ /* @__PURE__ */ jsx18(Account, { ...props })
1469
+ ] }) : null
1470
+ ] })
1471
+ ] });
1472
+ }
1473
+
1474
+ // src/ui/member-portal-pages.tsx
1475
+ import { Markdown as Markdown2 } from "@odla-ai/ui/components";
1476
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs17 } from "preact/jsx-runtime";
1477
+ var upsellOf = (page, ctx) => {
1478
+ const tierName = page.upsell?.tierName;
1479
+ const tierId = page.upsell?.tierId ?? (page.audience.kind === "tiers" ? page.audience.tierIds[0] : void 0);
1480
+ const href = tierId && ctx.upsellHref ? ctx.upsellHref(tierId, page) : void 0;
1481
+ return { tierName, href, cta: page.upsell?.ctaLabel ?? ctx.copy.pages.upgrade };
1482
+ };
1483
+ function LockedPill({ page, ctx }) {
1484
+ const { tierName } = upsellOf(page, ctx);
1485
+ return /* @__PURE__ */ jsxs17("span", { className: "chapter-portal-pill", children: [
1486
+ /* @__PURE__ */ jsx19(PortalIcon, { name: "lock", size: 12 }),
1487
+ tierName ? formatChapterCopy(ctx.copy.pages.includedWith, { tierName }) : ctx.copy.shell.locked
1488
+ ] });
1489
+ }
1490
+ function UpgradeLink({ page, ctx, variant }) {
1491
+ const { href, cta } = upsellOf(page, ctx);
1492
+ return href ? /* @__PURE__ */ jsx19("a", { className: "chapter-portal-btn", "data-variant": variant, href, target: "_blank", rel: "noopener", children: cta }) : null;
1493
+ }
1494
+ function LockedScreen({ page, ctx }) {
1495
+ return /* @__PURE__ */ jsxs17("div", { className: "chapter-portal-card chapter-portal-soon", "data-access": "locked", children: [
1496
+ /* @__PURE__ */ jsx19(LockedPill, { page, ctx }),
1497
+ /* @__PURE__ */ jsx19("h2", { children: page.title }),
1498
+ page.summary ? /* @__PURE__ */ jsx19("p", { children: page.summary }) : null,
1499
+ page.teaserMarkdown ? /* @__PURE__ */ jsx19(Markdown2, { source: page.teaserMarkdown }) : null,
1500
+ page.upsell?.bodyMarkdown ? /* @__PURE__ */ jsx19(Markdown2, { source: page.upsell.bodyMarkdown }) : null,
1501
+ /* @__PURE__ */ jsx19("div", { className: "chapter-portal-btns", style: { justifyContent: "center", marginTop: "1.25rem" }, children: /* @__PURE__ */ jsx19(UpgradeLink, { page, ctx }) })
1502
+ ] });
1503
+ }
1504
+ function PlaceholderScreen({ page }) {
1505
+ return /* @__PURE__ */ jsxs17("div", { className: "chapter-portal-card chapter-portal-soon", "data-placeholder": true, children: [
1506
+ /* @__PURE__ */ jsx19("span", { className: "chapter-portal-pill", children: placeholderBadge(page) }),
1507
+ /* @__PURE__ */ jsx19("h2", { children: page.title }),
1508
+ page.summary ? /* @__PURE__ */ jsx19("p", { children: page.summary }) : null,
1509
+ page.bodyMarkdown ? /* @__PURE__ */ jsx19(Markdown2, { source: page.bodyMarkdown }) : null,
1510
+ page.placeholder?.actionLabel ? /* @__PURE__ */ jsx19("div", { className: "chapter-portal-btns", style: { justifyContent: "center", marginTop: "1.25rem" }, children: /* @__PURE__ */ jsx19("button", { className: "chapter-portal-btn", type: "button", disabled: true, children: page.placeholder.actionLabel }) }) : null
1511
+ ] });
1512
+ }
1513
+ function PortalPage({ page, ctx, features }) {
1514
+ if (page.access === "locked") return /* @__PURE__ */ jsx19(LockedScreen, { page, ctx });
1515
+ if (page.kind === "placeholder") return /* @__PURE__ */ jsx19(PlaceholderScreen, { page });
1516
+ if (page.kind === "feature" && page.feature) {
1517
+ const feature = features[page.feature.name];
1518
+ if (feature) return /* @__PURE__ */ jsx19(Fragment6, { children: feature.page(page, ctx) });
1519
+ }
1520
+ return /* @__PURE__ */ jsxs17("article", { className: "chapter-portal-card", children: [
1521
+ /* @__PURE__ */ jsx19("h2", { children: page.title }),
1522
+ page.summary ? /* @__PURE__ */ jsx19("p", { children: page.summary }) : null,
1523
+ page.bodyMarkdown ? /* @__PURE__ */ jsx19(Markdown2, { source: page.bodyMarkdown }) : null
1524
+ ] });
1525
+ }
1526
+ function PortalTile({ page, ctx }) {
1527
+ return /* @__PURE__ */ jsxs17("div", { className: "chapter-portal-card chapter-portal-tile", "data-access": page.access, children: [
1528
+ /* @__PURE__ */ jsx19(PortalIcon, { name: page.navigation.icon ?? "document", size: 22 }),
1529
+ /* @__PURE__ */ jsx19("h3", { children: page.title }),
1530
+ page.summary ? /* @__PURE__ */ jsx19("p", { children: page.summary }) : null,
1531
+ page.access === "locked" ? /* @__PURE__ */ jsx19(LockedPill, { page, ctx }) : /* @__PURE__ */ jsx19("span", { className: "chapter-portal-pill", style: { alignSelf: "flex-start" }, children: placeholderBadge(page) })
1532
+ ] });
1533
+ }
1534
+ function DefaultHome({ ctx, features, slots }) {
1535
+ const panels = ctx.content.pages.filter((page) => page.kind === "feature" && page.feature && features[page.feature.name]?.home);
1536
+ const soon = ctx.content.pages.filter((page) => page.kind === "placeholder");
1537
+ return /* @__PURE__ */ jsxs17("div", { className: "chapter-portal-stack", children: [
1538
+ slots.top?.(ctx) ?? null,
1539
+ panels.map((page) => {
1540
+ const panel = features[page.feature.name].home(page, ctx);
1541
+ if (page.access !== "locked") return /* @__PURE__ */ jsx19("div", { children: panel }, page.id);
1542
+ return /* @__PURE__ */ jsxs17("div", { className: "chapter-portal-locked-wrap", style: { position: "relative" }, children: [
1543
+ /* @__PURE__ */ jsx19("div", { "aria-hidden": "true", inert: true, style: { opacity: 0.35, filter: "grayscale(1)", pointerEvents: "none" }, children: panel }),
1544
+ /* @__PURE__ */ jsx19("div", { style: { position: "absolute", inset: 0, display: "grid", placeItems: "center", padding: "1rem" }, children: /* @__PURE__ */ jsxs17("div", { className: "chapter-portal-card", style: { maxWidth: "26rem", textAlign: "center" }, children: [
1545
+ /* @__PURE__ */ jsx19(LockedPill, { page, ctx }),
1546
+ /* @__PURE__ */ jsx19("h3", { children: page.title }),
1547
+ page.summary ? /* @__PURE__ */ jsx19("p", { children: page.summary }) : null,
1548
+ /* @__PURE__ */ jsx19(UpgradeLink, { page, ctx })
1549
+ ] }) })
1550
+ ] }, page.id);
1551
+ }),
1552
+ slots.middle?.(ctx) ?? null,
1553
+ soon.length ? /* @__PURE__ */ jsxs17("section", { children: [
1554
+ /* @__PURE__ */ jsx19("h2", { style: { fontFamily: "var(--ui-font-display,inherit)", fontWeight: 400 }, children: ctx.copy.pages.comingToPortal }),
1555
+ /* @__PURE__ */ jsx19("div", { className: "chapter-portal-tiles", children: soon.map((page) => /* @__PURE__ */ jsx19("div", { children: slots.tile?.(page, ctx) ?? /* @__PURE__ */ jsx19(PortalTile, { page, ctx }) }, page.id)) })
1556
+ ] }) : null
1557
+ ] });
1558
+ }
1559
+
1560
+ // src/ui/member-profile-page.tsx
1561
+ import { useEffect as useEffect6, useState as useState13 } from "preact/hooks";
1562
+ import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs18 } from "preact/jsx-runtime";
1563
+ var memberMonth = (at) => new Date(at).toLocaleString("en-US", { month: "long", year: "numeric", timeZone: "UTC" });
1564
+ var startingPrompts = (view) => view.profile.prompts.length ? view.profile.prompts : view.promptChoices.slice(0, view.maxPrompts).map((prompt) => ({ prompt, answer: "" }));
1565
+ function choosePrompt(prompts, prompt, max) {
1566
+ if (prompts.some((entry) => entry.prompt === prompt) || max === 0) return prompts;
1567
+ return prompts.length < max ? [...prompts, { prompt, answer: "" }] : [...prompts.slice(0, max - 1), { prompt, answer: "" }];
1568
+ }
1569
+ async function resizePhoto(file) {
1570
+ const bitmap = await createImageBitmap(file);
1571
+ const side = Math.min(bitmap.width, bitmap.height);
1572
+ const canvas = document.createElement("canvas");
1573
+ canvas.width = canvas.height = 400;
1574
+ canvas.getContext("2d").drawImage(bitmap, (bitmap.width - side) / 2, (bitmap.height - side) / 2, side, side, 0, 0, 400, 400);
1575
+ return new Promise((resolve, reject) => canvas.toBlob((blob) => blob ? resolve(blob) : reject(new Error("encode failed")), "image/jpeg", 0.9));
1576
+ }
1577
+ function MemberProfilePage({ ctx, middle, extras }) {
1578
+ const copy = ctx.copy.profile;
1579
+ const [view, setView] = useState13(null);
1580
+ const [draft, setDraft] = useState13(null);
1581
+ const [status, setStatus] = useState13(null);
1582
+ const [busy, setBusy] = useState13(false);
1583
+ const load = async () => {
1584
+ try {
1585
+ setView(await ctx.api("/api/member/profile"));
1586
+ } catch {
1587
+ setStatus(ctx.copy.shell.loadFailed);
1588
+ }
1589
+ };
1590
+ useEffect6(() => {
1591
+ void load();
1592
+ }, []);
1593
+ if (!view) return /* @__PURE__ */ jsx20("p", { children: status ?? ctx.copy.shell.loading });
1594
+ const prompts = draft?.prompts ?? startingPrompts(view);
1595
+ const edit = () => setDraft({
1596
+ preferredName: view.profile.preferredName ?? ctx.me.displayName,
1597
+ location: view.profile.location ?? "",
1598
+ introducedByText: view.profile.introducedByText ?? "",
1599
+ prompts: startingPrompts(view)
1600
+ });
1601
+ const save = async () => {
1602
+ if (!draft) return;
1603
+ setBusy(true);
1604
+ try {
1605
+ const next = await ctx.api("/api/member/profile", { method: "PUT", headers: { "content-type": "application/json" }, body: JSON.stringify({
1606
+ expectedRevision: view.revision,
1607
+ preferredName: draft.preferredName,
1608
+ location: draft.location,
1609
+ introducedByText: draft.introducedByText,
1610
+ prompts: draft.prompts
1611
+ }) });
1612
+ setView(next);
1613
+ setDraft(null);
1614
+ setStatus(copy.saved);
1615
+ await ctx.reloadMe();
1616
+ } catch (error) {
1617
+ setStatus(String(error).includes("409") ? copy.conflict : copy.saveFailed);
1618
+ } finally {
1619
+ setBusy(false);
1620
+ }
1621
+ };
1622
+ const upload = async (file) => {
1623
+ if (!file) return;
1624
+ setBusy(true);
1625
+ setStatus(copy.photoUploading);
1626
+ try {
1627
+ if (file.size > 10 * 1024 * 1024) throw new Error("too large");
1628
+ await ctx.api("/api/member/profile/photo", { method: "POST", headers: { "content-type": "image/jpeg" }, body: await resizePhoto(file) });
1629
+ setStatus(null);
1630
+ await ctx.reloadMe();
1631
+ } catch {
1632
+ setStatus(copy.photoFailed);
1633
+ } finally {
1634
+ setBusy(false);
1635
+ }
1636
+ };
1637
+ const remaining = view.promptChoices.filter((choice) => !prompts.some((entry) => entry.prompt === choice));
1638
+ return /* @__PURE__ */ jsxs18("div", { className: "chapter-portal-stack", "data-portal-profile": true, children: [
1639
+ /* @__PURE__ */ jsxs18("div", { className: "chapter-portal-card", style: { display: "flex", gap: "1.25rem", alignItems: "center", flexWrap: "wrap" }, children: [
1640
+ /* @__PURE__ */ jsxs18("div", { style: { textAlign: "center" }, children: [
1641
+ /* @__PURE__ */ jsx20(PortalAvatar, { me: ctx.me, className: "chapter-portal-profile-photo" }),
1642
+ view.photos ? /* @__PURE__ */ jsxs18("label", { className: "chapter-portal-btn", "data-variant": "ghost", style: { marginTop: ".6rem", padding: ".35rem .8rem", fontSize: ".78rem" }, children: [
1643
+ ctx.me.photoUrl ? copy.changePhoto : copy.uploadPhoto,
1644
+ /* @__PURE__ */ jsx20("input", { type: "file", accept: "image/jpeg,image/png", hidden: true, disabled: busy, onChange: (event) => void upload(event.currentTarget.files?.[0]) })
1645
+ ] }) : null
1646
+ ] }),
1647
+ /* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: "12rem" }, children: [
1648
+ /* @__PURE__ */ jsx20("div", { className: "chapter-portal-group-label", style: { padding: 0 }, children: copy.eyebrow }),
1649
+ draft ? /* @__PURE__ */ jsx20(
1650
+ "input",
1651
+ {
1652
+ "aria-label": copy.namePlaceholder,
1653
+ placeholder: copy.namePlaceholder,
1654
+ value: draft.preferredName,
1655
+ maxLength: 80,
1656
+ onInput: (event) => setDraft({ ...draft, preferredName: event.currentTarget.value })
1657
+ }
1658
+ ) : /* @__PURE__ */ jsx20("h2", { style: { margin: ".2rem 0" }, children: ctx.me.displayName }),
1659
+ /* @__PURE__ */ jsxs18("p", { style: { margin: 0, color: "var(--ui-text-muted)" }, children: [
1660
+ ctx.me.tier && ctx.me.memberSince ? formatChapterCopy(copy.memberSince, { tierName: ctx.me.tier.name, month: memberMonth(ctx.me.memberSince) }) : null,
1661
+ draft ? /* @__PURE__ */ jsx20(
1662
+ "input",
1663
+ {
1664
+ "aria-label": copy.locationPlaceholder,
1665
+ placeholder: copy.locationPlaceholder,
1666
+ value: draft.location,
1667
+ maxLength: 80,
1668
+ onInput: (event) => setDraft({ ...draft, location: event.currentTarget.value })
1669
+ }
1670
+ ) : ctx.me.location ? /* @__PURE__ */ jsxs18(Fragment7, { children: [
1671
+ " \xB7 ",
1672
+ ctx.me.location
1673
+ ] }) : null
1674
+ ] })
1675
+ ] }),
1676
+ /* @__PURE__ */ jsxs18("div", { className: "chapter-portal-btns", children: [
1677
+ draft ? /* @__PURE__ */ jsx20("button", { type: "button", className: "chapter-portal-btn", disabled: busy, onClick: () => void save(), children: busy ? copy.saving : copy.saveProfile }) : /* @__PURE__ */ jsx20("button", { type: "button", className: "chapter-portal-btn", "data-variant": "ghost", onClick: edit, children: copy.editProfile }),
1678
+ draft ? /* @__PURE__ */ jsx20("button", { type: "button", className: "chapter-portal-btn", "data-variant": "ghost", disabled: busy, onClick: () => setDraft(null), children: copy.cancel }) : null
1679
+ ] })
1680
+ ] }),
1681
+ status ? /* @__PURE__ */ jsx20("p", { role: "status", children: status }) : null,
1682
+ view.maxPrompts > 0 && view.promptChoices.length ? /* @__PURE__ */ jsxs18("div", { className: "chapter-portal-card", children: [
1683
+ /* @__PURE__ */ jsx20("h2", { children: copy.promptsTitle }),
1684
+ /* @__PURE__ */ jsx20("p", { style: { color: "var(--ui-text-muted)" }, children: formatChapterCopy(copy.promptsNote, { count: String(view.maxPrompts) }) }),
1685
+ /* @__PURE__ */ jsx20("div", { className: "chapter-portal-tiles", children: prompts.map((entry, index) => /* @__PURE__ */ jsxs18("div", { className: "chapter-portal-card", children: [
1686
+ /* @__PURE__ */ jsx20("q", { children: entry.prompt }),
1687
+ draft ? /* @__PURE__ */ jsx20(
1688
+ "textarea",
1689
+ {
1690
+ "aria-label": entry.prompt,
1691
+ maxLength: 280,
1692
+ rows: 3,
1693
+ value: entry.answer,
1694
+ onInput: (event) => setDraft({ ...draft, prompts: draft.prompts.map((p, i) => i === index ? { ...p, answer: event.currentTarget.value } : p) })
1695
+ }
1696
+ ) : /* @__PURE__ */ jsx20("p", { children: entry.answer || /* @__PURE__ */ jsx20("span", { style: { color: "var(--ui-text-muted)" }, children: copy.answerEmpty }) })
1697
+ ] }, entry.prompt)) }),
1698
+ /* @__PURE__ */ jsx20("div", { className: "chapter-portal-btns", style: { marginTop: "1rem" }, children: remaining.map((choice) => /* @__PURE__ */ jsx20(
1699
+ "button",
1700
+ {
1701
+ type: "button",
1702
+ className: "chapter-portal-pill",
1703
+ style: { cursor: "pointer", background: "transparent" },
1704
+ onClick: () => {
1705
+ const base = draft ?? { preferredName: view.profile.preferredName ?? ctx.me.displayName, location: view.profile.location ?? "", introducedByText: view.profile.introducedByText ?? "", prompts };
1706
+ setDraft({ ...base, prompts: choosePrompt(base.prompts, choice, view.maxPrompts) });
1707
+ },
1708
+ children: formatChapterCopy(copy.addPrompt, { prompt: choice })
1709
+ },
1710
+ choice
1711
+ )) })
1712
+ ] }) : null,
1713
+ middle,
1714
+ /* @__PURE__ */ jsxs18("div", { className: "chapter-portal-card", children: [
1715
+ /* @__PURE__ */ jsx20("h2", { children: copy.howYouFoundUs }),
1716
+ view.referredBy ? /* @__PURE__ */ jsxs18("p", { children: [
1717
+ /* @__PURE__ */ jsx20("strong", { children: copy.referredBy }),
1718
+ " \xB7 ",
1719
+ /* @__PURE__ */ jsx20("strong", { children: view.referredBy.name }),
1720
+ " ",
1721
+ /* @__PURE__ */ jsxs18("span", { style: { color: "var(--ui-text-muted)" }, children: [
1722
+ "\xB7 ",
1723
+ copy.referredByNote
1724
+ ] })
1725
+ ] }) : null,
1726
+ /* @__PURE__ */ jsxs18("label", { style: { display: "block" }, children: [
1727
+ /* @__PURE__ */ jsx20("span", { children: copy.introducedBy }),
1728
+ draft ? /* @__PURE__ */ jsx20(
1729
+ "input",
1730
+ {
1731
+ placeholder: copy.introducedByPlaceholder,
1732
+ value: draft.introducedByText,
1733
+ maxLength: 120,
1734
+ onInput: (event) => setDraft({ ...draft, introducedByText: event.currentTarget.value })
1735
+ }
1736
+ ) : /* @__PURE__ */ jsx20("p", { children: view.profile.introducedByText || /* @__PURE__ */ jsx20("span", { style: { color: "var(--ui-text-muted)" }, children: copy.introducedByPlaceholder }) })
1737
+ ] })
1738
+ ] }),
1739
+ extras
1740
+ ] });
1741
+ }
1742
+
1743
+ // src/ui/member-portal-styles.ts
1744
+ var MEMBER_PORTAL_CSS = `
1745
+ .chapter-portal{display:grid;grid-template-columns:clamp(232px,22vw,272px) minmax(0,1fr);min-height:100vh;background:var(--ui-bg,#f4efe6);color:var(--ui-text,#2f3e34);font-family:var(--ui-font-sans,inherit);line-height:1.55;overflow-x:clip}
1746
+ .chapter-portal *,.chapter-portal *::before,.chapter-portal *::after{box-sizing:border-box}
1747
+ .chapter-portal-side{position:sticky;top:0;height:100vh;overflow-y:auto;display:flex;flex-direction:column;gap:1.5rem;padding:1.5rem 1rem;background:var(--ui-surface,#2f3e34);color:var(--ui-text,#fbf8f2)}
1748
+ .chapter-portal-brand{display:flex;align-items:center;gap:.65rem;padding:0 .5rem;font-family:var(--ui-font-display,inherit);font-size:1.35rem;line-height:1.1;white-space:nowrap}
1749
+ .chapter-portal-brand :where(img,svg){width:30px;height:30px;flex:none}
1750
+ .chapter-portal-group-label{padding:0 .75rem;margin-bottom:.35rem;font-size:.68rem;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-text-muted,#b8c4bb)}
1751
+ .chapter-portal-nav-item{display:flex;align-items:center;gap:.7rem;width:100%;padding:.55rem .75rem;border:0;border-radius:var(--ui-radius-sm,8px);background:transparent;color:inherit;font:inherit;font-size:.95rem;text-align:left;text-decoration:none;cursor:pointer}
1752
+ .chapter-portal-nav-item:hover{background:color-mix(in srgb,var(--ui-text,#fff) 8%,transparent)}
1753
+ .chapter-portal-nav-item[aria-current=page]{background:color-mix(in srgb,var(--ui-text,#fff) 14%,transparent);font-weight:600}
1754
+ .chapter-portal-nav-item>span:first-of-type{flex:1;min-width:0}
1755
+ .chapter-portal-tag{border:1px solid var(--ui-border,rgba(255,255,255,.2));border-radius:999px;padding:.05rem .5rem;font-size:.62rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--ui-text-muted,#b8c4bb)}
1756
+ .chapter-portal-tag[data-tag=new]{border-color:transparent;background:var(--ui-accent,#7cb83f);color:var(--ui-on-accent,#2f3e34)}
1757
+ .chapter-portal-account{margin-top:auto;padding:.75rem;border-top:1px solid var(--ui-border,rgba(255,255,255,.15));font-size:.85rem;color:var(--ui-text-muted,#b8c4bb);overflow-wrap:anywhere}
1758
+ .chapter-portal-account .chapter-portal-nav-item{margin:0 -.75rem .75rem;width:calc(100% + 1.5rem);color:var(--ui-text,#fff)}
1759
+ .chapter-portal-account button.chapter-portal-link,.chapter-portal-account a{display:inline;padding:0;border:0;background:none;color:var(--ui-text,#fff);font:inherit;text-decoration:underline;cursor:pointer}
1760
+ .chapter-portal-avatar{display:inline-grid;place-items:center;flex:none;width:28px;height:28px;border-radius:50%;overflow:hidden;background:var(--ui-accent-soft,rgba(124,184,63,.2));color:var(--ui-text,inherit);font-size:.72rem;font-weight:700;letter-spacing:.04em}
1761
+ .chapter-portal-avatar img{width:100%;height:100%;object-fit:cover}
1762
+ .chapter-portal-avatar.chapter-portal-profile-photo{width:96px;height:96px;font-size:1.6rem}
1763
+ .chapter-portal-locked-wrap{position:relative}
1764
+ .chapter-portal-main{min-width:0;padding:clamp(1rem,3vw,2.5rem);display:flex;flex-direction:column;gap:1.5rem}
1765
+ .chapter-portal-top{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}
1766
+ .chapter-portal-top h1{margin:0;font-family:var(--ui-font-display,inherit);font-weight:400;font-size:clamp(1.8rem,4vw,2.4rem);line-height:1.1}
1767
+ .chapter-portal-back{padding:0;border:0;background:none;color:var(--ui-text,inherit);font:inherit;font-weight:600;cursor:pointer}
1768
+ .chapter-portal-top-right{display:flex;align-items:center;gap:.75rem;margin-left:auto}
1769
+ .chapter-portal-pill{display:inline-flex;align-items:center;gap:.35rem;border:1px solid var(--ui-border,#e2dccf);border-radius:999px;padding:.2rem .7rem;font-size:.66rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--ui-text-muted,#7e8e84);white-space:nowrap}
1770
+ .chapter-portal-me{padding:0;border:0;background:none;cursor:pointer;border-radius:50%}
1771
+ .chapter-portal-me .chapter-portal-avatar{width:40px;height:40px;font-size:.85rem}
1772
+ .chapter-portal-card{background:var(--ui-surface,#fbf8f2);border:1px solid var(--ui-border,#e2dccf);border-radius:var(--ui-radius-lg,14px);padding:clamp(1.1rem,3vw,1.6rem);min-width:0}
1773
+ .chapter-portal-card h2{margin:.4rem 0 .5rem;font-family:var(--ui-font-display,inherit);font-weight:400;font-size:1.6rem;line-height:1.15}
1774
+ .chapter-portal-stack{display:flex;flex-direction:column;gap:1.25rem}
1775
+ .chapter-portal-soon{text-align:center;padding:clamp(2rem,6vw,3.5rem) 1.5rem}
1776
+ .chapter-portal-soon p{max-width:36rem;margin:.5rem auto 0;color:var(--ui-text-muted,#7e8e84)}
1777
+ .chapter-portal-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));gap:1rem}
1778
+ .chapter-portal-tile{display:flex;flex-direction:column;gap:.5rem}
1779
+ .chapter-portal-tile h3{margin:0;font-size:1.05rem}
1780
+ .chapter-portal-tile p{margin:0;color:var(--ui-text-muted,#7e8e84);font-size:.92rem;flex:1}
1781
+ .chapter-portal-btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;min-height:42px;padding:.6rem 1.2rem;border:1px solid var(--ui-text,#2f3e34);border-radius:999px;background:var(--ui-text,#2f3e34);color:var(--ui-bg,#fbf8f2);font:inherit;font-size:.92rem;font-weight:600;text-decoration:none;cursor:pointer}
1782
+ .chapter-portal-btn[data-variant=ghost]{background:transparent;color:var(--ui-text,#2f3e34)}
1783
+ .chapter-portal-btn[data-variant=accent]{background:var(--ui-accent,#7cb83f);border-color:var(--ui-accent,#7cb83f);color:var(--ui-on-accent,#2f3e34)}
1784
+ .chapter-portal-btn:disabled{opacity:.5;cursor:not-allowed}
1785
+ .chapter-portal-btns{display:flex;flex-wrap:wrap;gap:.6rem}
1786
+ .chapter-portal :where(button,a,input,textarea,select):focus-visible{outline:3px solid var(--ui-focus,var(--ui-accent,#7cb83f));outline-offset:2px}
1787
+ .chapter-portal-modal-back{position:fixed;inset:0;z-index:50;display:grid;place-items:center;padding:1rem;background:color-mix(in srgb,var(--ui-text,#2f3e34) 45%,transparent)}
1788
+ .chapter-portal-modal{position:relative;width:min(100%,46rem);max-height:90vh;overflow:auto}
1789
+ .chapter-portal-modal-x{position:absolute;top:.75rem;right:.75rem;width:40px;height:40px;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}
1790
+ .chapter-portal-link-box{display:flex;align-items:center;justify-content:space-between;gap:.75rem;flex-wrap:wrap;margin:.75rem 0;padding:.5rem .5rem .5rem 1rem;border:1px solid var(--ui-border,#e2dccf);border-radius:999px;background:var(--ui-bg,#f4efe6)}
1791
+ .chapter-portal-link-box code{overflow-wrap:anywhere;font-size:.9rem}
1792
+ .chapter-portal-tally{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.75rem}
1793
+ .chapter-portal-tally[data-tiles="2"]{grid-template-columns:repeat(2,minmax(0,1fr))}
1794
+ .chapter-portal-tally>div{display:flex;flex-direction:column;padding:.9rem;border:1px solid var(--ui-border,#e2dccf);border-radius:var(--ui-radius-sm,10px)}
1795
+ .chapter-portal-tally b{font-family:var(--ui-font-numeral,var(--ui-font-display,inherit));font-size:1.8rem;font-weight:400}
1796
+ .chapter-portal-tally span{font-size:.8rem;color:var(--ui-text-muted,#7e8e84)}
1797
+ .chapter-portal-fallback{margin-top:1rem;padding:1rem;border:1px dashed var(--ui-border-strong,#aeb9b5);border-radius:var(--ui-radius-sm,10px)}
1798
+ .chapter-portal-material{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;padding:.35rem 0;list-style:none}
1799
+ .chapter-portal-material small{color:var(--ui-text-muted,#7e8e84)}
1800
+ .chapter-portal-material a{margin-left:auto}
1801
+ .chapter-portal details{border-top:1px solid var(--ui-border,#e2dccf);padding:.75rem 0}
1802
+ .chapter-portal summary{cursor:pointer}
1803
+ .chapter-portal dl{display:grid;grid-template-columns:auto 1fr;gap:.3rem 1rem;margin:.75rem 0}
1804
+ .chapter-portal dt{color:var(--ui-text-muted,#7e8e84)}
1805
+ .chapter-portal dd{margin:0}
1806
+ .chapter-portal-mobile{display:none}
1807
+ @media (max-width:860px){
1808
+ .chapter-portal{grid-template-columns:minmax(0,1fr)}
1809
+ .chapter-portal-side{display:none}
1810
+ .chapter-portal-mobile{display:block;margin:calc(-1*clamp(1rem,3vw,2.5rem)) calc(-1*clamp(1rem,3vw,2.5rem)) 0;background:var(--ui-surface,#2f3e34);color:var(--ui-text,#fbf8f2)}
1811
+ .chapter-portal-mobile-bar{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.8rem 1rem}
1812
+ .chapter-portal-burger{display:inline-grid;place-items:center;width:44px;height:44px;border:0;border-radius:8px;background:transparent;color:inherit;cursor:pointer}
1813
+ .chapter-portal-mobile-menu{display:flex;flex-direction:column;gap:1rem;padding:0 1rem 1.25rem}
1814
+ .chapter-portal-mobile .chapter-portal-brand{font-size:1.15rem;padding:0}
1815
+ .chapter-portal-modal-back{place-items:end stretch;padding:0}
1816
+ .chapter-portal-modal{width:100%;border-radius:var(--ui-radius-lg,14px) var(--ui-radius-lg,14px) 0 0}
1817
+ }
1818
+ `;
1819
+
1820
+ // src/ui/member-course-page.tsx
1821
+ import { useEffect as useEffect7, useState as useState14 } from "preact/hooks";
1822
+ import { Markdown as Markdown3 } from "@odla-ai/ui/components";
1823
+ import { Fragment as Fragment8, jsx as jsx21, jsxs as jsxs19 } from "preact/jsx-runtime";
1824
+ function useCourse(ctx, courseId) {
1825
+ const [view, setView] = useState14(null);
1826
+ const [error, setError] = useState14(null);
1827
+ const [busy, setBusy] = useState14(false);
1828
+ useEffect7(() => {
1829
+ if (!courseId) return;
1830
+ let live = true;
1831
+ void ctx.api(`/api/member/courses/${encodeURIComponent(courseId)}`).then((v) => live && setView(v)).catch(() => live && setError(ctx.copy.course.failed));
1832
+ return () => {
1833
+ live = false;
1834
+ };
1835
+ }, [courseId]);
1836
+ const act = async (action) => {
1837
+ setBusy(true);
1838
+ setError(null);
1839
+ try {
1840
+ setView(await ctx.api(`/api/member/courses/${encodeURIComponent(courseId)}/${action}`, { method: "POST" }));
1841
+ } catch {
1842
+ setError(ctx.copy.course.failed);
1843
+ } finally {
1844
+ setBusy(false);
1845
+ }
1846
+ };
1847
+ return { view, error, busy, act };
1848
+ }
1849
+ function seatLine(view, copy) {
1850
+ if (view.enrollment?.status === "enrolled") return copy.invitesOnWay;
1851
+ if (view.enrollment?.status === "waitlisted") return formatChapterCopy(copy.onWaitlist, { position: String(view.enrollment.waitlistPosition ?? "") });
1852
+ if (view.course.status === "closed") return copy.closed;
1853
+ return formatChapterCopy(copy.seatsOpen, { left: String(view.course.seatsLeft), capacity: String(view.course.capacity) });
1854
+ }
1855
+ function SeatActions({ ctx, course, details }) {
1856
+ const copy = ctx.copy.course;
1857
+ const view = course.view;
1858
+ const status = view.enrollment?.status;
1859
+ const confirmThen = (action) => {
1860
+ if (window.confirm(copy.confirmGiveUp)) void course.act(action);
1861
+ };
1862
+ return /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-btns", children: [
1863
+ status === "enrolled" ? /* @__PURE__ */ jsx21("span", { className: "chapter-portal-pill", children: copy.youreIn }) : null,
1864
+ status === "enrolled" && view.meetUrl ? /* @__PURE__ */ jsx21("a", { className: "chapter-portal-btn", href: view.meetUrl, target: "_blank", rel: "noopener", children: copy.openMeet }) : null,
1865
+ status === "enrolled" ? /* @__PURE__ */ jsx21("button", { type: "button", className: "chapter-portal-btn", "data-variant": "ghost", disabled: course.busy, onClick: () => confirmThen("withdraw"), children: copy.giveUp }) : null,
1866
+ status === "waitlisted" ? /* @__PURE__ */ jsx21("button", { type: "button", className: "chapter-portal-btn", "data-variant": "ghost", disabled: course.busy, onClick: () => void course.act("withdraw"), children: copy.leaveWaitlist }) : null,
1867
+ !status && view.course.status !== "closed" ? /* @__PURE__ */ jsx21("button", { type: "button", className: "chapter-portal-btn", disabled: course.busy, onClick: () => void course.act("enroll"), children: view.course.status === "full" ? copy.joinWaitlist : copy.reserve }) : null,
1868
+ details ? /* @__PURE__ */ jsx21("button", { type: "button", className: "chapter-portal-btn", "data-variant": "ghost", onClick: details, children: status === "enrolled" ? copy.courseDetails : copy.seeDetails }) : null
1869
+ ] });
1870
+ }
1871
+ var MaterialItem = ({ item, kind, ctx, enrolled }) => /* @__PURE__ */ jsxs19("li", { className: "chapter-portal-material", children: [
1872
+ kind ? /* @__PURE__ */ jsx21("span", { className: "chapter-portal-pill", children: ctx.copy.course[kind] }) : null,
1873
+ /* @__PURE__ */ jsxs19("span", { children: [
1874
+ item.label,
1875
+ item.meta ? /* @__PURE__ */ jsxs19("small", { children: [
1876
+ " \xB7 ",
1877
+ item.meta
1878
+ ] }) : null
1879
+ ] }),
1880
+ enrolled && item.url ? /* @__PURE__ */ jsx21("a", { href: item.url, target: "_blank", rel: "noopener", children: ctx.copy.course.open }) : null
1881
+ ] });
1882
+ function CoursePage({ page, ctx }) {
1883
+ const course = useCourse(ctx, page.feature?.ref);
1884
+ const copy = ctx.copy.course;
1885
+ if (!course.view) return /* @__PURE__ */ jsx21("p", { children: course.error ?? ctx.copy.shell.loading });
1886
+ const { course: c } = course.view;
1887
+ const enrolled = course.view.enrollment?.status === "enrolled";
1888
+ return /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-stack", "data-course-page": true, children: [
1889
+ /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-card ui-invert", style: { display: "grid", gap: "1.25rem", gridTemplateColumns: "repeat(auto-fit,minmax(min(100%,18rem),1fr))" }, children: [
1890
+ /* @__PURE__ */ jsxs19("div", { children: [
1891
+ /* @__PURE__ */ jsx21("h1", { style: { fontFamily: "var(--ui-font-display,inherit)", fontWeight: 400, margin: 0 }, children: c.title }),
1892
+ c.content.basedOn ? /* @__PURE__ */ jsx21("p", { style: { fontStyle: "italic" }, children: c.content.basedOn }) : null,
1893
+ c.content.short ? /* @__PURE__ */ jsx21("p", { children: c.content.short }) : null
1894
+ ] }),
1895
+ /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-card", "data-seat-box": true, children: [
1896
+ !course.view.enrollment ? /* @__PURE__ */ jsx21("div", { className: "chapter-portal-group-label", style: { padding: 0 }, children: copy.reserveYourSeat }) : null,
1897
+ /* @__PURE__ */ jsxs19("dl", { children: [
1898
+ c.content.when ? /* @__PURE__ */ jsxs19(Fragment8, { children: [
1899
+ /* @__PURE__ */ jsx21("dt", { children: copy.when }),
1900
+ /* @__PURE__ */ jsx21("dd", { children: c.content.when })
1901
+ ] }) : null,
1902
+ c.content.dates ?? c.content.start ? /* @__PURE__ */ jsxs19(Fragment8, { children: [
1903
+ /* @__PURE__ */ jsx21("dt", { children: copy.dates }),
1904
+ /* @__PURE__ */ jsx21("dd", { children: c.content.dates ?? c.content.start })
1905
+ ] }) : null,
1906
+ /* @__PURE__ */ jsx21("dt", { children: copy.where }),
1907
+ /* @__PURE__ */ jsx21("dd", { children: copy.googleMeet }),
1908
+ /* @__PURE__ */ jsx21("dt", { children: copy.seats }),
1909
+ /* @__PURE__ */ jsx21("dd", { children: seatLine(course.view, copy) })
1910
+ ] }),
1911
+ /* @__PURE__ */ jsx21(SeatActions, { ctx, course }),
1912
+ !course.view.enrollment ? /* @__PURE__ */ jsx21("p", { style: { color: "var(--ui-text-muted)" }, children: copy.inviteEvery }) : null,
1913
+ course.error ? /* @__PURE__ */ jsx21("p", { role: "alert", children: course.error }) : null
1914
+ ] })
1915
+ ] }),
1916
+ c.guides.length ? /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-card", children: [
1917
+ /* @__PURE__ */ jsx21("h2", { children: copy.guidesTitle }),
1918
+ /* @__PURE__ */ jsx21("div", { className: "chapter-portal-tiles", children: c.guides.map((guide) => /* @__PURE__ */ jsxs19("div", { children: [
1919
+ guide.photoUrl ? /* @__PURE__ */ jsx21("img", { src: guide.photoUrl, alt: guide.name, style: { width: 72, height: 72, borderRadius: "50%", objectFit: "cover" } }) : null,
1920
+ /* @__PURE__ */ jsx21("h3", { children: guide.name }),
1921
+ guide.role ? /* @__PURE__ */ jsx21("div", { style: { color: "var(--ui-text-muted)" }, children: guide.role }) : null,
1922
+ guide.bio ? /* @__PURE__ */ jsx21("p", { children: guide.bio }) : null
1923
+ ] }, guide.name)) })
1924
+ ] }) : null,
1925
+ /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-card", "data-sessions": true, children: [
1926
+ /* @__PURE__ */ jsx21("h2", { children: copy.sessionsTitle }),
1927
+ /* @__PURE__ */ jsx21("p", { style: { color: "var(--ui-text-muted)" }, children: enrolled ? copy.materialsOpen : copy.materialsLocked }),
1928
+ c.sessions.map((session, index) => /* @__PURE__ */ jsxs19("details", { open: enrolled && index === 0, children: [
1929
+ /* @__PURE__ */ jsxs19("summary", { children: [
1930
+ /* @__PURE__ */ jsx21("strong", { children: session.date }),
1931
+ " \xB7 ",
1932
+ session.topic,
1933
+ session.materials.length ? /* @__PURE__ */ jsxs19("small", { children: [
1934
+ " \xB7 ",
1935
+ formatChapterCopy(copy.items, { count: String(session.materials.length) })
1936
+ ] }) : null
1937
+ ] }),
1938
+ session.description ? /* @__PURE__ */ jsx21("p", { children: session.description }) : null,
1939
+ /* @__PURE__ */ jsx21("ul", { children: session.materials.map((item) => /* @__PURE__ */ jsx21(MaterialItem, { item, kind: item.kind, ctx, enrolled }, item.label)) })
1940
+ ] }, session.startsAt))
1941
+ ] }),
1942
+ /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-tiles", children: [
1943
+ c.content.outcomes?.length ? /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-card", children: [
1944
+ /* @__PURE__ */ jsx21("h2", { children: copy.outcomesTitle }),
1945
+ /* @__PURE__ */ jsx21("ul", { children: c.content.outcomes.map((o) => /* @__PURE__ */ jsx21("li", { children: o }, o)) })
1946
+ ] }) : null,
1947
+ c.toolkit.length || c.content.time ? /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-card", children: [
1948
+ /* @__PURE__ */ jsx21("h2", { children: copy.toolkitTitle }),
1949
+ /* @__PURE__ */ jsx21("ul", { children: c.toolkit.map((item) => /* @__PURE__ */ jsx21(MaterialItem, { item, ctx, enrolled }, item.label)) }),
1950
+ c.content.time ? /* @__PURE__ */ jsxs19(Fragment8, { children: [
1951
+ /* @__PURE__ */ jsx21("h2", { children: copy.timeTitle }),
1952
+ /* @__PURE__ */ jsx21("p", { children: c.content.time })
1953
+ ] }) : null
1954
+ ] }) : null
1955
+ ] }),
1956
+ c.content.partnerMarkdown ? /* @__PURE__ */ jsx21("div", { className: "chapter-portal-card", children: /* @__PURE__ */ jsx21(Markdown3, { source: c.content.partnerMarkdown }) }) : null
1957
+ ] });
1958
+ }
1959
+ function CourseHomePanel({ page, ctx }) {
1960
+ const locked = page.access === "locked";
1961
+ const course = useCourse(ctx, locked ? void 0 : page.feature?.ref);
1962
+ const view = course.view;
1963
+ return /* @__PURE__ */ jsxs19("div", { className: "chapter-portal-card", "data-course-panel": true, children: [
1964
+ /* @__PURE__ */ jsx21("div", { className: "chapter-portal-group-label", style: { padding: 0 }, children: ctx.copy.course.eyebrow }),
1965
+ /* @__PURE__ */ jsx21("h2", { children: view?.course.title ?? page.title }),
1966
+ view?.course.content.basedOn ? /* @__PURE__ */ jsx21("p", { style: { fontStyle: "italic" }, children: view.course.content.basedOn }) : null,
1967
+ /* @__PURE__ */ jsx21("p", { children: view?.course.content.short ?? page.summary }),
1968
+ view ? /* @__PURE__ */ jsxs19(Fragment8, { children: [
1969
+ /* @__PURE__ */ jsx21("p", { children: [view.course.content.when, view.course.content.start].filter(Boolean).join(" \xB7 ") }),
1970
+ /* @__PURE__ */ jsx21("p", { children: /* @__PURE__ */ jsx21("strong", { children: seatLine(view, ctx.copy.course) }) }),
1971
+ /* @__PURE__ */ jsx21(SeatActions, { ctx, course, details: () => ctx.navigate({ kind: "page", slug: page.slug }) })
1972
+ ] }) : null
1973
+ ] });
1974
+ }
1975
+
1976
+ // src/ui/member-share-page.tsx
1977
+ import { useEffect as useEffect9, useState as useState16 } from "preact/hooks";
1978
+
1979
+ // src/ui/member-share.ts
1980
+ import { useEffect as useEffect8, useState as useState15 } from "preact/hooks";
1981
+ var articleFor = (word) => /^[aeiou]/i.test(word.trim()) ? "an" : "a";
1982
+ function shareVars(me, referrals) {
1983
+ const tierName = me.tier?.name ?? "";
1984
+ return {
1985
+ tierName,
1986
+ article: articleFor(tierName),
1987
+ inviteUrl: referrals?.link ?? "",
1988
+ firstName: me.firstName ?? me.displayName.split(/\s+/)[0] ?? "",
1989
+ location: me.location ?? "",
1990
+ credit: referrals?.credit ? fmtMoney(referrals.credit.perFriendCents).replace(/\.00$/, "") : ""
1991
+ };
1992
+ }
1993
+ var fill = (template, vars) => formatChapterCopy(template, vars);
1994
+ var linkedinShareUrl = (post, link) => `https://www.linkedin.com/feed/?shareActive=true&text=${encodeURIComponent(`${post}
1995
+
1996
+
1997
+ ${link}`).replace(/'/g, "%27")}`;
1998
+ var mailtoHref = (subject, body) => `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
1999
+ var smsHref = (body, ios) => `${ios ? "sms:&body=" : "sms:?body="}${encodeURIComponent(body)}`;
2000
+ var isIOS = (nav) => /iPad|iPhone|iPod/.test(nav.userAgent) || nav.platform === "MacIntel" && (nav.maxTouchPoints ?? 0) > 1;
2001
+ function useMemberReferrals(api) {
2002
+ const [state, setState] = useState15({ data: null, failed: false });
2003
+ useEffect8(() => {
2004
+ let live = true;
2005
+ void api("/api/member/referrals").then((data) => live && setState({ data, failed: false })).catch(() => live && setState({ data: null, failed: true }));
2006
+ return () => {
2007
+ live = false;
2008
+ };
2009
+ }, [api]);
2010
+ return state;
2011
+ }
2012
+ async function copyText(text, copy) {
2013
+ try {
2014
+ await navigator.clipboard.writeText(text);
2015
+ return copy.copied;
2016
+ } catch {
2017
+ return copy.copyBlocked;
2018
+ }
2019
+ }
2020
+
2021
+ // src/ui/member-share-page.tsx
2022
+ import { Fragment as Fragment9, jsx as jsx22, jsxs as jsxs20 } from "preact/jsx-runtime";
2023
+ function CopyButton({ text, label, ctx, variant }) {
2024
+ const [shown, setShown] = useState16(label);
2025
+ return /* @__PURE__ */ jsx22("button", { type: "button", className: "chapter-portal-btn", "data-variant": variant, onClick: () => void copyText(text, ctx.copy.share).then((result) => {
2026
+ setShown(result);
2027
+ setTimeout(() => setShown(label), 2e3);
2028
+ }), children: shown });
2029
+ }
2030
+ function HowDialog({ ctx, vars, onClose, example }) {
2031
+ const copy = ctx.copy.share;
2032
+ useEffect9(() => {
2033
+ const onKey = (event) => {
2034
+ if (event.key === "Escape") onClose();
2035
+ };
2036
+ document.addEventListener("keydown", onKey);
2037
+ return () => document.removeEventListener("keydown", onKey);
2038
+ }, []);
2039
+ return /* @__PURE__ */ jsx22("div", { className: "chapter-portal-modal-back", "data-portal-dialog": true, onClick: (event) => {
2040
+ if (event.target === event.currentTarget) onClose();
2041
+ }, children: /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-modal chapter-portal-card", role: "dialog", "aria-modal": "true", "aria-labelledby": "chapter-portal-how-title", children: [
2042
+ /* @__PURE__ */ jsx22("button", { type: "button", className: "chapter-portal-modal-x", "aria-label": copy.close, onClick: onClose, children: /* @__PURE__ */ jsx22(PortalIcon, { name: "close", size: 20 }) }),
2043
+ /* @__PURE__ */ jsx22("div", { className: "chapter-portal-group-label", style: { padding: 0 }, children: copy.stepsEyebrow }),
2044
+ /* @__PURE__ */ jsx22("h2", { id: "chapter-portal-how-title", children: copy.officialTitle }),
2045
+ /* @__PURE__ */ jsx22("ol", { children: copy.steps.map((step) => /* @__PURE__ */ jsx22("li", { children: fill(step, vars) }, step)) }),
2046
+ /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-btns", children: [
2047
+ /* @__PURE__ */ jsx22("a", { className: "chapter-portal-btn", href: "https://www.linkedin.com/in/me/", target: "_blank", rel: "noopener", children: copy.openLinkedIn }),
2048
+ /* @__PURE__ */ jsx22(CopyButton, { text: fill(copy.linkedinDescription, vars), label: copy.copyDescription, ctx, variant: "ghost" })
2049
+ ] }),
2050
+ example
2051
+ ] }) });
2052
+ }
2053
+ function OfficialCard({ ctx, referrals, example }) {
2054
+ const [open, setOpen] = useState16(false);
2055
+ const copy = ctx.copy.share;
2056
+ return /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-card", "data-official": true, children: [
2057
+ /* @__PURE__ */ jsx22("h2", { children: copy.officialTitle }),
2058
+ /* @__PURE__ */ jsx22("p", { children: copy.officialBody }),
2059
+ /* @__PURE__ */ jsx22("button", { type: "button", className: "chapter-portal-btn", onClick: () => setOpen(true), children: copy.showMeHow }),
2060
+ open ? /* @__PURE__ */ jsx22(HowDialog, { ctx, vars: shareVars(ctx.me, referrals), onClose: () => setOpen(false), example }) : null
2061
+ ] });
2062
+ }
2063
+ function PostCard({ ctx, referrals }) {
2064
+ const copy = ctx.copy.share;
2065
+ const vars = shareVars(ctx.me, referrals);
2066
+ const post = fill(copy.linkedinPost, vars);
2067
+ return /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-card", "data-post": true, children: [
2068
+ /* @__PURE__ */ jsx22("h2", { children: copy.postTitle }),
2069
+ /* @__PURE__ */ jsx22("p", { children: copy.postBody }),
2070
+ /* @__PURE__ */ jsx22("a", { className: "chapter-portal-btn", href: linkedinShareUrl(post, vars.inviteUrl), target: "_blank", rel: "noopener", children: copy.shareOnLinkedIn }),
2071
+ /* @__PURE__ */ jsxs20("details", { children: [
2072
+ /* @__PURE__ */ jsx22("summary", { children: copy.previewPost }),
2073
+ /* @__PURE__ */ jsx22("p", { children: post }),
2074
+ /* @__PURE__ */ jsx22("p", { children: vars.inviteUrl })
2075
+ ] })
2076
+ ] });
2077
+ }
2078
+ function InviteLinkCard({ ctx, referrals }) {
2079
+ const copy = ctx.copy.share;
2080
+ const vars = shareVars(ctx.me, referrals);
2081
+ const [fallback, setFallback] = useState16(null);
2082
+ const ios = typeof navigator !== "undefined" && isIOS(navigator);
2083
+ const email = fill(copy.emailBody, vars), text = fill(copy.textBody, vars);
2084
+ const tryOpen = (kind) => setTimeout(() => {
2085
+ if (!document.hidden) setFallback(kind);
2086
+ }, 900);
2087
+ return /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-card", "data-invite-link": true, children: [
2088
+ /* @__PURE__ */ jsx22("h2", { children: copy.linkTitle }),
2089
+ /* @__PURE__ */ jsxs20("p", { children: [
2090
+ copy.linkBody,
2091
+ referrals?.credit ? /* @__PURE__ */ jsxs20(Fragment9, { children: [
2092
+ " ",
2093
+ /* @__PURE__ */ jsx22("strong", { children: fill(copy.linkCredit, vars) })
2094
+ ] }) : null
2095
+ ] }),
2096
+ /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-link-box", children: [
2097
+ /* @__PURE__ */ jsx22("code", { children: referrals ? referrals.link.replace(/^https?:\/\//, "") : "\u2026" }),
2098
+ referrals ? /* @__PURE__ */ jsx22(CopyButton, { text: referrals.link, label: copy.copyLink, ctx }) : null
2099
+ ] }),
2100
+ /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-btns", children: [
2101
+ /* @__PURE__ */ jsx22("a", { className: "chapter-portal-btn", "data-variant": "ghost", href: mailtoHref(copy.emailSubject, email), onClick: () => tryOpen("email"), children: copy.emailInvite }),
2102
+ /* @__PURE__ */ jsx22("a", { className: "chapter-portal-btn", "data-variant": "ghost", href: smsHref(text, ios), onClick: () => tryOpen("text"), children: copy.textInvite })
2103
+ ] }),
2104
+ fallback ? /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-fallback", "data-fallback": fallback, children: [
2105
+ /* @__PURE__ */ jsx22("p", { children: fallback === "email" ? copy.fallbackEmail : copy.fallbackText }),
2106
+ /* @__PURE__ */ jsx22("pre", { style: { whiteSpace: "pre-wrap" }, children: fallback === "email" ? `${copy.emailSubject}
2107
+
2108
+ ${email}` : text }),
2109
+ /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-btns", children: [
2110
+ /* @__PURE__ */ jsx22(CopyButton, { text: fallback === "email" ? email : text, label: copy.copyMessage, ctx }),
2111
+ /* @__PURE__ */ jsx22("button", { type: "button", className: "chapter-portal-btn", "data-variant": "ghost", onClick: () => setFallback(null), children: copy.close })
2112
+ ] })
2113
+ ] }) : null,
2114
+ /* @__PURE__ */ jsx22("p", { style: { color: "var(--ui-text-muted)" }, children: copy.inviteNote })
2115
+ ] });
2116
+ }
2117
+ function ReferralsCard({ ctx, referrals }) {
2118
+ const copy = ctx.copy.share;
2119
+ if (!referrals) return null;
2120
+ const vars = shareVars(ctx.me, referrals);
2121
+ const tile = (value, label) => /* @__PURE__ */ jsxs20("div", { children: [
2122
+ /* @__PURE__ */ jsx22("b", { children: value }),
2123
+ /* @__PURE__ */ jsx22("span", { children: label })
2124
+ ] });
2125
+ return /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-card", "data-referrals": true, children: [
2126
+ /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-top", children: [
2127
+ /* @__PURE__ */ jsx22("h2", { children: copy.referralsTitle }),
2128
+ /* @__PURE__ */ jsx22("button", { type: "button", className: "chapter-portal-btn", "data-variant": "ghost", onClick: () => {
2129
+ const share = ctx.content.pages.find((page) => page.kind === "feature" && page.feature?.name === "share");
2130
+ if (share) ctx.navigate({ kind: "page", slug: share.slug });
2131
+ }, children: copy.inviteFriend })
2132
+ ] }),
2133
+ /* @__PURE__ */ jsxs20("p", { children: [
2134
+ copy.referralsBody,
2135
+ referrals.credit ? /* @__PURE__ */ jsxs20(Fragment9, { children: [
2136
+ " ",
2137
+ fill(copy.referralsCredit, vars)
2138
+ ] }) : null
2139
+ ] }),
2140
+ /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-tally", "data-tiles": referrals.credit ? 3 : 2, children: [
2141
+ tile(String(referrals.joined.count), copy.joined),
2142
+ tile(String(referrals.inProcess), copy.inProcess),
2143
+ referrals.credit ? tile(fmtMoney(referrals.credit.offNextYearCents).replace(/\.00$/, ""), copy.offNextYear) : null
2144
+ ] }),
2145
+ referrals.joined.names.length ? /* @__PURE__ */ jsx22("p", { children: fill(copy.joinedWith, { names: referrals.joined.names.join(", ") }) }) : null
2146
+ ] });
2147
+ }
2148
+ function SharePage({ page, ctx, example }) {
2149
+ const { data } = useMemberReferrals(ctx.api);
2150
+ return /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-stack", "data-share-page": true, children: [
2151
+ /* @__PURE__ */ jsxs20("div", { children: [
2152
+ /* @__PURE__ */ jsx22("div", { className: "chapter-portal-group-label", style: { padding: 0 }, children: ctx.copy.share.eyebrow }),
2153
+ /* @__PURE__ */ jsx22("h1", { style: { margin: ".3rem 0", fontFamily: "var(--ui-font-display,inherit)", fontWeight: 400 }, children: page.title }),
2154
+ /* @__PURE__ */ jsx22("p", { style: { maxWidth: "40rem", color: "var(--ui-text-muted)" }, children: page.summary ?? ctx.copy.share.intro })
2155
+ ] }),
2156
+ /* @__PURE__ */ jsx22(OfficialCard, { ctx, referrals: data, example }),
2157
+ /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-tiles", children: [
2158
+ /* @__PURE__ */ jsx22(PostCard, { ctx, referrals: data }),
2159
+ /* @__PURE__ */ jsx22(InviteLinkCard, { ctx, referrals: data })
2160
+ ] })
2161
+ ] });
2162
+ }
2163
+ function ShareHome({ ctx }) {
2164
+ const { data } = useMemberReferrals(ctx.api);
2165
+ return /* @__PURE__ */ jsxs20("section", { className: "chapter-portal-stack", "data-share-home": true, children: [
2166
+ /* @__PURE__ */ jsx22("h2", { style: { fontFamily: "var(--ui-font-display,inherit)", fontWeight: 400, margin: 0 }, children: ctx.copy.share.eyebrow }),
2167
+ /* @__PURE__ */ jsx22(OfficialCard, { ctx, referrals: data }),
2168
+ /* @__PURE__ */ jsxs20("div", { className: "chapter-portal-tiles", children: [
2169
+ /* @__PURE__ */ jsx22(PostCard, { ctx, referrals: data }),
2170
+ /* @__PURE__ */ jsx22(InviteLinkCard, { ctx, referrals: data })
2171
+ ] })
2172
+ ] });
2173
+ }
2174
+ function ProfileReferrals({ ctx }) {
2175
+ const { data } = useMemberReferrals(ctx.api);
2176
+ return /* @__PURE__ */ jsx22(ReferralsCard, { ctx, referrals: data });
2177
+ }
2178
+
2179
+ // src/ui/member-portal-features.tsx
2180
+ import { jsx as jsx23 } from "preact/jsx-runtime";
2181
+ var PACKAGED_PORTAL_FEATURES = {
2182
+ course: { page: (page, ctx) => /* @__PURE__ */ jsx23(CoursePage, { page, ctx }), home: (page, ctx) => /* @__PURE__ */ jsx23(CourseHomePanel, { page, ctx }) },
2183
+ share: { page: (page, ctx) => /* @__PURE__ */ jsx23(SharePage, { page, ctx }), home: (_page, ctx) => /* @__PURE__ */ jsx23(ShareHome, { ctx }) }
2184
+ };
2185
+
2186
+ // src/ui/member-portal.tsx
2187
+ import { jsx as jsx24, jsxs as jsxs21 } from "preact/jsx-runtime";
2188
+ var EMPTY = { pages: [], sections: [], viewer: { tierId: null, admin: false }, revisions: { parent: null, chapter: null } };
2189
+ function MemberPortal(props) {
2190
+ const inherited = useChapterCopy();
2191
+ const copy = props.copy ?? inherited.portal;
2192
+ const routing = { mode: props.routing?.mode ?? "hash", basePath: props.routing?.basePath ?? "/members" };
2193
+ const [content, setContent] = useState17(props.initialContent ?? null);
2194
+ const [route, setRoute] = useState17(props.initialRoute ?? { kind: "home" });
2195
+ const [menuOpen, setMenuOpen] = useState17(false);
2196
+ useEffect10(() => {
2197
+ if (props.initialContent) return;
2198
+ let live = true;
2199
+ void props.api("/api/member-content").then((value) => live && setContent(value)).catch(() => live && setContent(EMPTY));
2200
+ return () => {
2201
+ live = false;
2202
+ };
2203
+ }, [props.api, props.initialContent]);
2204
+ useEffect10(() => {
2205
+ if (typeof window === "undefined" || props.initialRoute) return;
2206
+ const sync = () => setRoute(routeFromLocation(window.location, routing));
2207
+ sync();
2208
+ window.addEventListener("popstate", sync);
2209
+ window.addEventListener("hashchange", sync);
2210
+ return () => {
2211
+ window.removeEventListener("popstate", sync);
2212
+ window.removeEventListener("hashchange", sync);
2213
+ };
2214
+ }, [routing.mode, routing.basePath]);
2215
+ const navigate = (next) => {
2216
+ setRoute(next);
2217
+ setMenuOpen(false);
2218
+ if (typeof window !== "undefined") {
2219
+ window.history.pushState(null, "", hrefFor(next, routing));
2220
+ window.scrollTo?.(0, 0);
2221
+ }
2222
+ };
2223
+ const features = useMemo3(() => ({ ...PACKAGED_PORTAL_FEATURES, ...props.features }), [props.features]);
2224
+ if (!content) return /* @__PURE__ */ jsxs21("div", { className: "chapter-portal", children: [
2225
+ /* @__PURE__ */ jsx24("style", { children: MEMBER_PORTAL_CSS }),
2226
+ /* @__PURE__ */ jsx24("p", { children: copy.shell.loading })
2227
+ ] });
2228
+ const ctx = {
2229
+ api: props.api,
2230
+ me: props.me.portal,
2231
+ email: props.me.email,
2232
+ content,
2233
+ copy,
2234
+ navigate,
2235
+ hrefFor: (target) => hrefFor(target, routing),
2236
+ upsellHref: props.upsellHref,
2237
+ reloadMe: props.reloadMe ?? (async () => {
2238
+ })
2239
+ };
2240
+ const page = route.kind === "page" ? content.pages.find((candidate) => candidate.slug === route.slug) : void 0;
2241
+ const slots = typeof props.home === "function" ? {} : props.home ?? {};
2242
+ const shareAllowed = content.pages.some((candidate) => candidate.kind === "feature" && candidate.feature?.name === "share" && candidate.access === "allowed");
2243
+ const home = /* @__PURE__ */ jsx24(DefaultHome, { ctx, features, slots });
2244
+ const body = route.kind === "home" ? typeof props.home === "function" ? props.home(ctx, home) : home : route.kind === "profile" ? /* @__PURE__ */ jsx24(MemberProfilePage, { ctx, middle: props.profile?.middle ? props.profile.middle(ctx) : shareAllowed ? /* @__PURE__ */ jsx24(ProfileReferrals, { ctx }) : null, extras: props.profile?.extras }) : page ? /* @__PURE__ */ jsx24(PortalPage, { page, ctx, features }) : /* @__PURE__ */ jsx24("div", { className: "chapter-portal-card", children: copy.shell.notFound });
2245
+ const admin = props.me.authorized || props.me.superAdmin ? props.adminHref ?? "/admin/" : void 0;
2246
+ return /* @__PURE__ */ jsxs21("div", { className: "chapter-portal", "data-member-portal": true, "data-route": route.kind, children: [
2247
+ /* @__PURE__ */ jsx24("style", { children: MEMBER_PORTAL_CSS }),
2248
+ /* @__PURE__ */ jsx24(
2249
+ PortalNav,
2250
+ {
2251
+ content,
2252
+ me: ctx.me,
2253
+ email: props.me.email,
2254
+ route,
2255
+ copy,
2256
+ adminHref: admin,
2257
+ onNavigate: navigate,
2258
+ onSignOut: props.signOut,
2259
+ brand: props.brand,
2260
+ sidebarTokens: props.sidebarTokens,
2261
+ menuOpen,
2262
+ onToggleMenu: () => setMenuOpen(!menuOpen)
2263
+ }
2264
+ ),
2265
+ /* @__PURE__ */ jsxs21("main", { className: "chapter-portal-main", children: [
2266
+ /* @__PURE__ */ jsxs21("div", { className: "chapter-portal-top", children: [
2267
+ route.kind === "home" ? /* @__PURE__ */ jsx24("h1", { children: copy.shell.welcomeBack }) : /* @__PURE__ */ jsx24("button", { type: "button", className: "chapter-portal-back", onClick: () => navigate({ kind: "home" }), children: copy.shell.backHome }),
2268
+ /* @__PURE__ */ jsxs21("div", { className: "chapter-portal-top-right", children: [
2269
+ ctx.me.tier ? /* @__PURE__ */ jsx24("span", { className: "chapter-portal-pill", children: ctx.me.tier.name }) : null,
2270
+ /* @__PURE__ */ jsx24(
2271
+ "button",
2272
+ {
2273
+ type: "button",
2274
+ className: "chapter-portal-me",
2275
+ "aria-label": copy.shell.yourProfile,
2276
+ title: copy.shell.yourProfile,
2277
+ "aria-current": route.kind === "profile" ? "page" : void 0,
2278
+ onClick: () => navigate({ kind: "profile" }),
2279
+ children: /* @__PURE__ */ jsx24(PortalAvatar, { me: ctx.me })
2280
+ }
2281
+ )
2282
+ ] })
2283
+ ] }),
2284
+ body
2285
+ ] })
2286
+ ] });
2287
+ }
1364
2288
 
1365
2289
  // src/ui/members.tsx
1366
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs16 } from "preact/jsx-runtime";
2290
+ import { Fragment as Fragment10, jsx as jsx25, jsxs as jsxs22 } from "preact/jsx-runtime";
1367
2291
  function card(copy, kicker, body) {
1368
- return /* @__PURE__ */ jsxs16("div", { className: "card", children: [
1369
- /* @__PURE__ */ jsx17("div", { className: "card-label", children: copy.provisional.cardLabel }),
1370
- /* @__PURE__ */ jsxs16("div", { className: "meeting-block", children: [
1371
- /* @__PURE__ */ jsx17("div", { className: "meeting-kicker", children: kicker }),
2292
+ return /* @__PURE__ */ jsxs22("div", { className: "card", children: [
2293
+ /* @__PURE__ */ jsx25("div", { className: "card-label", children: copy.provisional.cardLabel }),
2294
+ /* @__PURE__ */ jsxs22("div", { className: "meeting-block", children: [
2295
+ /* @__PURE__ */ jsx25("div", { className: "meeting-kicker", children: kicker }),
1372
2296
  body
1373
2297
  ] })
1374
2298
  ] });
@@ -1379,29 +2303,32 @@ function ProvisionalCard(props) {
1379
2303
  return card(
1380
2304
  copy,
1381
2305
  copy.provisional.applicationNeeded,
1382
- /* @__PURE__ */ jsxs16(Fragment5, { children: [
1383
- /* @__PURE__ */ jsx17("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
1384
- /* @__PURE__ */ jsx17("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
2306
+ /* @__PURE__ */ jsxs22(Fragment10, { children: [
2307
+ /* @__PURE__ */ jsx25("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
2308
+ /* @__PURE__ */ jsx25("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
1385
2309
  ] })
1386
2310
  );
1387
2311
  }
2312
+ if (application.status === "declined") {
2313
+ return card(copy, copy.provisional.declined, /* @__PURE__ */ jsx25("div", { className: "meeting-note", children: copy.provisional.declinedBody }));
2314
+ }
1388
2315
  if (application.status === "refunded") {
1389
2316
  return card(
1390
2317
  copy,
1391
2318
  copy.provisional.refunded,
1392
- /* @__PURE__ */ jsx17("div", { className: "meeting-note", children: copy.provisional.refundedBody })
2319
+ /* @__PURE__ */ jsx25("div", { className: "meeting-note", children: copy.provisional.refundedBody })
1393
2320
  );
1394
2321
  }
1395
- const membership = application.paid ? /* @__PURE__ */ jsx17("div", { className: "meeting-note", children: application.renewalAt ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) }) : copy.provisional.active }) : null;
2322
+ const membership = application.paid ? /* @__PURE__ */ jsx25("div", { className: "meeting-note", children: application.renewalAt ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) }) : copy.provisional.active }) : null;
1396
2323
  if (application.meetingAt) {
1397
2324
  return card(
1398
2325
  copy,
1399
2326
  copy.provisional.introductionCall,
1400
- /* @__PURE__ */ jsxs16(Fragment5, { children: [
1401
- /* @__PURE__ */ jsx17("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
1402
- /* @__PURE__ */ jsx17("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
1403
- application.meetUrl ? /* @__PURE__ */ jsx17("div", { className: "meeting-note", children: /* @__PURE__ */ jsx17("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
1404
- /* @__PURE__ */ jsx17(
2327
+ /* @__PURE__ */ jsxs22(Fragment10, { children: [
2328
+ /* @__PURE__ */ jsx25("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
2329
+ /* @__PURE__ */ jsx25("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
2330
+ application.meetUrl ? /* @__PURE__ */ jsx25("div", { className: "meeting-note", children: /* @__PURE__ */ jsx25("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
2331
+ /* @__PURE__ */ jsx25(
1405
2332
  Rescheduler,
1406
2333
  {
1407
2334
  api,
@@ -1418,9 +2345,9 @@ function ProvisionalCard(props) {
1418
2345
  return card(
1419
2346
  copy,
1420
2347
  copy.provisional.bookCall,
1421
- /* @__PURE__ */ jsxs16(Fragment5, { children: [
1422
- /* @__PURE__ */ jsx17("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
1423
- /* @__PURE__ */ jsx17(
2348
+ /* @__PURE__ */ jsxs22(Fragment10, { children: [
2349
+ /* @__PURE__ */ jsx25("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
2350
+ /* @__PURE__ */ jsx25(
1424
2351
  Rescheduler,
1425
2352
  {
1426
2353
  api,
@@ -1447,8 +2374,8 @@ function MembersArea(props) {
1447
2374
  renderProvisional
1448
2375
  } = props;
1449
2376
  const copy = props.copy ?? inheritedCopy.members;
1450
- const [me, setMe] = useState13(null);
1451
- const [error, setError] = useState13(false);
2377
+ const [me, setMe] = useState18(null);
2378
+ const [error, setError] = useState18(false);
1452
2379
  const reload = async () => {
1453
2380
  try {
1454
2381
  setMe(await api("/api/me"));
@@ -1456,25 +2383,44 @@ function MembersArea(props) {
1456
2383
  setError(true);
1457
2384
  }
1458
2385
  };
1459
- useEffect6(() => {
2386
+ useEffect11(() => {
1460
2387
  void reload();
1461
2388
  }, []);
1462
- if (error) return /* @__PURE__ */ jsx17("p", { className: "meeting-note", children: copy.loadFailed });
1463
- if (!me) return /* @__PURE__ */ jsx17("p", { className: "meeting-note", children: inheritedCopy.common.loading });
2389
+ if (error) return /* @__PURE__ */ jsx25("p", { className: "meeting-note", children: copy.loadFailed });
2390
+ if (!me) return /* @__PURE__ */ jsx25("p", { className: "meeting-note", children: inheritedCopy.common.loading });
2391
+ if (props.portal && me.memberAccess === true && me.portal) {
2392
+ const gifts = /* @__PURE__ */ jsx25(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment });
2393
+ return /* @__PURE__ */ jsx25(
2394
+ MemberPortal,
2395
+ {
2396
+ ...props.portal,
2397
+ api,
2398
+ me: { ...me, portal: me.portal },
2399
+ signOut,
2400
+ reloadMe: reload,
2401
+ adminHref: props.portal.adminHref ?? adminHref,
2402
+ profile: { ...props.portal.profile, extras: /* @__PURE__ */ jsxs22(Fragment10, { children: [
2403
+ props.portal.profile?.extras,
2404
+ gifts,
2405
+ me.namedSeatsEnabled ? /* @__PURE__ */ jsx25(NamedSeatPurchase, { api }) : null
2406
+ ] }) }
2407
+ }
2408
+ );
2409
+ }
1464
2410
  const role = me.role || "provisional";
1465
- return /* @__PURE__ */ jsxs16(Fragment5, { children: [
1466
- /* @__PURE__ */ jsxs16("div", { className: "card", children: [
1467
- /* @__PURE__ */ jsxs16("div", { className: "member-row", children: [
1468
- /* @__PURE__ */ jsx17("span", { className: "member-email", children: me.email }),
1469
- /* @__PURE__ */ jsx17("span", { className: "role-badge " + role, children: role })
2411
+ return /* @__PURE__ */ jsxs22(Fragment10, { children: [
2412
+ /* @__PURE__ */ jsxs22("div", { className: "card", children: [
2413
+ /* @__PURE__ */ jsxs22("div", { className: "member-row", children: [
2414
+ /* @__PURE__ */ jsx25("span", { className: "member-email", children: me.email }),
2415
+ /* @__PURE__ */ jsx25("span", { className: "role-badge " + role, children: role })
1470
2416
  ] }),
1471
- /* @__PURE__ */ jsxs16("div", { className: "account-actions", children: [
1472
- /* @__PURE__ */ jsx17("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
1473
- me.authorized || me.superAdmin ? /* @__PURE__ */ jsx17("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
2417
+ /* @__PURE__ */ jsxs22("div", { className: "account-actions", children: [
2418
+ /* @__PURE__ */ jsx25("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
2419
+ me.authorized || me.superAdmin ? /* @__PURE__ */ jsx25("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
1474
2420
  ] })
1475
2421
  ] }),
1476
- me.memberAccess !== true && me.membershipRestart?.eligible && me.application ? /* @__PURE__ */ jsx17(MemberRestart, { api, application: me.application, applicationId: me.membershipRestart.applicationId, onComplete: reload }) : me.memberAccess !== true ? (() => {
1477
- const defaultContent = /* @__PURE__ */ jsx17(
2422
+ me.memberAccess !== true && me.membershipRestart?.eligible && me.application ? /* @__PURE__ */ jsx25(MemberRestart, { api, application: me.application, applicationId: me.membershipRestart.applicationId, onComplete: reload }) : me.memberAccess !== true ? (() => {
2423
+ const defaultContent = /* @__PURE__ */ jsx25(
1478
2424
  ProvisionalCard,
1479
2425
  {
1480
2426
  api,
@@ -1491,27 +2437,34 @@ function MembersArea(props) {
1491
2437
  reload,
1492
2438
  defaultContent
1493
2439
  }) ?? defaultContent;
1494
- })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx17(
2440
+ })() : memberContent ?? (memberPages ? /* @__PURE__ */ jsx25(
1495
2441
  MemberContentArea,
1496
2442
  {
1497
2443
  api,
1498
2444
  ...memberPages === true ? {} : memberPages
1499
2445
  }
1500
- ) : /* @__PURE__ */ jsx17("div", { className: "card", children: /* @__PURE__ */ jsx17("div", { className: "card-label", children: copy.full.welcome }) })),
1501
- /* @__PURE__ */ jsx17(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
1502
- me.namedSeatsEnabled ? /* @__PURE__ */ jsx17(NamedSeatPurchase, { api }) : null
2446
+ ) : /* @__PURE__ */ jsx25("div", { className: "card", children: /* @__PURE__ */ jsx25("div", { className: "card-label", children: copy.full.welcome }) })),
2447
+ /* @__PURE__ */ jsx25(GiftPurchases, { api, copy: copy.gifts, paymentCopy: inheritedCopy.join.payment }),
2448
+ me.namedSeatsEnabled ? /* @__PURE__ */ jsx25(NamedSeatPurchase, { api }) : null
1503
2449
  ] });
1504
2450
  }
1505
2451
 
2452
+ // src/ui/join-referral.ts
2453
+ var CODE = /^[23456789abcdefghjkmnpqrstuvwxyz]{5,8}$/;
2454
+ function referralFromLocation(search = typeof location === "undefined" ? "" : location.search) {
2455
+ const code = new URLSearchParams(search).get("ref")?.trim().toLowerCase() ?? "";
2456
+ return CODE.test(code) ? { referralCode: code } : {};
2457
+ }
2458
+
1506
2459
  // src/ui/join.tsx
1507
- import { useEffect as useEffect11, useRef as useRef8, useState as useState21 } from "preact/hooks";
2460
+ import { useEffect as useEffect16, useRef as useRef8, useState as useState26 } from "preact/hooks";
1508
2461
 
1509
2462
  // src/ui/join-booking.tsx
1510
- import { useEffect as useEffect7, useRef as useRef6, useState as useState14 } from "preact/hooks";
1511
- import { jsx as jsx18, jsxs as jsxs17 } from "preact/jsx-runtime";
2463
+ import { useEffect as useEffect12, useRef as useRef6, useState as useState19 } from "preact/hooks";
2464
+ import { jsx as jsx26, jsxs as jsxs23 } from "preact/jsx-runtime";
1512
2465
  function useJoinSlotsPreload(active) {
1513
2466
  const preload = useRef6(null);
1514
- useEffect7(() => {
2467
+ useEffect12(() => {
1515
2468
  if (!active || preload.current) return;
1516
2469
  preload.current = Promise.resolve().then(() => fetch("/api/schedule/slots"));
1517
2470
  preload.current.catch(() => {
@@ -1522,11 +2475,11 @@ function useJoinSlotsPreload(active) {
1522
2475
  }
1523
2476
  function JoinBooking(props) {
1524
2477
  const { applicationId, copy, onBooked, simulation } = props;
1525
- const [state, setState] = useState14(null);
1526
- const [msg, setMsg] = useState14(null);
1527
- const [busy, setBusy] = useState14(false);
1528
- const [day2, setDay] = useState14(null);
1529
- const [selected, setSelected] = useState14(null);
2478
+ const [state, setState] = useState19(null);
2479
+ const [msg, setMsg] = useState19(null);
2480
+ const [busy, setBusy] = useState19(false);
2481
+ const [day2, setDay] = useState19(null);
2482
+ const [selected, setSelected] = useState19(null);
1530
2483
  const load = async (preloaded) => {
1531
2484
  setMsg(null);
1532
2485
  try {
@@ -1551,7 +2504,7 @@ function JoinBooking(props) {
1551
2504
  setMsg(copy.loadFailed);
1552
2505
  }
1553
2506
  };
1554
- useEffect7(() => {
2507
+ useEffect12(() => {
1555
2508
  void load(props.preload);
1556
2509
  }, [simulation]);
1557
2510
  const book = async () => {
@@ -1583,17 +2536,17 @@ function JoinBooking(props) {
1583
2536
  setBusy(false);
1584
2537
  }
1585
2538
  };
1586
- if (!state) return /* @__PURE__ */ jsx18("p", { className: "slots-status", children: msg ?? copy.loading });
2539
+ if (!state) return /* @__PURE__ */ jsx26("p", { className: "slots-status", children: msg ?? copy.loading });
1587
2540
  const hint = !day2 ? copy.pickDate ?? DEFAULT_JOIN_COPY.booking.pickDate : !selected ? copy.pickTime ?? DEFAULT_JOIN_COPY.booking.pickTime : null;
1588
- return /* @__PURE__ */ jsxs17("div", { className: "join-book", children: [
1589
- /* @__PURE__ */ jsx18("h2", { children: copy.title ?? DEFAULT_JOIN_COPY.booking.title }),
1590
- /* @__PURE__ */ jsx18("p", { children: copy.intro ?? DEFAULT_JOIN_COPY.booking.intro }),
1591
- /* @__PURE__ */ jsxs17("div", { className: "slot-confirm", children: [
1592
- /* @__PURE__ */ jsx18("button", { className: "submit-btn", type: "button", disabled: busy || !selected, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
1593
- hint ? /* @__PURE__ */ jsx18("p", { className: "slot-hint", role: "status", children: hint }) : null,
1594
- msg ? /* @__PURE__ */ jsx18("p", { className: "step2-note", role: "alert", children: msg }) : null
2541
+ return /* @__PURE__ */ jsxs23("div", { className: "join-book", children: [
2542
+ /* @__PURE__ */ jsx26("h2", { children: copy.title ?? DEFAULT_JOIN_COPY.booking.title }),
2543
+ /* @__PURE__ */ jsx26("p", { children: copy.intro ?? DEFAULT_JOIN_COPY.booking.intro }),
2544
+ /* @__PURE__ */ jsxs23("div", { className: "slot-confirm", children: [
2545
+ /* @__PURE__ */ jsx26("button", { className: "submit-btn", type: "button", disabled: busy || !selected, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
2546
+ hint ? /* @__PURE__ */ jsx26("p", { className: "slot-hint", role: "status", children: hint }) : null,
2547
+ msg ? /* @__PURE__ */ jsx26("p", { className: "step2-note", role: "alert", children: msg }) : null
1595
2548
  ] }),
1596
- /* @__PURE__ */ jsx18(
2549
+ /* @__PURE__ */ jsx26(
1597
2550
  SlotPicker,
1598
2551
  {
1599
2552
  slots: state.slots,
@@ -1611,21 +2564,21 @@ function JoinBooking(props) {
1611
2564
  }
1612
2565
 
1613
2566
  // src/ui/join-accepted.tsx
1614
- import { jsx as jsx19, jsxs as jsxs18 } from "preact/jsx-runtime";
2567
+ import { jsx as jsx27, jsxs as jsxs24 } from "preact/jsx-runtime";
1615
2568
  function JoinAccepted(props) {
1616
- return /* @__PURE__ */ jsxs18("div", { className: "join-done card", children: [
1617
- /* @__PURE__ */ jsx19("div", { className: "card-label", children: props.copy.accepted.label }),
1618
- /* @__PURE__ */ jsx19("p", { className: "meeting-note", children: props.copy.accepted.body }),
1619
- /* @__PURE__ */ jsx19("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
2569
+ return /* @__PURE__ */ jsxs24("div", { className: "join-done card", children: [
2570
+ /* @__PURE__ */ jsx27("div", { className: "card-label", children: props.copy.accepted.label }),
2571
+ /* @__PURE__ */ jsx27("p", { className: "meeting-note", children: props.copy.accepted.body }),
2572
+ /* @__PURE__ */ jsx27("a", { className: "apply-link", href: props.membersHref, children: props.copy.done.memberArea })
1620
2573
  ] });
1621
2574
  }
1622
2575
 
1623
2576
  // src/ui/gift-claim-card.tsx
1624
- import { useState as useState15 } from "preact/hooks";
1625
- import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs19 } from "preact/jsx-runtime";
2577
+ import { useState as useState20 } from "preact/hooks";
2578
+ import { Fragment as Fragment11, jsx as jsx28, jsxs as jsxs25 } from "preact/jsx-runtime";
1626
2579
  function GiftClaimCard(props) {
1627
- const [code, setCode] = useState15(props.initialCode ?? "");
1628
- const [error, setError] = useState15(false);
2580
+ const [code, setCode] = useState20(props.initialCode ?? "");
2581
+ const [error, setError] = useState20(false);
1629
2582
  const rotate = async () => {
1630
2583
  if (!props.claimId) return;
1631
2584
  setError(false);
@@ -1638,15 +2591,15 @@ function GiftClaimCard(props) {
1638
2591
  if (response.ok && body.code) setCode(body.code);
1639
2592
  else setError(true);
1640
2593
  };
1641
- return /* @__PURE__ */ jsxs19("div", { className: "join-done card", children: [
1642
- /* @__PURE__ */ jsx20("div", { className: "card-label", children: props.copy.label }),
1643
- /* @__PURE__ */ jsx20("p", { className: "meeting-note", children: props.copy.body }),
1644
- code ? /* @__PURE__ */ jsxs19(Fragment6, { children: [
1645
- /* @__PURE__ */ jsx20("output", { className: "gift-code", children: code }),
1646
- /* @__PURE__ */ jsx20("p", { children: props.copy.save })
2594
+ return /* @__PURE__ */ jsxs25("div", { className: "join-done card", children: [
2595
+ /* @__PURE__ */ jsx28("div", { className: "card-label", children: props.copy.label }),
2596
+ /* @__PURE__ */ jsx28("p", { className: "meeting-note", children: props.copy.body }),
2597
+ code ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
2598
+ /* @__PURE__ */ jsx28("output", { className: "gift-code", children: code }),
2599
+ /* @__PURE__ */ jsx28("p", { children: props.copy.save })
1647
2600
  ] }) : null,
1648
- !code && props.claimId ? /* @__PURE__ */ jsx20("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
1649
- error ? /* @__PURE__ */ jsx20("p", { role: "alert", children: props.copy.rotateFailed }) : null
2601
+ !code && props.claimId ? /* @__PURE__ */ jsx28("button", { type: "button", className: "btn secondary", onClick: () => void rotate(), children: props.copy.rotate }) : null,
2602
+ error ? /* @__PURE__ */ jsx28("p", { role: "alert", children: props.copy.rotateFailed }) : null
1650
2603
  ] });
1651
2604
  }
1652
2605
 
@@ -1654,11 +2607,11 @@ function GiftClaimCard(props) {
1654
2607
  import { useRef as useRef7 } from "preact/hooks";
1655
2608
 
1656
2609
  // src/ui/join-tiers.tsx
1657
- import { useState as useState16 } from "preact/hooks";
1658
- import { Fragment as Fragment7, jsx as jsx21, jsxs as jsxs20 } from "preact/jsx-runtime";
2610
+ import { useState as useState21 } from "preact/hooks";
2611
+ import { Fragment as Fragment12, jsx as jsx29, jsxs as jsxs26 } from "preact/jsx-runtime";
1659
2612
  var joinTierMoney = (cents, currency = "usd") => new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
1660
2613
  function useTierSelection(tiers, initialTierId) {
1661
- const [selectedTierId, setSelectedTierId] = useState16(() => {
2614
+ const [selectedTierId, setSelectedTierId] = useState21(() => {
1662
2615
  const preset = tiers.find((tier) => tier.id === initialTierId);
1663
2616
  if (preset) return preset.id;
1664
2617
  return tiers.length === 1 ? tiers[0].id : null;
@@ -1673,10 +2626,10 @@ function useTierSelection(tiers, initialTierId) {
1673
2626
  };
1674
2627
  }
1675
2628
  function TierPlaceholder(context) {
1676
- return /* @__PURE__ */ jsxs20("fieldset", { "data-chapter-placeholder": "tiers", children: [
1677
- /* @__PURE__ */ jsx21("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
1678
- context.tiers.map((tier) => /* @__PURE__ */ jsxs20("label", { children: [
1679
- /* @__PURE__ */ jsx21(
2629
+ return /* @__PURE__ */ jsxs26("fieldset", { "data-chapter-placeholder": "tiers", children: [
2630
+ /* @__PURE__ */ jsx29("legend", { children: "Membership (placeholder \u2014 pass renderTiers to style this)" }),
2631
+ context.tiers.map((tier) => /* @__PURE__ */ jsxs26("label", { children: [
2632
+ /* @__PURE__ */ jsx29(
1680
2633
  "input",
1681
2634
  {
1682
2635
  type: "radio",
@@ -1687,13 +2640,13 @@ function TierPlaceholder(context) {
1687
2640
  }
1688
2641
  ),
1689
2642
  tier.name,
1690
- tier.founding ? /* @__PURE__ */ jsxs20(Fragment7, { children: [
2643
+ tier.founding ? /* @__PURE__ */ jsxs26(Fragment12, { children: [
1691
2644
  " \u2014 ",
1692
2645
  joinTierMoney(tier.founding.dueTodayCents),
1693
2646
  " founding member rate (list ",
1694
2647
  joinTierMoney(tier.priceCents),
1695
2648
  ")"
1696
- ] }) : tier.free ? " \u2014 free" : /* @__PURE__ */ jsxs20(Fragment7, { children: [
2649
+ ] }) : tier.free ? " \u2014 free" : /* @__PURE__ */ jsxs26(Fragment12, { children: [
1697
2650
  " \u2014 ",
1698
2651
  joinTierMoney(tier.priceCents)
1699
2652
  ] })
@@ -1705,12 +2658,12 @@ function tierNeedsPayment(selected, paymentsReady) {
1705
2658
  }
1706
2659
 
1707
2660
  // src/ui/join-application-form.tsx
1708
- import { jsx as jsx22, jsxs as jsxs21 } from "preact/jsx-runtime";
2661
+ import { jsx as jsx30, jsxs as jsxs27 } from "preact/jsx-runtime";
1709
2662
  function JoinApplicationForm({ props, copy, invitation, requestedSeat, tierSelection, giftOfferId, selectGift, submitting, error, submit }) {
1710
2663
  const form = useRef7(null);
1711
2664
  const ready = useFormReadiness(form);
1712
2665
  const disabled = submitting || props.submitDisabled === true || !ready || !validJoinRecipient(props.payment?.initialNamedSeat);
1713
- return /* @__PURE__ */ jsxs21("form", { ref: form, className: "join-form", onSubmit: (event) => {
2666
+ return /* @__PURE__ */ jsxs27("form", { ref: form, className: "join-form", onSubmit: (event) => {
1714
2667
  if (!isFormReady(event.currentTarget)) {
1715
2668
  event.preventDefault();
1716
2669
  event.currentTarget.reportValidity();
@@ -1718,15 +2671,15 @@ function JoinApplicationForm({ props, copy, invitation, requestedSeat, tierSelec
1718
2671
  }
1719
2672
  void submit(event);
1720
2673
  }, children: [
1721
- !requestedSeat && (props.config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs21("fieldset", { children: [
1722
- /* @__PURE__ */ jsx22("legend", { children: copy.gift.optionsLabel }),
1723
- /* @__PURE__ */ jsx22("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => selectGift(null), children: copy.gift.chooseSelf }),
1724
- /* @__PURE__ */ jsx22(GiftOfferCards, { offers: props.config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: selectGift })
2674
+ !requestedSeat && (props.config.giftOffers?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs27("fieldset", { children: [
2675
+ /* @__PURE__ */ jsx30("legend", { children: copy.gift.optionsLabel }),
2676
+ /* @__PURE__ */ jsx30("button", { type: "button", className: "btn secondary", "aria-pressed": !giftOfferId, onClick: () => selectGift(null), children: copy.gift.chooseSelf }),
2677
+ /* @__PURE__ */ jsx30(GiftOfferCards, { offers: props.config.giftOffers ?? [], mode: "select", selectedOfferId: giftOfferId, onSelect: selectGift })
1725
2678
  ] }) : null,
1726
2679
  !requestedSeat && (props.config.tiers?.length ?? 0) > 1 ? props.renderTiers?.(tierSelection) ?? TierPlaceholder(tierSelection) : null,
1727
2680
  typeof props.children === "function" ? props.children({ invitation }) : props.children,
1728
- error ? /* @__PURE__ */ jsx22("p", { className: "join-error", role: "alert", children: error }) : null,
1729
- props.renderSubmit?.({ submitting, disabled }) ?? /* @__PURE__ */ jsx22(JoinSubmitButton, { disabled, children: submitting ? copy.form.submitting : copy.form.submit })
2681
+ error ? /* @__PURE__ */ jsx30("p", { className: "join-error", role: "alert", children: error }) : null,
2682
+ props.renderSubmit?.({ submitting, disabled }) ?? /* @__PURE__ */ jsx30(JoinSubmitButton, { disabled, children: submitting ? copy.form.submitting : copy.form.submit })
1730
2683
  ] });
1731
2684
  }
1732
2685
 
@@ -1751,8 +2704,8 @@ function joinStateFromApplicationResponse(data, fallbackNeedsPayment, submittedG
1751
2704
 
1752
2705
  // src/ui/gift-code-redeemer.tsx
1753
2706
  import { InlineActionField } from "@odla-ai/ui/components";
1754
- import { useState as useState17 } from "preact/hooks";
1755
- import { Fragment as Fragment8, jsx as jsx23, jsxs as jsxs22 } from "preact/jsx-runtime";
2707
+ import { useState as useState22 } from "preact/hooks";
2708
+ import { Fragment as Fragment13, jsx as jsx31, jsxs as jsxs28 } from "preact/jsx-runtime";
1756
2709
  var GIFT_CODE_REDEEMER_CSS = `
1757
2710
  .gift-code-redeemer{container-type:inline-size;display:grid;gap:var(--ui-space-1,.35rem);margin:0}
1758
2711
  .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}
@@ -1767,10 +2720,10 @@ var GIFT_CODE_REDEEMER_CSS = `
1767
2720
  @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%}}
1768
2721
  `;
1769
2722
  function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1770
- const [code, setCode] = useState17("");
1771
- const [loading, setLoading] = useState17(false);
1772
- const [message, setMessage] = useState17("");
1773
- const [messageTone, setMessageTone] = useState17("neutral");
2723
+ const [code, setCode] = useState22("");
2724
+ const [loading, setLoading] = useState22(false);
2725
+ const [message, setMessage] = useState22("");
2726
+ const [messageTone, setMessageTone] = useState22("neutral");
1774
2727
  const normalizedCode = code.trim();
1775
2728
  const redeem = async () => {
1776
2729
  if (!normalizedCode || loading) return;
@@ -1797,9 +2750,9 @@ function GiftCodeRedeemer({ api, applicationId, copy, id, onRedeemed }) {
1797
2750
  setLoading(false);
1798
2751
  }
1799
2752
  };
1800
- return /* @__PURE__ */ jsxs22(Fragment8, { children: [
1801
- /* @__PURE__ */ jsx23("style", { children: GIFT_CODE_REDEEMER_CSS }),
1802
- /* @__PURE__ */ jsx23(
2753
+ return /* @__PURE__ */ jsxs28(Fragment13, { children: [
2754
+ /* @__PURE__ */ jsx31("style", { children: GIFT_CODE_REDEEMER_CSS }),
2755
+ /* @__PURE__ */ jsx31(
1803
2756
  InlineActionField,
1804
2757
  {
1805
2758
  id,
@@ -1855,12 +2808,12 @@ async function loadJoinResume(applicationId, fetcher = fetch) {
1855
2808
  }
1856
2809
 
1857
2810
  // src/ui/join-gift-claim.tsx
1858
- import { jsx as jsx24, jsxs as jsxs23 } from "preact/jsx-runtime";
2811
+ import { jsx as jsx32, jsxs as jsxs29 } from "preact/jsx-runtime";
1859
2812
  function JoinGiftClaim(props) {
1860
- return /* @__PURE__ */ jsxs23("section", { className: "join-gift-claim card", children: [
1861
- /* @__PURE__ */ jsx24("div", { className: "card-label", children: props.copy.claimTitle }),
1862
- /* @__PURE__ */ jsx24("p", { children: props.copy.claimBody }),
1863
- /* @__PURE__ */ jsx24(
2813
+ return /* @__PURE__ */ jsxs29("section", { className: "join-gift-claim card", children: [
2814
+ /* @__PURE__ */ jsx32("div", { className: "card-label", children: props.copy.claimTitle }),
2815
+ /* @__PURE__ */ jsx32("p", { children: props.copy.claimBody }),
2816
+ /* @__PURE__ */ jsx32(
1864
2817
  GiftCodeRedeemer,
1865
2818
  {
1866
2819
  api: props.api,
@@ -1884,21 +2837,21 @@ function JoinGiftClaim(props) {
1884
2837
  }
1885
2838
 
1886
2839
  // src/ui/join-named-seat.tsx
1887
- import { useState as useState18 } from "preact/hooks";
1888
- import { jsx as jsx25, jsxs as jsxs24 } from "preact/jsx-runtime";
2840
+ import { useState as useState23 } from "preact/hooks";
2841
+ import { jsx as jsx33, jsxs as jsxs30 } from "preact/jsx-runtime";
1889
2842
  function JoinNamedSeat(props) {
1890
2843
  const seatId = props.seatId ?? "";
1891
2844
  const inherited = useChapterCopy(), copy = props.copy ?? inherited.join.namedSeat;
1892
- const [accepted, setAccepted] = useState18(false), [busy, setBusy] = useState18(false), [error, setError] = useState18("");
1893
- return /* @__PURE__ */ jsxs24("section", { className: "join-named-seat card", children: [
1894
- /* @__PURE__ */ jsx25("h2", { children: copy.title }),
1895
- /* @__PURE__ */ jsx25("p", { children: copy.body }),
1896
- /* @__PURE__ */ jsxs24("label", { className: "journey-consent", children: [
1897
- /* @__PURE__ */ jsx25("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
1898
- /* @__PURE__ */ jsx25("span", { children: copy.consent })
2845
+ const [accepted, setAccepted] = useState23(false), [busy, setBusy] = useState23(false), [error, setError] = useState23("");
2846
+ return /* @__PURE__ */ jsxs30("section", { className: "join-named-seat card", children: [
2847
+ /* @__PURE__ */ jsx33("h2", { children: copy.title }),
2848
+ /* @__PURE__ */ jsx33("p", { children: copy.body }),
2849
+ /* @__PURE__ */ jsxs30("label", { className: "journey-consent", children: [
2850
+ /* @__PURE__ */ jsx33("input", { type: "checkbox", checked: accepted, disabled: busy, onChange: (event) => setAccepted(event.currentTarget.checked) }),
2851
+ /* @__PURE__ */ jsx33("span", { children: copy.consent })
1899
2852
  ] }),
1900
- error ? /* @__PURE__ */ jsx25("p", { role: "alert", children: error }) : null,
1901
- /* @__PURE__ */ jsx25("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
2853
+ error ? /* @__PURE__ */ jsx33("p", { role: "alert", children: error }) : null,
2854
+ /* @__PURE__ */ jsx33("button", { type: "button", className: "btn btn-primary", disabled: busy || !accepted || !seatId.trim(), onClick: async () => {
1902
2855
  setBusy(true);
1903
2856
  setError("");
1904
2857
  try {
@@ -1918,13 +2871,13 @@ function JoinNamedSeat(props) {
1918
2871
  }
1919
2872
 
1920
2873
  // src/ui/join-invitation.tsx
1921
- import { useEffect as useEffect8, useState as useState19 } from "preact/hooks";
1922
- import { jsx as jsx26, jsxs as jsxs25 } from "preact/jsx-runtime";
2874
+ import { useEffect as useEffect13, useState as useState24 } from "preact/hooks";
2875
+ import { jsx as jsx34, jsxs as jsxs31 } from "preact/jsx-runtime";
1923
2876
  function useJoinInvitation(seatId, api, fallback) {
1924
- const [invitation, setInvitation] = useState19(null);
1925
- const [error, setError] = useState19("");
1926
- const [revision, setRevision] = useState19(0);
1927
- useEffect8(() => {
2877
+ const [invitation, setInvitation] = useState24(null);
2878
+ const [error, setError] = useState24("");
2879
+ const [revision, setRevision] = useState24(0);
2880
+ useEffect13(() => {
1928
2881
  let live = true;
1929
2882
  setInvitation(null);
1930
2883
  setError("");
@@ -1946,9 +2899,9 @@ function useJoinInvitation(seatId, api, fallback) {
1946
2899
  return { invitation: invitation?.seatId === seatId ? invitation : null, error, retry: () => setRevision((value) => value + 1) };
1947
2900
  }
1948
2901
  function JoinInvitationNotice(props) {
1949
- return /* @__PURE__ */ jsxs25("aside", { className: "journey-invitation", "aria-label": props.copy.title, children: [
1950
- /* @__PURE__ */ jsx26("p", { children: /* @__PURE__ */ jsx26("strong", { children: formatChapterCopy(props.copy.from, { purchaserName: props.invitation.purchaserName }) }) }),
1951
- /* @__PURE__ */ jsx26("p", { children: formatChapterCopy(props.copy.prepaid, { tierName: props.tierName }) })
2902
+ return /* @__PURE__ */ jsxs31("aside", { className: "journey-invitation", "aria-label": props.copy.title, children: [
2903
+ /* @__PURE__ */ jsx34("p", { children: /* @__PURE__ */ jsx34("strong", { children: formatChapterCopy(props.copy.from, { purchaserName: props.invitation.purchaserName }) }) }),
2904
+ /* @__PURE__ */ jsx34("p", { children: formatChapterCopy(props.copy.prepaid, { tierName: props.tierName }) })
1952
2905
  ] });
1953
2906
  }
1954
2907
 
@@ -1963,9 +2916,9 @@ function joinSimulationPayment(simulation, priceCents) {
1963
2916
  }
1964
2917
 
1965
2918
  // src/ui/join-pending.ts
1966
- import { useEffect as useEffect9 } from "preact/hooks";
2919
+ import { useEffect as useEffect14 } from "preact/hooks";
1967
2920
  function useJoinPending(state, loadResume, setState) {
1968
- useEffect9(() => {
2921
+ useEffect14(() => {
1969
2922
  if (state.step !== "paymentPending") return;
1970
2923
  let live = true;
1971
2924
  let timer;
@@ -1988,16 +2941,16 @@ function useJoinPending(state, loadResume, setState) {
1988
2941
  }
1989
2942
 
1990
2943
  // src/ui/payment-receipt.tsx
1991
- import { useEffect as useEffect10, useState as useState20 } from "preact/hooks";
1992
- import { Fragment as Fragment9, jsx as jsx27, jsxs as jsxs26 } from "preact/jsx-runtime";
2944
+ import { useEffect as useEffect15, useState as useState25 } from "preact/hooks";
2945
+ import { Fragment as Fragment14, jsx as jsx35, jsxs as jsxs32 } from "preact/jsx-runtime";
1993
2946
  function PaymentReceiptCard({ applicationId }) {
1994
- const [receipt, setReceipt] = useState20(null);
1995
- const [error, setError] = useState20("");
1996
- const [revision, setRevision] = useState20(0);
1997
- const [emailState, setEmailState] = useState20("idle");
1998
- const [previewEmail, setPreviewEmail] = useState20(false);
2947
+ const [receipt, setReceipt] = useState25(null);
2948
+ const [error, setError] = useState25("");
2949
+ const [revision, setRevision] = useState25(0);
2950
+ const [emailState, setEmailState] = useState25("idle");
2951
+ const [previewEmail, setPreviewEmail] = useState25(false);
1999
2952
  const endpoint = `/api/payments/receipt?application=${encodeURIComponent(applicationId)}`;
2000
- useEffect10(() => {
2953
+ useEffect15(() => {
2001
2954
  let live = true, timer;
2002
2955
  setReceipt(null);
2003
2956
  setError("");
@@ -2023,9 +2976,9 @@ function PaymentReceiptCard({ applicationId }) {
2023
2976
  if (timer) clearTimeout(timer);
2024
2977
  };
2025
2978
  }, [endpoint, revision]);
2026
- if (!receipt) return error ? /* @__PURE__ */ jsxs26("section", { "aria-label": "Payment receipt", children: [
2027
- /* @__PURE__ */ jsx27("p", { role: "status", children: error }),
2028
- /* @__PURE__ */ jsx27("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Reload receipt" })
2979
+ if (!receipt) return error ? /* @__PURE__ */ jsxs32("section", { "aria-label": "Payment receipt", children: [
2980
+ /* @__PURE__ */ jsx35("p", { role: "status", children: error }),
2981
+ /* @__PURE__ */ jsx35("button", { type: "button", onClick: () => setRevision((value) => value + 1), children: "Reload receipt" })
2029
2982
  ] }) : null;
2030
2983
  const money = (cents) => new Intl.NumberFormat("en-US", { style: "currency", currency: receipt.currency }).format(cents / 100);
2031
2984
  const email = async () => {
@@ -2042,38 +2995,38 @@ function PaymentReceiptCard({ applicationId }) {
2042
2995
  setError("We could not confirm email delivery. You can print or save your receipt.");
2043
2996
  }
2044
2997
  };
2045
- return /* @__PURE__ */ jsxs26("section", { className: "payment-receipt card", "aria-label": "Payment receipt", children: [
2046
- /* @__PURE__ */ jsx27("h2", { children: "Payment received" }),
2047
- /* @__PURE__ */ jsx27("p", { children: receipt.tierName }),
2048
- /* @__PURE__ */ jsxs26("dl", { className: "journey-summary", children: [
2049
- /* @__PURE__ */ jsx27("dt", { children: "Membership" }),
2050
- /* @__PURE__ */ jsx27("dd", { children: money(receipt.membershipCents) }),
2051
- receipt.namedSeat ? /* @__PURE__ */ jsxs26(Fragment9, { children: [
2052
- /* @__PURE__ */ jsxs26("dt", { children: [
2998
+ return /* @__PURE__ */ jsxs32("section", { className: "payment-receipt card", "aria-label": "Payment receipt", children: [
2999
+ /* @__PURE__ */ jsx35("h2", { children: "Payment received" }),
3000
+ /* @__PURE__ */ jsx35("p", { children: receipt.tierName }),
3001
+ /* @__PURE__ */ jsxs32("dl", { className: "journey-summary", children: [
3002
+ /* @__PURE__ */ jsx35("dt", { children: "Membership" }),
3003
+ /* @__PURE__ */ jsx35("dd", { children: money(receipt.membershipCents) }),
3004
+ receipt.namedSeat ? /* @__PURE__ */ jsxs32(Fragment14, { children: [
3005
+ /* @__PURE__ */ jsxs32("dt", { children: [
2053
3006
  "Additional member \u2014 ",
2054
3007
  receipt.namedSeat.recipientName
2055
3008
  ] }),
2056
- /* @__PURE__ */ jsxs26("dd", { children: [
3009
+ /* @__PURE__ */ jsxs32("dd", { children: [
2057
3010
  receipt.namedSeat.amountCents === 0 ? "Included \u2014 " : "",
2058
3011
  money(receipt.namedSeat.amountCents)
2059
3012
  ] })
2060
3013
  ] }) : null,
2061
- /* @__PURE__ */ jsx27("dt", { children: "Total paid" }),
2062
- /* @__PURE__ */ jsx27("dd", { children: money(receipt.totalCents) })
3014
+ /* @__PURE__ */ jsx35("dt", { children: "Total paid" }),
3015
+ /* @__PURE__ */ jsx35("dd", { children: money(receipt.totalCents) })
2063
3016
  ] }),
2064
- /* @__PURE__ */ jsxs26("div", { className: "journey-actions", children: [
2065
- /* @__PURE__ */ jsx27("a", { className: "apply-link journey-secondary", href: `${endpoint}&format=html`, target: "_blank", rel: "noopener noreferrer", children: "Print / save receipt" }),
2066
- /* @__PURE__ */ jsx27("button", { type: "button", disabled: emailState !== "idle", onClick: () => void email(), children: emailState === "sending" ? "Sending\u2026" : emailState === "sent" ? "Receipt emailed" : "Email me my receipt" })
3017
+ /* @__PURE__ */ jsxs32("div", { className: "journey-actions", children: [
3018
+ /* @__PURE__ */ jsx35("a", { className: "apply-link journey-secondary", href: `${endpoint}&format=html`, target: "_blank", rel: "noopener noreferrer", children: "Print / save receipt" }),
3019
+ /* @__PURE__ */ jsx35("button", { type: "button", disabled: emailState !== "idle", onClick: () => void email(), children: emailState === "sending" ? "Sending\u2026" : emailState === "sent" ? "Receipt emailed" : "Email me my receipt" })
2067
3020
  ] }),
2068
- emailState === "sent" ? /* @__PURE__ */ jsx27("p", { role: "status", children: previewEmail ? "Test receipt sent to the configured test inbox." : "Sent to the email used for your payment." }) : null,
2069
- error ? /* @__PURE__ */ jsx27("p", { role: "alert", children: error }) : null
3021
+ emailState === "sent" ? /* @__PURE__ */ jsx35("p", { role: "status", children: previewEmail ? "Test receipt sent to the configured test inbox." : "Sent to the email used for your payment." }) : null,
3022
+ error ? /* @__PURE__ */ jsx35("p", { role: "alert", children: error }) : null
2070
3023
  ] });
2071
3024
  }
2072
3025
 
2073
3026
  // src/ui/join.tsx
2074
- import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs27 } from "preact/jsx-runtime";
3027
+ import { Fragment as Fragment15, jsx as jsx36, jsxs as jsxs33 } from "preact/jsx-runtime";
2075
3028
  function JoinIsland(props) {
2076
- return /* @__PURE__ */ jsx28(JourneyFrame, { children: /* @__PURE__ */ jsx28(JoinFlow, { ...props }) });
3029
+ return /* @__PURE__ */ jsx36(JourneyFrame, { children: /* @__PURE__ */ jsx36(JoinFlow, { ...props }) });
2077
3030
  }
2078
3031
  function JoinFlow(props) {
2079
3032
  const inheritedCopy = useChapterCopy();
@@ -2094,16 +3047,16 @@ function JoinFlow(props) {
2094
3047
  const invitation = invitationState.invitation;
2095
3048
  const selectedTierId = invitation?.skuId ?? tierSelection.selectedTierId;
2096
3049
  const selectedTier = tiers.find((tier) => tier.id === selectedTierId) ?? tierSelection.selected;
2097
- const [state, setState] = useState21(requestedSeat ? { step: "form" } : props.initialState ?? { step: "form" });
2098
- const [error, setError] = useState21(null);
2099
- const [submitting, setSubmitting] = useState21(false);
2100
- const [editingNamedSeat, setEditingNamedSeat] = useState21(false);
2101
- const [giftOfferId, setGiftOfferId] = useState21(props.initialGiftOfferId ?? null);
3050
+ const [state, setState] = useState26(requestedSeat ? { step: "form" } : props.initialState ?? { step: "form" });
3051
+ const [error, setError] = useState26(null);
3052
+ const [submitting, setSubmitting] = useState26(false);
3053
+ const [editingNamedSeat, setEditingNamedSeat] = useState26(false);
3054
+ const [giftOfferId, setGiftOfferId] = useState26(props.initialGiftOfferId ?? null);
2102
3055
  const simulatedPayment = joinSimulationPayment(props.simulation, selectedTier?.priceCents ?? 0);
2103
3056
  const simulatedBooking = props.simulation ? props.simulation.booking ?? { slots: [], timezone: "UTC" } : void 0;
2104
3057
  const submissionId = useRef8(null);
2105
3058
  const slotsPreload = useJoinSlotsPreload(!props.simulation && ["payment", "paymentPending"].includes(state.step));
2106
- useEffect11(() => {
3059
+ useEffect16(() => {
2107
3060
  if (props.simulation) return;
2108
3061
  const resolver = requestedSeat ? () => invitation?.applicationId ? loadJoinResume(invitation.applicationId) : Promise.resolve(null) : props.loadResume ?? (() => {
2109
3062
  if (typeof window === "undefined") return Promise.resolve(null);
@@ -2132,7 +3085,7 @@ function JoinFlow(props) {
2132
3085
  setError(null);
2133
3086
  try {
2134
3087
  const fields = collectFormFields(new FormData(e.currentTarget));
2135
- fields.submissionId = submissionId.current ??= crypto.randomUUID();
3088
+ Object.assign(fields, { submissionId: submissionId.current ??= crypto.randomUUID() }, invitation ? {} : referralFromLocation());
2136
3089
  if (invitation) {
2137
3090
  fields.namedSeatId = invitation.seatId;
2138
3091
  fields.tierId = invitation.skuId;
@@ -2176,13 +3129,13 @@ function JoinFlow(props) {
2176
3129
  setSubmitting(false);
2177
3130
  }
2178
3131
  };
2179
- if (requestedSeat && !invitation) return /* @__PURE__ */ jsxs27("section", { className: "card", "aria-live": "polite", children: [
2180
- /* @__PURE__ */ jsx28("h2", { children: seatCopy.title }),
2181
- /* @__PURE__ */ jsx28("p", { role: invitationState.error ? "alert" : "status", children: invitationState.error || seatCopy.loading }),
2182
- invitationState.error ? /* @__PURE__ */ jsx28("button", { type: "button", onClick: invitationState.retry, children: seatCopy.retry }) : null
3132
+ if (requestedSeat && !invitation) return /* @__PURE__ */ jsxs33("section", { className: "card", "aria-live": "polite", children: [
3133
+ /* @__PURE__ */ jsx36("h2", { children: seatCopy.title }),
3134
+ /* @__PURE__ */ jsx36("p", { role: invitationState.error ? "alert" : "status", children: invitationState.error || seatCopy.loading }),
3135
+ invitationState.error ? /* @__PURE__ */ jsx36("button", { type: "button", onClick: invitationState.retry, children: seatCopy.retry }) : null
2183
3136
  ] });
2184
- const header = /* @__PURE__ */ jsxs27(Fragment10, { children: [
2185
- invitation ? /* @__PURE__ */ jsx28(
3137
+ const header = /* @__PURE__ */ jsxs33(Fragment15, { children: [
3138
+ invitation ? /* @__PURE__ */ jsx36(
2186
3139
  JoinInvitationNotice,
2187
3140
  {
2188
3141
  invitation,
@@ -2197,49 +3150,49 @@ function JoinFlow(props) {
2197
3150
  ..."applicationId" in state ? { applicationId: state.applicationId } : {}
2198
3151
  })
2199
3152
  ] });
2200
- const receipt = !props.simulation && !requestedSeat && ["paymentPending", "booking", "accepted", "done"].includes(state.step) && "applicationId" in state ? /* @__PURE__ */ jsx28(PaymentReceiptCard, { applicationId: state.applicationId }) : null;
3153
+ const receipt = !props.simulation && !requestedSeat && ["paymentPending", "booking", "accepted", "done"].includes(state.step) && "applicationId" in state ? /* @__PURE__ */ jsx36(PaymentReceiptCard, { applicationId: state.applicationId }) : null;
2201
3154
  if (state.step === "closed") {
2202
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3155
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2203
3156
  header,
2204
- /* @__PURE__ */ jsxs27("section", { className: "card", "aria-label": "Application closed", children: [
2205
- /* @__PURE__ */ jsx28("h2", { children: "This application is closed" }),
2206
- /* @__PURE__ */ jsx28("p", { children: "This link cannot resume payment or booking. Sign in to view your application status, or contact the chapter if you need help." }),
2207
- /* @__PURE__ */ jsx28("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
3157
+ /* @__PURE__ */ jsxs33("section", { className: "card", "aria-label": "Application closed", children: [
3158
+ /* @__PURE__ */ jsx36("h2", { children: "This application is closed" }),
3159
+ /* @__PURE__ */ jsx36("p", { children: "This link cannot resume payment or booking. Sign in to view your application status, or contact the chapter if you need help." }),
3160
+ /* @__PURE__ */ jsx36("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
2208
3161
  ] })
2209
3162
  ] });
2210
3163
  }
2211
3164
  if (state.step === "done") {
2212
- const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs27("div", { className: "join-done card", children: [
2213
- /* @__PURE__ */ jsx28("div", { className: "card-label", children: copy.done.label }),
2214
- /* @__PURE__ */ jsx28("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
2215
- /* @__PURE__ */ jsx28("p", { className: "meeting-note", children: copy.done.calendarInvite }),
2216
- /* @__PURE__ */ jsx28("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
3165
+ const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs33("div", { className: "join-done card", children: [
3166
+ /* @__PURE__ */ jsx36("div", { className: "card-label", children: copy.done.label }),
3167
+ /* @__PURE__ */ jsx36("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
3168
+ /* @__PURE__ */ jsx36("p", { className: "meeting-note", children: copy.done.calendarInvite }),
3169
+ /* @__PURE__ */ jsx36("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
2217
3170
  ] });
2218
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3171
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2219
3172
  header,
2220
3173
  body,
2221
3174
  receipt
2222
3175
  ] });
2223
3176
  }
2224
3177
  if (state.step === "accepted") {
2225
- const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx28(JoinAccepted, { copy, membersHref });
2226
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3178
+ const body = renderAccepted?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsx36(JoinAccepted, { copy, membersHref });
3179
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2227
3180
  header,
2228
3181
  body,
2229
3182
  receipt
2230
3183
  ] });
2231
3184
  }
2232
3185
  if (state.step === "gift") {
2233
- const body = renderGift?.(state) ?? /* @__PURE__ */ jsx28(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
2234
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3186
+ const body = renderGift?.(state) ?? /* @__PURE__ */ jsx36(GiftClaimCard, { applicationId: state.applicationId, claimId: state.claimId, initialCode: state.code, copy: copy.gift });
3187
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2235
3188
  header,
2236
3189
  body
2237
3190
  ] });
2238
3191
  }
2239
3192
  if (state.step === "booking") {
2240
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3193
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2241
3194
  header,
2242
- /* @__PURE__ */ jsx28(
3195
+ /* @__PURE__ */ jsx36(
2243
3196
  JoinBooking,
2244
3197
  {
2245
3198
  applicationId: state.applicationId,
@@ -2256,9 +3209,9 @@ function JoinFlow(props) {
2256
3209
  }
2257
3210
  if (state.step === "payment") {
2258
3211
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation && !state.giftPurchase) {
2259
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3212
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2260
3213
  header,
2261
- /* @__PURE__ */ jsx28(
3214
+ /* @__PURE__ */ jsx36(
2262
3215
  JoinNamedSeat,
2263
3216
  {
2264
3217
  api: props.namedSeatClaimApi,
@@ -2271,9 +3224,9 @@ function JoinFlow(props) {
2271
3224
  ] });
2272
3225
  }
2273
3226
  const Payment = state.giftPurchase || props.simulation ? PaymentStep : MembershipPaymentStep;
2274
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3227
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2275
3228
  header,
2276
- props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx28(
3229
+ props.giftClaimApi && !state.giftPurchase ? /* @__PURE__ */ jsx36(
2277
3230
  JoinGiftClaim,
2278
3231
  {
2279
3232
  api: props.giftClaimApi,
@@ -2284,7 +3237,7 @@ function JoinFlow(props) {
2284
3237
  onError: () => setError(copy.form.unexpectedFailure)
2285
3238
  }
2286
3239
  ) : null,
2287
- /* @__PURE__ */ jsx28(
3240
+ /* @__PURE__ */ jsx36(
2288
3241
  Payment,
2289
3242
  {
2290
3243
  applicationId: state.applicationId,
@@ -2312,9 +3265,9 @@ function JoinFlow(props) {
2312
3265
  }
2313
3266
  if (state.step === "paymentPending") {
2314
3267
  if (props.namedSeatClaimApi && props.initialNamedSeatId && !props.simulation) {
2315
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3268
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2316
3269
  header,
2317
- /* @__PURE__ */ jsx28(
3270
+ /* @__PURE__ */ jsx36(
2318
3271
  JoinNamedSeat,
2319
3272
  {
2320
3273
  api: props.namedSeatClaimApi,
@@ -2326,15 +3279,15 @@ function JoinFlow(props) {
2326
3279
  )
2327
3280
  ] });
2328
3281
  }
2329
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3282
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2330
3283
  header,
2331
- /* @__PURE__ */ jsx28("p", { className: "pay-status", role: "status", children: copy.payment.pending }),
3284
+ /* @__PURE__ */ jsx36("p", { className: "pay-status", role: "status", children: copy.payment.pending }),
2332
3285
  receipt
2333
3286
  ] });
2334
3287
  }
2335
- return /* @__PURE__ */ jsxs27(Fragment10, { children: [
3288
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
2336
3289
  header,
2337
- /* @__PURE__ */ jsx28(
3290
+ /* @__PURE__ */ jsx36(
2338
3291
  JoinApplicationForm,
2339
3292
  {
2340
3293
  props,
@@ -2353,7 +3306,7 @@ function JoinFlow(props) {
2353
3306
  }
2354
3307
 
2355
3308
  // src/ui/membership-cards.tsx
2356
- import { Markdown as Markdown2 } from "@odla-ai/ui/components";
3309
+ import { Markdown as Markdown4 } from "@odla-ai/ui/components";
2357
3310
 
2358
3311
  // src/membership-page.ts
2359
3312
  var MEMBERSHIP_PAGE_RENDERER = "membership-cards-v1";
@@ -2524,7 +3477,7 @@ function membershipPageDocument(input, options = {}) {
2524
3477
  }
2525
3478
 
2526
3479
  // src/ui/membership-cards.tsx
2527
- import { jsx as jsx29, jsxs as jsxs28 } from "preact/jsx-runtime";
3480
+ import { jsx as jsx37, jsxs as jsxs34 } from "preact/jsx-runtime";
2528
3481
  var MEMBERSHIP_CARD_TOKEN_DEFAULTS = Object.freeze({
2529
3482
  "--ui-membership-grid-gap": "1.4rem",
2530
3483
  "--ui-membership-card-padding": "2rem 1.7rem 1.8rem",
@@ -2593,79 +3546,71 @@ function membershipApplicationHref(applicationPath, tierId) {
2593
3546
  }
2594
3547
  function TierAction(props) {
2595
3548
  if (props.mode === "select") {
2596
- return /* @__PURE__ */ jsxs28("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
3549
+ return /* @__PURE__ */ jsxs34("button", { type: "button", className: "chapter-membership-action", "aria-current": props.selected ? "true" : void 0, onClick: () => props.onSelectTier?.(props.tierId), children: [
2597
3550
  props.label,
2598
- /* @__PURE__ */ jsx29("span", { "aria-hidden": "true", children: "\u2192" })
3551
+ /* @__PURE__ */ jsx37("span", { "aria-hidden": "true", children: "\u2192" })
2599
3552
  ] });
2600
3553
  }
2601
3554
  if (props.mode === "preview") {
2602
- return /* @__PURE__ */ jsxs28("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
3555
+ return /* @__PURE__ */ jsxs34("span", { className: "chapter-membership-action", "aria-disabled": "true", children: [
2603
3556
  props.label,
2604
- /* @__PURE__ */ jsx29("span", { "aria-hidden": "true", children: "\u2192" })
3557
+ /* @__PURE__ */ jsx37("span", { "aria-hidden": "true", children: "\u2192" })
2605
3558
  ] });
2606
3559
  }
2607
- return /* @__PURE__ */ jsxs28("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
3560
+ return /* @__PURE__ */ jsxs34("a", { className: "chapter-membership-action", href: membershipApplicationHref(props.applicationPath, props.tierId), children: [
2608
3561
  props.label,
2609
- /* @__PURE__ */ jsx29("span", { "aria-hidden": "true", children: "\u2192" })
3562
+ /* @__PURE__ */ jsx37("span", { "aria-hidden": "true", children: "\u2192" })
2610
3563
  ] });
2611
3564
  }
2612
3565
  function TierCard(props) {
2613
3566
  const { tier, presentation } = props;
2614
3567
  const summary = presentation.summaryMarkdown ?? tier.blurb;
2615
3568
  const action = presentation.ctaLabel ?? (tier.free ? "Join for free" : `Choose ${tier.name}`);
2616
- return /* @__PURE__ */ jsxs28("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
2617
- presentation.badge ? /* @__PURE__ */ jsx29("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
2618
- /* @__PURE__ */ jsx29("h3", { className: "chapter-membership-name", children: tier.name }),
2619
- /* @__PURE__ */ jsxs28("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
2620
- presentation.capacity ? /* @__PURE__ */ jsxs28("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
2621
- /* @__PURE__ */ jsx29("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
2622
- /* @__PURE__ */ jsx29("small", { children: presentation.capacity.label ?? "available" })
3569
+ return /* @__PURE__ */ jsxs34("article", { className: "chapter-membership-card", "data-tier-id": tier.id, "data-variant": presentation.variant ?? "standard", children: [
3570
+ presentation.badge ? /* @__PURE__ */ jsx37("div", { className: "chapter-membership-badge", children: presentation.badge }) : null,
3571
+ /* @__PURE__ */ jsx37("h3", { className: "chapter-membership-name", children: tier.name }),
3572
+ /* @__PURE__ */ jsxs34("div", { className: "chapter-membership-offer", "data-with-capacity": presentation.capacity ? "true" : void 0, children: [
3573
+ presentation.capacity ? /* @__PURE__ */ jsxs34("div", { className: "chapter-membership-capacity", "aria-label": `${presentation.capacity.total} ${presentation.capacity.label ?? "available"}`, children: [
3574
+ /* @__PURE__ */ jsx37("strong", { children: presentation.capacity.total.toLocaleString("en-US") }),
3575
+ /* @__PURE__ */ jsx37("small", { children: presentation.capacity.label ?? "available" })
2623
3576
  ] }) : null,
2624
- /* @__PURE__ */ jsxs28("div", { className: "chapter-membership-price-stack", children: [
2625
- presentation.priceEyebrow ? /* @__PURE__ */ jsx29("p", { className: "chapter-membership-price-eyebrow", children: presentation.priceEyebrow }) : tier.founding ? /* @__PURE__ */ jsx29("p", { className: "chapter-membership-price-eyebrow", children: "Founding member rate" }) : null,
2626
- /* @__PURE__ */ jsxs28("div", { className: "chapter-membership-price-row", children: [
2627
- tier.founding ? /* @__PURE__ */ jsx29("span", { className: "chapter-membership-compare", children: fmtMoney(tier.priceCents) }) : presentation.compareAtPriceCents !== void 0 ? /* @__PURE__ */ jsx29("span", { className: "chapter-membership-compare", children: fmtMoney(presentation.compareAtPriceCents) }) : null,
2628
- /* @__PURE__ */ jsx29("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.founding ? tier.founding.dueTodayCents : tier.priceCents) }),
2629
- !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx29("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
3577
+ /* @__PURE__ */ jsxs34("div", { className: "chapter-membership-price-stack", children: [
3578
+ presentation.priceEyebrow ? /* @__PURE__ */ jsx37("p", { className: "chapter-membership-price-eyebrow", children: presentation.priceEyebrow }) : tier.founding ? /* @__PURE__ */ jsx37("p", { className: "chapter-membership-price-eyebrow", children: "Founding member rate" }) : null,
3579
+ /* @__PURE__ */ jsxs34("div", { className: "chapter-membership-price-row", children: [
3580
+ tier.founding ? /* @__PURE__ */ jsx37("span", { className: "chapter-membership-compare", children: fmtMoney(tier.priceCents) }) : presentation.compareAtPriceCents !== void 0 ? /* @__PURE__ */ jsx37("span", { className: "chapter-membership-compare", children: fmtMoney(presentation.compareAtPriceCents) }) : null,
3581
+ /* @__PURE__ */ jsx37("span", { className: "chapter-membership-price", children: tier.free ? "Free" : fmtMoney(tier.founding ? tier.founding.dueTodayCents : tier.priceCents) }),
3582
+ !tier.free && presentation.billingLabel ? /* @__PURE__ */ jsx37("span", { className: "chapter-membership-billing", children: presentation.billingLabel }) : null
2630
3583
  ] })
2631
3584
  ] })
2632
3585
  ] }),
2633
- presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx29(Markdown2, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
2634
- summary ? /* @__PURE__ */ jsx29(Markdown2, { className: "chapter-membership-summary", source: summary }) : null,
2635
- presentation.benefits?.length ? /* @__PURE__ */ jsx29("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs28("li", { children: [
2636
- /* @__PURE__ */ jsx29("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
2637
- /* @__PURE__ */ jsx29(Markdown2, { className: "chapter-membership-benefit", source: benefit })
3586
+ presentation.priceNoteMarkdown ? /* @__PURE__ */ jsx37(Markdown4, { className: "chapter-membership-price-note", source: presentation.priceNoteMarkdown }) : null,
3587
+ summary ? /* @__PURE__ */ jsx37(Markdown4, { className: "chapter-membership-summary", source: summary }) : null,
3588
+ presentation.benefits?.length ? /* @__PURE__ */ jsx37("ul", { className: "chapter-membership-benefits", children: presentation.benefits.map((benefit, index) => /* @__PURE__ */ jsxs34("li", { children: [
3589
+ /* @__PURE__ */ jsx37("span", { className: "chapter-membership-check", "aria-hidden": "true", children: "\u2713" }),
3590
+ /* @__PURE__ */ jsx37(Markdown4, { className: "chapter-membership-benefit", source: benefit })
2638
3591
  ] }, `${tier.id}-benefit-${index}`)) }) : null,
2639
- presentation.callout ? /* @__PURE__ */ jsxs28("aside", { className: "chapter-membership-callout", children: [
2640
- presentation.callout.label ? /* @__PURE__ */ jsx29("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
2641
- /* @__PURE__ */ jsx29(Markdown2, { source: presentation.callout.bodyMarkdown })
3592
+ presentation.callout ? /* @__PURE__ */ jsxs34("aside", { className: "chapter-membership-callout", children: [
3593
+ presentation.callout.label ? /* @__PURE__ */ jsx37("p", { className: "chapter-membership-callout-label", children: presentation.callout.label }) : null,
3594
+ /* @__PURE__ */ jsx37(Markdown4, { source: presentation.callout.bodyMarkdown })
2642
3595
  ] }) : null,
2643
- /* @__PURE__ */ jsx29(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
3596
+ /* @__PURE__ */ jsx37(TierAction, { tierId: tier.id, label: action, mode: props.mode, selected: props.selected, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier })
2644
3597
  ] });
2645
3598
  }
2646
3599
  function MembershipCards(props) {
2647
3600
  const document2 = membershipPageDocument(props.document, { tiers: props.tiers });
2648
3601
  const byId = new Map(props.tiers.map((tier) => [tier.id, tier]));
2649
3602
  const mode = props.mode ?? "link";
2650
- return /* @__PURE__ */ jsxs28("section", { className: ["chapter-membership", props.className].filter(Boolean).join(" "), "data-chapter-membership": MEMBERSHIP_PAGE_RENDERER, "aria-label": props.ariaLabel ?? "Membership options", children: [
2651
- /* @__PURE__ */ jsx29("style", { children: MEMBERSHIP_CARDS_CSS }),
2652
- /* @__PURE__ */ jsx29("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
3603
+ return /* @__PURE__ */ jsxs34("section", { className: ["chapter-membership", props.className].filter(Boolean).join(" "), "data-chapter-membership": MEMBERSHIP_PAGE_RENDERER, "aria-label": props.ariaLabel ?? "Membership options", children: [
3604
+ /* @__PURE__ */ jsx37("style", { children: MEMBERSHIP_CARDS_CSS }),
3605
+ /* @__PURE__ */ jsx37("div", { className: "chapter-membership-grid", style: { "--chapter-membership-columns": String(Math.min(document2.tiers.length, 3)) }, children: document2.tiers.map((presentation) => {
2653
3606
  const tier = byId.get(presentation.tierId);
2654
- return /* @__PURE__ */ jsx29(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
3607
+ return /* @__PURE__ */ jsx37(TierCard, { tier, presentation, mode, selected: props.selectedTierId === tier.id, applicationPath: props.applicationPath, onSelectTier: props.onSelectTier }, tier.id);
2655
3608
  }) }),
2656
- document2.footnoteMarkdown ? /* @__PURE__ */ jsx29(Markdown2, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
3609
+ document2.footnoteMarkdown ? /* @__PURE__ */ jsx37(Markdown4, { className: "chapter-membership-footnote", source: document2.footnoteMarkdown }) : null
2657
3610
  ] });
2658
3611
  }
2659
3612
 
2660
3613
  export {
2661
- tzShort,
2662
- dayKey,
2663
- dayLabel,
2664
- timeLabel,
2665
- fullLabel,
2666
- fmtMoney,
2667
- fmtDate,
2668
- groupSlotsByDay,
2669
3614
  SlotPicker,
2670
3615
  ADDITIONAL_MEMBER_COPY,
2671
3616
  additionalMemberSelection,
@@ -2687,7 +3632,51 @@ export {
2687
3632
  NamedSeatRecipientLine,
2688
3633
  NamedSeatPurchase,
2689
3634
  MemberContentArea,
3635
+ placeholderBadge,
3636
+ MemberPlaceholderBody,
3637
+ PortalIcon,
3638
+ PROFILE_SEGMENT,
3639
+ routeFromLocation,
3640
+ hrefFor,
3641
+ sameRoute,
3642
+ PortalAvatar,
3643
+ navTag,
3644
+ PortalNav,
3645
+ LockedPill,
3646
+ UpgradeLink,
3647
+ LockedScreen,
3648
+ PlaceholderScreen,
3649
+ PortalPage,
3650
+ PortalTile,
3651
+ DefaultHome,
3652
+ memberMonth,
3653
+ startingPrompts,
3654
+ choosePrompt,
3655
+ MemberProfilePage,
3656
+ useCourse,
3657
+ seatLine,
3658
+ SeatActions,
3659
+ CoursePage,
3660
+ CourseHomePanel,
3661
+ articleFor,
3662
+ shareVars,
3663
+ linkedinShareUrl,
3664
+ mailtoHref,
3665
+ smsHref,
3666
+ isIOS,
3667
+ useMemberReferrals,
3668
+ CopyButton,
3669
+ OfficialCard,
3670
+ PostCard,
3671
+ InviteLinkCard,
3672
+ ReferralsCard,
3673
+ SharePage,
3674
+ ShareHome,
3675
+ ProfileReferrals,
3676
+ PACKAGED_PORTAL_FEATURES,
3677
+ MemberPortal,
2690
3678
  MembersArea,
3679
+ referralFromLocation,
2691
3680
  useTierSelection,
2692
3681
  TierPlaceholder,
2693
3682
  tierNeedsPayment,
@@ -2698,4 +3687,4 @@ export {
2698
3687
  membershipApplicationHref,
2699
3688
  MembershipCards
2700
3689
  };
2701
- //# sourceMappingURL=chunk-RTJKUOFE.js.map
3690
+ //# sourceMappingURL=chunk-QPFJBHHK.js.map