@odla-ai/chapter 0.60.0 → 0.62.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.
- package/README.md +138 -0
- package/dist/{chunk-QNP3UEIL.js → chunk-7CVP6VSO.js} +61 -1
- package/dist/chunk-7CVP6VSO.js.map +1 -0
- package/dist/{chunk-J7ES2OWN.js → chunk-MUUBATZJ.js} +899 -707
- package/dist/chunk-MUUBATZJ.js.map +1 -0
- package/dist/{chunk-D5UR5H6R.js → chunk-ZIYK3OV2.js} +869 -239
- package/dist/chunk-ZIYK3OV2.js.map +1 -0
- package/dist/copy-context-BJUkVUjE.d.ts +364 -0
- package/dist/index.cjs +3847 -3186
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +435 -89
- package/dist/index.d.ts +435 -89
- package/dist/index.js +3851 -3190
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +672 -3
- package/dist/ui/admin/index.js +2 -2
- package/dist/ui/index.d.ts +3 -2
- package/dist/ui/index.js +49 -3
- package/dist/ui/member/index.d.ts +254 -3
- package/dist/ui/member/index.js +48 -2
- package/dist/worker/index.cjs +1641 -477
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +222 -82
- package/dist/worker/index.d.ts +222 -82
- package/dist/worker/index.js +1572 -408
- package/dist/worker/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-D5UR5H6R.js.map +0 -1
- package/dist/chunk-J7ES2OWN.js.map +0 -1
- package/dist/chunk-QNP3UEIL.js.map +0 -1
- package/dist/copy-context-BP3G1iBd.d.ts +0 -899
package/README.md
CHANGED
|
@@ -604,6 +604,130 @@ producer process. The value is never accepted as an argument. `project` is the
|
|
|
604
604
|
server-disclosure proof and omits locked bodies; `compose` is a local operator
|
|
605
605
|
authoring view and contains the supplied content.
|
|
606
606
|
|
|
607
|
+
### Member portal
|
|
608
|
+
|
|
609
|
+
Schema version 2 of the member catalog turns member pages into a portal:
|
|
610
|
+
ordered navigation sections, page kinds, tags and icons. Version 1 documents
|
|
611
|
+
keep validating and keep their alphabetical section order; every version 2
|
|
612
|
+
field is refused on a version 1 document with a path that names it.
|
|
613
|
+
|
|
614
|
+
```ts
|
|
615
|
+
const local = {
|
|
616
|
+
schemaVersion: 2,
|
|
617
|
+
sections: [
|
|
618
|
+
{ id: "education", label: "Education" },
|
|
619
|
+
{ id: "community", label: "Community" },
|
|
620
|
+
],
|
|
621
|
+
pages: [
|
|
622
|
+
{
|
|
623
|
+
id: "course", slug: "investing-for-impact", title: "Investing for Impact",
|
|
624
|
+
kind: "feature", feature: { name: "course", ref: "2027-01-investing-for-impact" },
|
|
625
|
+
navigation: { section: "education", order: 1, tag: "new", icon: "course" },
|
|
626
|
+
audience: { kind: "tiers", tierIds: ["founding", "steward"] },
|
|
627
|
+
upsell: { owner: "chapter", offer: { tierId: "founding", ctaLabel: "Upgrade my membership" } },
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
id: "library", slug: "library", title: "Library", kind: "placeholder",
|
|
631
|
+
placeholder: {}, summary: "Recordings, guides and worksheets.",
|
|
632
|
+
navigation: { section: "education", order: 2, icon: "library" },
|
|
633
|
+
audience: { kind: "all-members" },
|
|
634
|
+
},
|
|
635
|
+
],
|
|
636
|
+
};
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
- `kind: "markdown"` (the default) needs `bodyMarkdown`. `kind: "placeholder"`
|
|
640
|
+
announces a page that is not open: `availableYear` reads "Coming in 2027",
|
|
641
|
+
without it "Coming soon"; `badge` replaces that text and `actionLabel`
|
|
642
|
+
renders a disabled button. `kind: "feature"` stands for a packaged feature
|
|
643
|
+
(`course`, `share`) and carries no body.
|
|
644
|
+
- Sections render in the order a document declares them. A chapter's own
|
|
645
|
+
sections come first; it may list a parent section id to place it. Version 1
|
|
646
|
+
labels follow alphabetically. With `sections` declared, every page must name
|
|
647
|
+
one of them or a section a parent page uses.
|
|
648
|
+
- One decision gates everything: `memberPageAllowed(page, viewer)` locks a
|
|
649
|
+
catalog page, and `memberFeatureAccess(content, viewer, name, ref)` answers
|
|
650
|
+
`allowed`, `locked` or `absent` for the feature routes behind a feature page.
|
|
651
|
+
No feature page means the feature is off.
|
|
652
|
+
|
|
653
|
+
Enable the portal beside `memberContent`:
|
|
654
|
+
|
|
655
|
+
```ts
|
|
656
|
+
defineChapter({
|
|
657
|
+
// ...
|
|
658
|
+
memberContent: { localAuthoring: true },
|
|
659
|
+
memberPortal: {
|
|
660
|
+
profilePrompts: ["The best money advice I ever got", "My go-to spot in Utah"],
|
|
661
|
+
maxPrompts: 3, // default 3
|
|
662
|
+
photos: true, // default true
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
`/api/me` then carries `portal` for an active member: `{ memberId, tier: { id,
|
|
668
|
+
name }, memberSince, displayName, firstName, initials, location, photoUrl }`.
|
|
669
|
+
The tier is the member's entitlement tier (below). Render it with the stock
|
|
670
|
+
member area; every pre-membership state keeps its screen:
|
|
671
|
+
|
|
672
|
+
```tsx
|
|
673
|
+
<MembersArea api={api} signOut={signOut} portal={{
|
|
674
|
+
brand: <><Mark /> Silver & Salt Capital</>,
|
|
675
|
+
sidebarTokens: { surface: "#2F3E34", text: "#FBF8F2", textMuted: "#B8C4BB", accent: "#7CB83F", onAccent: "#2F3E34" },
|
|
676
|
+
upsellHref: () => "https://example.org/membership#memberships",
|
|
677
|
+
routing: { mode: "hash", basePath: "/members" },
|
|
678
|
+
}} />
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
`MemberPortal` renders the sidebar (a top bar with a menu at 860px and below),
|
|
682
|
+
a header with "Welcome back" or a back link, the tier pill and a profile
|
|
683
|
+
button, Home, catalog pages and Your Profile. Routing uses the history API, so
|
|
684
|
+
back and refresh stay put; `hash` mode works on any static host, `path` mode
|
|
685
|
+
needs the host to serve the portal under `basePath`. Home composes each
|
|
686
|
+
feature's Home panel in catalog order (a locked one gets an upgrade overlay)
|
|
687
|
+
and the placeholder pages as "Coming to your portal" tiles; `home.top`,
|
|
688
|
+
`home.middle` and `home.tile` slot into it, or pass a function to replace it.
|
|
689
|
+
Words come from `copy.portal` (`shell`, `pages`, `profile`); the package ships
|
|
690
|
+
neutral defaults and a site supplies its own voice. The sidebar is a token
|
|
691
|
+
island: `sidebarTokens`, or the theme's `.ui-invert` palette.
|
|
692
|
+
|
|
693
|
+
Your Profile edits the member's own record only, under a revision guard:
|
|
694
|
+
`GET`/`PUT /api/member/profile`. Editable fields are the ones the member
|
|
695
|
+
authority contract lists (`MEMBER_PROFILE_FIELDS` maps each to its row):
|
|
696
|
+
preferred name, location, up to `maxPrompts` answers of 280 characters or fewer
|
|
697
|
+
to the site's prompts, and an "introduced by" name. A name that matches exactly
|
|
698
|
+
one member links to them; anything else opens a review item. It never earns
|
|
699
|
+
credit. `POST /api/member/profile/photo` takes a raw JPEG or PNG body: the
|
|
700
|
+
browser resizes to 400×400, the server checks magic bytes, a 10 MB cap and the
|
|
701
|
+
dimensions, strips EXIF, XMP, comments and PNG text chunks at byte level, and
|
|
702
|
+
stores the file privately in odla-db storage behind short-lived signed URLs.
|
|
703
|
+
Gift purchases and the gift seat card move onto Your Profile.
|
|
704
|
+
|
|
705
|
+
**Hubs.** A hub (`mode: "hub"`) may now enable `memberContent` and
|
|
706
|
+
`memberPortal`. Its members are the people an admin grants access to under
|
|
707
|
+
Settings → Member portal (`GET`/`POST /api/admin/hub-members`, `DELETE
|
|
708
|
+
/api/admin/hub-members/:id`). A grant never grants admin access and an admin
|
|
709
|
+
role never makes anyone a member. A hub has no tiers, so its catalog pages are
|
|
710
|
+
`all-members`; courses and invite links are chapter-only.
|
|
711
|
+
|
|
712
|
+
**Upgrades (launch path).** An admin can change an approved member's portal
|
|
713
|
+
tier after a payment taken outside checkout: the member record's Billing tab,
|
|
714
|
+
or `POST /api/admin/applications/:id/entitlement-tier` with `tierId`, a
|
|
715
|
+
required `reason`, an optional `paymentReference` and `endsAt`. `DELETE`
|
|
716
|
+
reverts to the purchased tier, and `GET` returns both tiers with the change
|
|
717
|
+
history. The purchase and its membership-authority access check are untouched;
|
|
718
|
+
every change appends a `memberTierChanges` row.
|
|
719
|
+
|
|
720
|
+
**Review queue.** Settings → Member portal lists open review items (`GET
|
|
721
|
+
/api/admin/portal-review`); each is resolved once with a written decision
|
|
722
|
+
(`POST /api/admin/portal-review/:id`).
|
|
723
|
+
|
|
724
|
+
**Admin second factor.** `auth.adminSecondFactor: "report" | "enforce"`
|
|
725
|
+
checks Clerk's factor-verification claim (`fva`) in the shared admin gate, so
|
|
726
|
+
every admin route inherits it. `report` allows and logs
|
|
727
|
+
`chapter.admin_second_factor_missing`; `enforce` refuses, and `/api/me`
|
|
728
|
+
returns `secondFactorRequired: true`. Turn MFA on in the Clerk instance and
|
|
729
|
+
enroll admins before enforcing.
|
|
730
|
+
|
|
607
731
|
### Private Builder Library
|
|
608
732
|
|
|
609
733
|
Builder source material is deliberately separate from member publishing. Enable
|
|
@@ -1622,8 +1746,22 @@ These bite silently — a smoke test won't catch them:
|
|
|
1622
1746
|
| `meetUrl`, `htmlLink` | `prepEmail` | the booked meeting's Google Meet and calendar links (blank when the provider returned none) |
|
|
1623
1747
|
| `meetingTime`, `meetingLink` | `prepEmail` | aliases of `startAt` and `meetUrl` (falling back to `htmlLink`) for copy written before those existed |
|
|
1624
1748
|
| `adminUrl`, `membersUrl` | `adminNotification`, `submitConfirmation`, `onboardingInvite` | `<origin>/admin/` and `<origin>/members/` from the request that fired the send |
|
|
1749
|
+
| `namedSeat`, `namedSeatRecipientName` | `adminNotification`, `submitConfirmation`, `paymentConfirmation`, `prepEmail`, `onboardingInvite` | `paid` or `included` when the frozen checkout carries a gift seat for another person, else blank (so `{{#namedSeat}}` hides); the recipient's name as entered at checkout. A host event reads the same values through `checkoutVariables` |
|
|
1625
1750
|
| `refundPolicyText`, `commitmentText`, `normsText` | all | the group row's policy copy |
|
|
1626
1751
|
|
|
1752
|
+
**Sections and an HTML body.** `{{#name}} … {{/name}}` keeps its contents
|
|
1753
|
+
only when `name` has a value, so a line such as
|
|
1754
|
+
`{{#namedSeat}}Your gift for {{namedSeatRecipientName}} is paid.{{/namedSeat}}`
|
|
1755
|
+
appears for gift buyers alone; a section on its own line disappears with
|
|
1756
|
+
its line. The event must supply a section's name like any variable, and an
|
|
1757
|
+
unclosed section blocks the send. A template may also carry an `html`
|
|
1758
|
+
body (Settings → Email, under the plain body) with the same placeholders
|
|
1759
|
+
and sections: values are escaped, a line break inside a value becomes a
|
|
1760
|
+
`<br>`, the text body is still sent as the plain alternative and recorded
|
|
1761
|
+
in the log, and an unknown placeholder in the HTML blocks the send like
|
|
1762
|
+
one in the text. Without an `html` body the text is rendered to HTML by
|
|
1763
|
+
`textToHtml`.
|
|
1764
|
+
|
|
1627
1765
|
**From display name.** `emails.fromName` (seeded to the group row, editable
|
|
1628
1766
|
in Settings → Email) turns the From header into `{ name, email }` —
|
|
1629
1767
|
`Tori Horton <tori@…>` in an inbox — while the address stays the Worker's
|
|
@@ -117,6 +117,8 @@ var DEFAULT_ADMIN_COPY = {
|
|
|
117
117
|
enabled: "Enabled",
|
|
118
118
|
subject: "Subject",
|
|
119
119
|
body: "Body",
|
|
120
|
+
htmlBody: "HTML body (optional)",
|
|
121
|
+
htmlBodyHint: "Sent beside the plain text when present, with the same {{placeholders}} and {{#name}} sections; values are escaped. Leave empty to send the plain text rendered as HTML.",
|
|
120
122
|
save: "Save changes",
|
|
121
123
|
sendLog: "Send log",
|
|
122
124
|
sentColumn: "Sent",
|
|
@@ -329,6 +331,8 @@ var DEFAULT_MEMBERS_COPY = {
|
|
|
329
331
|
apply: "Apply for membership",
|
|
330
332
|
refunded: "Membership refunded",
|
|
331
333
|
refundedBody: "Your fee has been refunded in full and your membership is canceled.",
|
|
334
|
+
declined: "Thank you for applying",
|
|
335
|
+
declinedBody: "We are not able to offer a membership at this time. Any fee you paid has been refunded.",
|
|
332
336
|
active: "Your membership is active.",
|
|
333
337
|
renews: "Your membership is active and renews {date}.",
|
|
334
338
|
introductionCall: "Your introduction call",
|
|
@@ -373,11 +377,67 @@ var DEFAULT_MEMBERS_COPY = {
|
|
|
373
377
|
}
|
|
374
378
|
};
|
|
375
379
|
|
|
380
|
+
// src/copy-portal.ts
|
|
381
|
+
var DEFAULT_PORTAL_COPY = {
|
|
382
|
+
shell: {
|
|
383
|
+
navLabel: "Member portal",
|
|
384
|
+
welcomeBack: "Welcome back",
|
|
385
|
+
home: "Home",
|
|
386
|
+
backHome: "\u2190 Home",
|
|
387
|
+
yourProfile: "Your Profile",
|
|
388
|
+
signedInAs: "Signed in as",
|
|
389
|
+
signOut: "Sign out",
|
|
390
|
+
adminConsole: "Admin console",
|
|
391
|
+
openMenu: "Open menu",
|
|
392
|
+
closeMenu: "Close menu",
|
|
393
|
+
locked: "Locked",
|
|
394
|
+
loading: "Loading\u2026",
|
|
395
|
+
loadFailed: "Your member portal could not be loaded. Please refresh the page.",
|
|
396
|
+
notFound: "This page is not available."
|
|
397
|
+
},
|
|
398
|
+
pages: {
|
|
399
|
+
comingSoon: "Coming soon",
|
|
400
|
+
comingIn: "Coming in {year}",
|
|
401
|
+
includedWith: "Included with {tierName} membership",
|
|
402
|
+
upgrade: "See membership options",
|
|
403
|
+
comingToPortal: "Coming to your portal",
|
|
404
|
+
tagNew: "New",
|
|
405
|
+
tagSoon: "Soon"
|
|
406
|
+
},
|
|
407
|
+
profile: {
|
|
408
|
+
eyebrow: "Your profile",
|
|
409
|
+
editProfile: "Edit profile",
|
|
410
|
+
saveProfile: "Save profile",
|
|
411
|
+
saving: "Saving\u2026",
|
|
412
|
+
cancel: "Cancel",
|
|
413
|
+
uploadPhoto: "Upload a photo",
|
|
414
|
+
changePhoto: "Change photo",
|
|
415
|
+
photoUploading: "Uploading\u2026",
|
|
416
|
+
photoFailed: "That photo could not be uploaded. Choose a JPEG or PNG under 10 MB.",
|
|
417
|
+
memberSince: "{tierName} since {month}",
|
|
418
|
+
promptsTitle: "In your own words",
|
|
419
|
+
promptsNote: "Choose up to {count} prompts.",
|
|
420
|
+
answerEmpty: "Select Edit profile to answer.",
|
|
421
|
+
addPrompt: "+ {prompt}",
|
|
422
|
+
howYouFoundUs: "How you found us",
|
|
423
|
+
referredBy: "Referred by",
|
|
424
|
+
referredByNote: "recorded from their invite link",
|
|
425
|
+
introducedBy: "Anyone else who introduced you?",
|
|
426
|
+
introducedByPlaceholder: "Their name (optional)",
|
|
427
|
+
namePlaceholder: "Your name",
|
|
428
|
+
locationPlaceholder: "Where you live",
|
|
429
|
+
saved: "Saved",
|
|
430
|
+
conflict: "Your profile changed in another window. Reload to see the latest version.",
|
|
431
|
+
saveFailed: "Your profile could not be saved. Please try again."
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
|
|
376
435
|
// src/copy-resolve.ts
|
|
377
436
|
var DEFAULT_CHAPTER_COPY = {
|
|
378
437
|
common: DEFAULT_COMMON_COPY,
|
|
379
438
|
join: DEFAULT_JOIN_COPY,
|
|
380
439
|
members: DEFAULT_MEMBERS_COPY,
|
|
440
|
+
portal: DEFAULT_PORTAL_COPY,
|
|
381
441
|
admin: DEFAULT_ADMIN_COPY
|
|
382
442
|
};
|
|
383
443
|
var isObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -527,4 +587,4 @@ export {
|
|
|
527
587
|
useChapterCopy,
|
|
528
588
|
BrandStyle
|
|
529
589
|
};
|
|
530
|
-
//# sourceMappingURL=chunk-
|
|
590
|
+
//# sourceMappingURL=chunk-7CVP6VSO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/copy-context.tsx","../src/copy-defaults-admin.ts","../src/copy-defaults.ts","../src/copy-portal.ts","../src/copy-resolve.ts","../src/brand.ts","../src/ui/brand-style.tsx"],"sourcesContent":["import { createContext, type ComponentChildren } from \"preact\";\nimport { useContext } from \"preact/hooks\";\nimport {\n DEFAULT_CHAPTER_COPY,\n resolveChapterCopy,\n type ChapterCopy,\n type ChapterCopyInput,\n} from \"../copy.js\";\n\nconst ChapterCopyContext = createContext<ChapterCopy>(DEFAULT_CHAPTER_COPY);\n\n/** Place a resolved or partial copy contract around any Chapter UI surface. */\nexport function ChapterCopyProvider(props: {\n copy?: ChapterCopy | ChapterCopyInput;\n children: ComponentChildren;\n}) {\n return (\n <ChapterCopyContext.Provider value={resolveChapterCopy(props.copy)}>\n {props.children}\n </ChapterCopyContext.Provider>\n );\n}\n\n/** Read the nearest complete Chapter copy contract. */\nexport function useChapterCopy(): ChapterCopy {\n return useContext(ChapterCopyContext);\n}\n","import type { ChapterCopy } from \"./copy.js\";\n\nexport const DEFAULT_ADMIN_COPY: ChapterCopy[\"admin\"] = {\n auth: {\n checking: \"Checking access…\",\n notAuthorized: \"Not authorized\",\n accountNotAuthorized: \"{account} isn't on the admin list.\",\n thisAccount: \"This account\",\n signOut: \"Sign out\",\n loading: \"Loading…\",\n signInNotConfigured: \"Sign-in not configured\",\n missingPublishableKey: \"No Clerk publishable key is set for this environment yet.\",\n noWorkspaces: \"No admin workspaces are configured.\",\n signInTagline: \"Admin sign-in, invite only\",\n },\n shell: {\n adminRole: \"admin\",\n adminConsole: \"Admin Console\",\n adminName: \"Admin\",\n navigationLabel: \"Admin navigation\",\n },\n workspaces: {\n dashboard: \"Dashboard\",\n overview: \"Overview\",\n billing: \"Billing\",\n people: \"People\",\n admission: \"Admission\",\n network: \"Network\",\n portfolio: \"Portfolio\",\n settings: \"Settings\",\n calendar: \"Calendar\",\n email: \"Email\",\n chapters: \"Chapters\",\n formations: \"Formation applications\",\n dealFlow: \"Deal flow\",\n dashboardViewsLabel: \"Dashboard views\",\n settingsViewsLabel: \"Settings views\",\n collectionsLabel: \"CRM collections\",\n portfolioViewsLabel: \"Portfolio views\",\n },\n dashboard: {\n loading: \"Loading dashboard…\",\n loadFailed: \"Couldn't load the dashboard\",\n upcomingCalls: \"Upcoming calls\",\n callsNeedAttention: \"{count} need attention\",\n noUpcomingCalls: \"No upcoming calls.\",\n drift: \"drift\",\n open: \"Open\",\n meet: \"Meet\",\n applications: \"Applications\",\n newMembers: \"New members\",\n newMembersUnavailable: \"Appears once billing is connected.\",\n revenueAdded: \"Revenue · annual run rate added\",\n revenue: \"Revenue\",\n revenueUnavailable: \"Appears once billing is connected.\",\n pipeline: \"Pipeline\",\n pipelineLabel: \"Membership pipeline\",\n thisWeek: \"+{count} this week\",\n noChange: \"no change\",\n activeMemberships: \"Active memberships\",\n annualRunRate: \"Annual run rate\",\n testMode: \"test mode\",\n },\n billing: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load billing\",\n notConfigured: \"Billing isn't configured. No Stripe key is vaulted for this group.\",\n active: \"Active\",\n annualized: \"Annualized\",\n renewingSoon: \"Renewing within 60 days\",\n pastDue: \"Past due\",\n subscriptions: \"Subscriptions\",\n testMode: \"Stripe test mode\",\n truncated: \"Showing the first 100 subscriptions. The list is truncated.\",\n name: \"Name\",\n email: \"Email\",\n application: \"Application\",\n subscription: \"Subscription\",\n cancelling: \"cancelling\",\n amount: \"Amount\",\n renews: \"Renews\",\n },\n availability: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load availability\",\n saved: \"Saved.\",\n title: \"Booking availability\",\n days: \"Days\",\n startHour: \"Start hour (0–24)\",\n endHour: \"End hour (0–24)\",\n slotMinutes: \"Slot length (minutes)\",\n minNoticeHours: \"Minimum notice (hours)\",\n windowDays: \"Booking window (days)\",\n timezone: \"Timezone\",\n summaryTemplate: \"Event summary template\",\n save: \"Save availability\",\n dayLabels: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n },\n email: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load email config\",\n saved: \"Saved.\",\n sent: \"Sent {template} to {address}{redirected}.\",\n testFailed: \"Test send did not deliver.\",\n delivery: \"Delivery\",\n notificationAddress: \"Notification address\",\n replyTo: \"Reply-to\",\n fromName: \"From name\",\n fromNameHint: \"Shown as the sender; the address stays the Worker's configured one.\",\n spamRisk: \"Spam-filter risk\",\n debugInbox: \"Debug inbox\",\n debugInboxHint: \"Non-production sends redirect here.\",\n sendTest: \"Send test\",\n enabled: \"Enabled\",\n subject: \"Subject\",\n body: \"Body\",\n htmlBody: \"HTML body (optional)\",\n htmlBodyHint: \"Sent beside the plain text when present, with the same {{placeholders}} and {{#name}} sections; values are escaped. Leave empty to send the plain text rendered as HTML.\",\n save: \"Save changes\",\n sendLog: \"Send log\",\n sentColumn: \"Sent\",\n templateColumn: \"Template\",\n statusColumn: \"Status\",\n toColumn: \"To\",\n failed: \"failed\",\n redirected: \"redirected\",\n delivered: \"delivered\",\n },\n meetings: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load meetings\",\n cancelConfirm: \"Cancel this call? The calendar provider notifies the attendee.\",\n newStartPrompt: \"New start time (for example, 2026-08-01 14:00):\",\n parseFailed: \"Couldn't parse that time.\",\n agenda: \"Agenda\",\n empty: \"No meetings.\",\n unknown: \"(unknown)\",\n drift: \"drift\",\n meet: \"Meet\",\n reschedule: \"Reschedule\",\n cancel: \"Cancel\",\n },\n admission: {\n title: \"Interview decisions\",\n description: \"Local decisions override Parent decisions. Requiring an interview locally suppresses a Parent waiver for the same email.\",\n email: \"Email\",\n decision: \"Decision\",\n expires: \"Expires\",\n note: \"Internal note\",\n waive: \"Waive interview\",\n require: \"Require interview\",\n add: \"Add decision\",\n ledger: \"Effective grant ledger\",\n loading: \"Loading…\",\n consumed: \"consumed\",\n revoked: \"revoked\",\n expired: \"expired\",\n active: \"active\",\n local: \"local\",\n parent: \"Parent\",\n revoke: \"Revoke\",\n },\n network: {\n title: \"Share to a follower\",\n allowlistDescription: \"Sends the target's allowlisted fields. Its pipeline and account state remain local.\",\n sharing: \"Sharing…\",\n shareWith: \"Share with {target}\",\n shared: \"Shared with {target}.\",\n shareFailed: \"Could not share with {target}.\",\n deliveryFailed: \"Delivery failed.\",\n loadingRollup: \"Loading network overview…\",\n loadRollupFailed: \"Couldn't load the network overview\",\n overview: \"Network overview\",\n configuredFollowers: \"Configured followers\",\n reachableFollowers: \"Reachable followers\",\n recordsAcrossFollowers: \"Records across followers\",\n noFollowers: \"No follower sites are configured.\",\n followers: \"Followers\",\n available: \"available\",\n unavailable: \"unavailable\",\n transportReachable: \"transport reachable\",\n transportUnavailable: \"transport unavailable\",\n transportNotChecked: \"transport not checked\",\n edgeUnpaired: \"edge unpaired\",\n edgeUnpairedHint: \"This follower's shared secret is not in the leader vault, so nothing can be signed. Pair both vaults from an enrolled machine:\",\n bindingTransport: \"service binding {binding}\",\n urlTransport: \"public URL transport\",\n activeConfiguration: \"active configuration r{revision}\",\n noActiveConfiguration: \"no active managed configuration\",\n publicSiteAvailable: \"public site reachable\",\n publicSiteUnavailable: \"public site unavailable\",\n publicSiteNotChecked: \"public site not probed\",\n recordTotals: \"Portfolio records\",\n noRecordTotals: \"No aggregate records are available yet.\",\n },\n records: {\n workflowMissing: \"This record is not linked to an application workflow.\",\n invitation: \"Invitation\",\n comms: \"Comms\",\n commsHistory: \"Comms history\",\n scheduling: \"Scheduling\",\n billing: \"Billing\",\n notes: \"Notes\",\n access: \"Access\",\n sharing: \"Sharing\",\n lifecycle: \"Access & lifecycle\",\n role: \"Role\",\n superAdminHint: \"Super-admin, managed in odla Studio.\",\n noAccount: \"No linked account yet. The role is set once they sign in.\",\n approve: \"Approve\",\n decline: \"Decline\",\n refund: \"Refund\",\n approved: \"Approved.\",\n declined: \"Declined.\",\n refunded: \"Refund issued.\",\n roleSet: \"Role set to {role}.\",\n messageSent: \"Message sent.\",\n messageNotSent: \"Message was not sent.\",\n loadingTemplates: \"Loading templates…\",\n template: \"Template\",\n chooseTemplate: \"Choose a template…\",\n sending: \"Sending…\",\n send: \"Send\",\n noMessages: \"No messages recorded.\",\n message: \"Message\",\n newStartPrompt: \"New start time:\",\n parseFailed: \"Couldn't parse that time.\",\n cancelConfirm: \"Cancel this call?\",\n callCancelled: \"Call cancelled.\",\n callRescheduled: \"Call rescheduled.\",\n noApplication: \"No application is linked to this record.\",\n loadingMeetings: \"Loading meetings…\",\n noMeetings: \"No meetings recorded.\",\n joinMeeting: \"Join meeting\",\n openCalendar: \"Open calendar\",\n reschedule: \"Reschedule\",\n cancel: \"Cancel\",\n },\n};\n","import type { ChapterCopy } from \"./copy.js\";\n\nexport const DEFAULT_COMMON_COPY: ChapterCopy[\"common\"] = {\n loading: \"Loading…\",\n saved: \"Saved.\",\n unknown: \"(unknown)\",\n};\n\nexport const DEFAULT_JOIN_COPY: ChapterCopy[\"join\"] = {\n namedSeat: {\n title: \"Your membership invitation\",\n from: \"{purchaserName} invited you.\",\n prepaid: \"Your {tierName} seat is already paid for.\",\n body: \"Accept your invitation, then complete your own application requirements. You will not be asked to pay for this seat.\",\n consent: \"I accept this membership for myself. It cannot be transferred and remains current while the purchaser's membership and my seat are current.\",\n accept: \"Accept my invitation\", accepting: \"Confirming your invitation…\", loading: \"Loading your invitation…\",\n unavailable: \"Your invitation is unavailable. Sign in with the invited email address, then try again.\",\n retry: \"Try again\", failure: \"We could not confirm acceptance. Retry this invitation; you do not need to purchase another membership.\",\n },\n form: {\n submit: \"Submit application\",\n submitting: \"Submitting…\",\n submitFailed: \"Your application could not be submitted.\",\n unexpectedFailure: \"Something went wrong. Please try again.\",\n },\n booking: {\n title: \"Book your introduction\",\n intro: \"Choose a time to meet your chapter and get to know each other.\",\n unavailable: \"Scheduling is briefly unavailable. We'll reach out by email to arrange your call.\",\n loadFailed: \"Times are briefly unavailable. Please try again.\",\n slotTaken: \"That time was just taken. Here are the current openings.\",\n failed: \"The booking could not be completed.\",\n loading: \"Loading available times…\",\n book: \"Book this time\",\n booking: \"Booking…\",\n pickDate: \"Pick a date\",\n pickTime: \"Pick a time\",\n },\n payment: {\n setupFailed: \"Payment could not be set up. Please try again.\",\n preparing: \"Preparing secure payment…\",\n pending: \"Confirming your payment…\",\n processing: \"Processing…\",\n payAndContinue: \"Pay and continue\",\n incomplete: \"The payment did not complete. Please try again.\",\n consent: \"I have read and agree to the pricing and refund policy above.\",\n foundingMember: \"Founding member rate\",\n foundingDiscount: \"Founding discount\",\n standardRate: \"Standard rate\",\n yourRate: \"Your rate\",\n },\n done: {\n label: \"You're booked\",\n calendarInvite: \"A calendar invitation with the video call link is on its way to your email.\",\n memberArea: \"Go to your member area\",\n },\n accepted: {\n label: \"You're approved\",\n body: \"Your membership is approved. No introduction call is required.\",\n },\n gift: {\n chooseSelf: \"Buy membership for myself\",\n optionsLabel: \"Or give access to someone else\",\n label: \"Your gift code\",\n body: \"Share this one-time code with the recipient. It becomes active after payment is confirmed.\",\n save: \"Save it now. For security, this code is shown only once.\",\n rotate: \"Generate a new code\",\n rotateFailed: \"A new code could not be generated.\",\n claimTitle: \"Have a gift code?\",\n claimBody: \"Use it here instead of paying by card.\",\n code: \"One-time code\",\n redeem: \"Use gift code\",\n redeemed: \"Gift code applied.\",\n redeemFailed: \"The code is invalid, unavailable, or does not match this membership.\",\n },\n};\n\nexport const DEFAULT_MEMBERS_COPY: ChapterCopy[\"members\"] = {\n loadFailed: \"Sign in is briefly unavailable. Please refresh.\",\n account: {\n signOut: \"Sign out\",\n adminConsole: \"Admin console\",\n },\n provisional: {\n cardLabel: \"Your Application\",\n applicationNeeded: \"One step remains\",\n applicationNeededBody: \"Your account is ready, and the application that completes it takes a few minutes.\",\n apply: \"Apply for membership\",\n refunded: \"Membership refunded\",\n refundedBody: \"Your fee has been refunded in full and your membership is canceled.\",\n declined: \"Thank you for applying\",\n declinedBody: \"We are not able to offer a membership at this time. Any fee you paid has been refunded.\",\n active: \"Your membership is active.\",\n renews: \"Your membership is active and renews {date}.\",\n introductionCall: \"Your introduction call\",\n calendarInvite: \"A calendar invitation with the video call link is in your email.\",\n joinCall: \"Join the video call\",\n bookCall: \"Book your introduction call\",\n bookCallBody: \"Your application is in. Choose a time below, and a calendar invitation will reach your email.\",\n chooseTime: \"Choose a time\",\n },\n full: {\n welcome: \"Welcome back.\",\n },\n gifts: {\n title: \"Give access\",\n body: \"Purchase access for someone else and share the one-time claim code.\",\n noneAvailable: \"No gift options are available right now.\",\n paid: \"Payment submitted. Save the code below while it is visible.\",\n saveCode: \"This code is shown once. Save it before leaving this page.\",\n purchases: \"Your gifts\",\n noPurchases: \"No gift purchases yet.\",\n pending: \"Pending\",\n redeemed: \"Redeemed\",\n expired: \"Expired\",\n revoked: \"Revoked\",\n rotate: \"Rotate code\",\n rotated: \"A new code was generated. Save it now.\",\n rotateFailed: \"The code could not be rotated.\",\n redeemTitle: \"Redeem a gift\",\n code: \"One-time code\",\n redeem: \"Redeem\",\n redeemFailed: \"The code is invalid or unavailable.\",\n },\n reschedule: {\n changeTime: \"Change your time\",\n unavailable: \"Scheduling is briefly unavailable. We'll reach out by email to arrange your call.\",\n loadFailed: \"Times are briefly unavailable. Please try again.\",\n slotGone: \"That time is no longer available. Please pick another.\",\n loading: \"Loading available times…\",\n noTimes: \"No open times right now. Please check back soon.\",\n rescheduling: \"Rescheduling…\",\n keepTime: \"Keep my current time\",\n },\n};\n","type TextFields<Key extends string> = { [Field in Key]: string };\n\n/**\n * Member portal language. Placeholders in braces are filled by\n * `formatChapterCopy()`. Sites override any field through `copy.portal`.\n */\nexport interface PortalCopy {\n shell: TextFields<\n | \"navLabel\" | \"welcomeBack\" | \"home\" | \"backHome\" | \"yourProfile\" | \"signedInAs\" | \"signOut\"\n | \"adminConsole\" | \"openMenu\" | \"closeMenu\" | \"locked\" | \"loading\" | \"loadFailed\" | \"notFound\"\n >;\n pages: TextFields<\n | \"comingSoon\" | \"comingIn\" | \"includedWith\" | \"upgrade\" | \"comingToPortal\" | \"tagNew\" | \"tagSoon\"\n >;\n profile: TextFields<\n | \"eyebrow\" | \"editProfile\" | \"saveProfile\" | \"saving\" | \"cancel\" | \"uploadPhoto\" | \"changePhoto\"\n | \"photoUploading\" | \"photoFailed\" | \"memberSince\" | \"promptsTitle\" | \"promptsNote\" | \"answerEmpty\"\n | \"addPrompt\" | \"howYouFoundUs\" | \"referredBy\" | \"referredByNote\" | \"introducedBy\" | \"introducedByPlaceholder\"\n | \"namePlaceholder\" | \"locationPlaceholder\" | \"saved\" | \"conflict\" | \"saveFailed\"\n >;\n}\n\n/** Neutral defaults; a site supplies its own voice. */\nexport const DEFAULT_PORTAL_COPY: PortalCopy = {\n shell: {\n navLabel: \"Member portal\",\n welcomeBack: \"Welcome back\",\n home: \"Home\",\n backHome: \"← Home\",\n yourProfile: \"Your Profile\",\n signedInAs: \"Signed in as\",\n signOut: \"Sign out\",\n adminConsole: \"Admin console\",\n openMenu: \"Open menu\",\n closeMenu: \"Close menu\",\n locked: \"Locked\",\n loading: \"Loading…\",\n loadFailed: \"Your member portal could not be loaded. Please refresh the page.\",\n notFound: \"This page is not available.\",\n },\n pages: {\n comingSoon: \"Coming soon\",\n comingIn: \"Coming in {year}\",\n includedWith: \"Included with {tierName} membership\",\n upgrade: \"See membership options\",\n comingToPortal: \"Coming to your portal\",\n tagNew: \"New\",\n tagSoon: \"Soon\",\n },\n profile: {\n eyebrow: \"Your profile\",\n editProfile: \"Edit profile\",\n saveProfile: \"Save profile\",\n saving: \"Saving…\",\n cancel: \"Cancel\",\n uploadPhoto: \"Upload a photo\",\n changePhoto: \"Change photo\",\n photoUploading: \"Uploading…\",\n photoFailed: \"That photo could not be uploaded. Choose a JPEG or PNG under 10 MB.\",\n memberSince: \"{tierName} since {month}\",\n promptsTitle: \"In your own words\",\n promptsNote: \"Choose up to {count} prompts.\",\n answerEmpty: \"Select Edit profile to answer.\",\n addPrompt: \"+ {prompt}\",\n howYouFoundUs: \"How you found us\",\n referredBy: \"Referred by\",\n referredByNote: \"recorded from their invite link\",\n introducedBy: \"Anyone else who introduced you?\",\n introducedByPlaceholder: \"Their name (optional)\",\n namePlaceholder: \"Your name\",\n locationPlaceholder: \"Where you live\",\n saved: \"Saved\",\n conflict: \"Your profile changed in another window. Reload to see the latest version.\",\n saveFailed: \"Your profile could not be saved. Please try again.\",\n },\n};\n","import { DEFAULT_ADMIN_COPY } from \"./copy-defaults-admin.js\";\nimport {\n DEFAULT_COMMON_COPY,\n DEFAULT_JOIN_COPY,\n DEFAULT_MEMBERS_COPY,\n} from \"./copy-defaults.js\";\nimport type { ChapterCopy, ChapterCopyInput } from \"./copy.js\";\nimport { DEFAULT_PORTAL_COPY } from \"./copy-portal.js\";\n\n/** Complete default language resolved beneath partial Chapter copy overrides. */\nexport const DEFAULT_CHAPTER_COPY: ChapterCopy = {\n common: DEFAULT_COMMON_COPY,\n join: DEFAULT_JOIN_COPY,\n members: DEFAULT_MEMBERS_COPY,\n portal: DEFAULT_PORTAL_COPY,\n admin: DEFAULT_ADMIN_COPY,\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n\nfunction mergeCopy<T>(base: T, input: unknown, path: string): T {\n if (typeof base === \"string\") {\n if (input === undefined) return base;\n if (typeof input !== \"string\") throw new Error(`${path}: must be a string`);\n return input as T;\n }\n if (Array.isArray(base)) {\n if (input === undefined) return [...base] as T;\n if (!Array.isArray(input) || !input.every((item) => typeof item === \"string\")) {\n throw new Error(`${path}: must be an array of strings`);\n }\n return [...input] as T;\n }\n if (!isObject(base)) return base;\n if (input !== undefined && !isObject(input)) throw new Error(`${path}: must be an object`);\n const overrides = input as Record<string, unknown> | undefined;\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(base)) {\n result[key] = mergeCopy(value, overrides?.[key], `${path}.${key}`);\n }\n return result as T;\n}\n\n/** Resolve a partial copy contract and validate every supplied leaf. */\nexport function resolveChapterCopy(input?: ChapterCopyInput): ChapterCopy {\n return mergeCopy(DEFAULT_CHAPTER_COPY, input, \"defineChapter.copy\");\n}\n\n/** Replace `{name}` placeholders without interpreting the resulting text as HTML. */\nexport function formatChapterCopy(\n template: string,\n values: Readonly<Record<string, string | number>>,\n): string {\n return template.replace(/\\{([a-zA-Z][a-zA-Z0-9]*)\\}/g, (match, key: string) =>\n Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : match,\n );\n}\n","// Brand tokens (H4). defineChapter accepts a `brand` block; this turns it into a\n// `:root { --…: … }` CSS block, so a chapter re-skins the WHOLE UI — the\n// @odla-ai/ui components, the admin shell, and the member islands, which all read\n// --ui-* design tokens — from one config instead of hand-writing inline CSS.\n//\n// Pure string generation, so it is unit-testable and can be emitted at\n// build/SSR time into the page <head> (no flash of unstyled content), or via the\n// <BrandStyle> component from @odla-ai/chapter/ui.\nimport type { ChapterBrand, ChapterBrandTokens } from \"./types\";\n\n// A palette entry is either a direct custom property (already `--…`, e.g.\n// `--ui-accent` to retheme components) or a bare name we expose as `--<name>`\n// (e.g. `moss` → `--moss`, for a site to reference in its own CSS).\nfunction paletteVar(key: string): string {\n return key.startsWith(\"--\") ? key : `--${key}`;\n}\n\n// Strip characters that could break out of a `--var: value;` declaration or the\n// surrounding <style>. Brand config is trusted author input, so this is a\n// belt-and-suspenders guard, not a security boundary.\nfunction cleanValue(value: string): string {\n return value.replace(/[<>{};]/g, \"\").trim();\n}\n\nfunction paletteDecls(palette: Record<string, string> | undefined): string[] {\n const decls: string[] = [];\n for (const [key, value] of Object.entries(palette ?? {})) {\n if (typeof value === \"string\" && value.trim()) decls.push(`${paletteVar(key)}: ${cleanValue(value)};`);\n }\n return decls;\n}\n\nconst TOKEN_VARS: Record<keyof ChapterBrandTokens, string> = {\n background: \"--ui-bg\",\n surface: \"--ui-surface\",\n surface2: \"--ui-surface-2\",\n text: \"--ui-text\",\n textMuted: \"--ui-text-muted\",\n textFaint: \"--ui-text-faint\",\n border: \"--ui-border\",\n borderStrong: \"--ui-border-strong\",\n accent: \"--ui-accent\",\n accentStrong: \"--ui-accent-strong\",\n accentSoft: \"--ui-accent-soft\",\n onAccent: \"--ui-on-accent\",\n good: \"--ui-good\",\n warn: \"--ui-warn\",\n danger: \"--ui-danger\",\n chart1: \"--ui-chart-1\",\n chart2: \"--ui-chart-2\",\n chart3: \"--ui-chart-3\",\n chart4: \"--ui-chart-4\",\n chart5: \"--ui-chart-5\",\n chart6: \"--ui-chart-6\",\n chartPositive: \"--ui-chart-pos\",\n chartNegative: \"--ui-chart-neg\",\n contentWidth: \"--ui-content-width\",\n panelRadius: \"--ui-radius-lg\",\n panelShadow: \"--ui-shadow\",\n masterDetailColumns: \"--ui-master-detail-columns\",\n masterDetailMinHeight: \"--ui-master-detail-min-height\",\n pagePadding: \"--chapter-admin-page-padding\",\n workspacePadding: \"--chapter-admin-workspace-padding\",\n};\n\nfunction semanticDecls(tokens: ChapterBrandTokens | undefined): string[] {\n return Object.entries(tokens ?? {}).flatMap(([key, value]) =>\n typeof value === \"string\" && value.trim()\n ? [`${TOKEN_VARS[key as keyof ChapterBrandTokens]}: ${cleanValue(value)};`]\n : [],\n );\n}\n\n/**\n * Build the CSS that maps a chapter's brand onto the design tokens the UI reads:\n * each `palette` entry becomes a custom property (light, and dark unless\n * `paletteDark` overrides), and `fonts` (display/body/numeral) map to\n * `--ui-font-display` / `--ui-font-sans` / `--ui-font-numeral`. The dark block is\n * emitted under both `:root[data-theme=\"dark\"]` (the odla-ui theme toggle) and\n * `@media (prefers-color-scheme: dark)`. Returns \"\" when there is nothing to\n * theme. These are brand OVERRIDES on top of a base theme — they do not replace\n * the theme layer the components need (see {@link brandTokens} usage in the docs).\n */\nexport function brandTokens(\n brand: ChapterBrand | undefined,\n options: { selector?: string } = {},\n): string {\n if (!brand) return \"\";\n const light = [...paletteDecls(brand.palette), ...semanticDecls(brand.tokens)];\n const fonts = brand.fonts;\n if (fonts?.display) light.push(`--ui-font-display: ${cleanValue(fonts.display)};`);\n if (fonts?.body) light.push(`--ui-font-sans: ${cleanValue(fonts.body)};`);\n if (fonts?.numeral) light.push(`--ui-font-numeral: ${cleanValue(fonts.numeral)};`);\n const dark = [...paletteDecls(brand.paletteDark), ...semanticDecls(brand.tokensDark)];\n const invert = paletteDecls(brand.paletteInvert);\n const selector = options.selector ?? \":root\";\n const darkSelector = selector === \":root\" ? ':root[data-theme=\"dark\"]' : `${selector}[data-theme=\"dark\"]`;\n const systemSelector = selector === \":root\"\n ? ':root:not([data-theme=\"light\"])'\n : `${selector}:not([data-theme=\"light\"])`;\n\n let css = light.length ? `${selector} {\\n ${light.join(\"\\n \")}\\n}\\n` : \"\";\n if (dark.length) {\n const block = `{\\n ${dark.join(\"\\n \")}\\n}`;\n css += `${darkSelector} ${block}\\n@media (prefers-color-scheme: dark) {\\n ${systemSelector} ${block}\\n}\\n`;\n }\n if (invert.length) {\n const invertSelector = selector === \":root\"\n ? \".ui-invert\"\n : `${selector}.ui-invert, ${selector} .ui-invert`;\n css += `${invertSelector} {\\n color-scheme: dark;\\n ${invert.join(\"\\n \")}\\n}\\n`;\n }\n return css;\n}\n\n/** Structural output accepted from `@odla-ai/brand`'s pure token compiler. */\nexport interface CompiledChapterBrandTokens {\n light: Record<string, string>;\n dark: Record<string, string>;\n}\n\n/**\n * Turn a complete compiled token pair into Chapter's brand contract without\n * coupling Chapter's runtime to the brand-book/agent package. Explicit values in\n * `base` win, so a host can make final per-site adjustments after compilation.\n */\nexport function chapterBrandFromTokens(\n compiled: CompiledChapterBrandTokens,\n base: ChapterBrand = {},\n): ChapterBrand {\n return {\n ...base,\n palette: { ...compiled.light, ...base.palette },\n paletteDark: { ...compiled.dark, ...base.paletteDark },\n paletteInvert: { ...compiled.dark, ...base.paletteInvert },\n };\n}\n","// Client-side convenience for brand tokens: render the chapter's brand as a\n// <style> tag. Prefer emitting brandTokens() into the page <head> at build/SSR\n// time (no flash); use this when that isn't available (e.g. a pure SPA mount).\nimport { brandTokens } from \"../brand.js\";\nimport type { ChapterBrand } from \"../types\";\n\n/** Props for {@link BrandStyle}. */\nexport interface BrandStyleProps {\n brand: ChapterBrand | undefined;\n /** CSS selector receiving the variables. Default `:root`. */\n selector?: string;\n}\n\n/** Render a chapter's brand tokens as an inline <style> block (or nothing when\n * there is no brand to theme). */\nexport function BrandStyle(props: BrandStyleProps) {\n const css = brandTokens(props.brand, { selector: props.selector });\n if (!css) return null;\n return <style>{css}</style>;\n}\n"],"mappings":"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","names":["jsx"]}
|