@aglyn/aglyn 1.0.0-beta.219 → 1.0.0-beta.220

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.
Files changed (175) hide show
  1. package/package.json +12 -12
  2. package/src/lib/app-utils/actions.d.ts +1 -59
  3. package/src/lib/app-utils/actions.js +7 -72
  4. package/src/lib/app-utils/actions.js.map +1 -1
  5. package/src/lib/app-utils/consent-groups.d.ts +1 -1
  6. package/src/lib/app-utils/consent-groups.js.map +1 -1
  7. package/src/lib/app-utils/console-routes.d.ts +0 -36
  8. package/src/lib/app-utils/console-routes.js +2 -37
  9. package/src/lib/app-utils/console-routes.js.map +1 -1
  10. package/src/lib/app-utils/contacts.d.ts +1 -1
  11. package/src/lib/app-utils/contacts.js.map +1 -1
  12. package/src/lib/app-utils/container-membership.d.ts +93 -0
  13. package/src/lib/app-utils/container-membership.js +165 -0
  14. package/src/lib/app-utils/container-membership.js.map +1 -0
  15. package/src/lib/app-utils/crm-task-reminders.d.ts +23 -6
  16. package/src/lib/app-utils/crm-task-reminders.js +32 -11
  17. package/src/lib/app-utils/crm-task-reminders.js.map +1 -1
  18. package/src/lib/app-utils/crm.d.ts +24 -4
  19. package/src/lib/app-utils/crm.js +23 -0
  20. package/src/lib/app-utils/crm.js.map +1 -1
  21. package/src/lib/app-utils/csv-upload.d.ts +1 -1
  22. package/src/lib/app-utils/csv-upload.js +1 -1
  23. package/src/lib/app-utils/csv-upload.js.map +1 -1
  24. package/src/lib/app-utils/{dataset-csv.d.ts → csv.d.ts} +10 -43
  25. package/src/lib/app-utils/{dataset-csv.js → csv.js} +10 -81
  26. package/src/lib/app-utils/csv.js.map +1 -0
  27. package/src/lib/app-utils/datasets.d.ts +0 -19
  28. package/src/lib/app-utils/datasets.js +0 -13
  29. package/src/lib/app-utils/datasets.js.map +1 -1
  30. package/src/lib/app-utils/docs-help.generated.d.ts +2 -2
  31. package/src/lib/app-utils/docs-help.generated.js +2 -0
  32. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  33. package/src/lib/app-utils/docs-index.generated.js +63 -35
  34. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  35. package/src/lib/app-utils/dynamic-list-rule.js +3 -3
  36. package/src/lib/app-utils/dynamic-list-rule.js.map +1 -1
  37. package/src/lib/app-utils/email-text.d.ts +80 -0
  38. package/src/lib/app-utils/email-text.js +189 -0
  39. package/src/lib/app-utils/email-text.js.map +1 -0
  40. package/src/lib/app-utils/{list-assignment-policy.d.ts → enrollment-basis.d.ts} +4 -0
  41. package/src/lib/app-utils/{list-assignment-policy.js → enrollment-basis.js} +5 -1
  42. package/src/lib/app-utils/enrollment-basis.js.map +1 -0
  43. package/src/lib/app-utils/expand-repeatables.js +4 -4
  44. package/src/lib/app-utils/expand-repeatables.js.map +1 -1
  45. package/src/lib/app-utils/forms.js +2 -2
  46. package/src/lib/app-utils/forms.js.map +1 -1
  47. package/src/lib/app-utils/health-report.d.ts +2 -3
  48. package/src/lib/app-utils/health-report.js +8 -6
  49. package/src/lib/app-utils/health-report.js.map +1 -1
  50. package/src/lib/app-utils/inbound-mail-domain.d.ts +27 -0
  51. package/src/lib/app-utils/inbound-mail-domain.js +31 -0
  52. package/src/lib/app-utils/inbound-mail-domain.js.map +1 -0
  53. package/src/lib/app-utils/lockdown.d.ts +11 -0
  54. package/src/lib/app-utils/lockdown.js +33 -3
  55. package/src/lib/app-utils/lockdown.js.map +1 -1
  56. package/src/lib/app-utils/member-email-aliases.d.ts +25 -0
  57. package/src/lib/app-utils/member-email-aliases.js +29 -0
  58. package/src/lib/app-utils/member-email-aliases.js.map +1 -1
  59. package/src/lib/app-utils/notifications.d.ts +69 -0
  60. package/src/lib/app-utils/notifications.js +119 -0
  61. package/src/lib/app-utils/notifications.js.map +1 -1
  62. package/src/lib/app-utils/operator-alerts.d.ts +15 -1
  63. package/src/lib/app-utils/operator-alerts.js +47 -10
  64. package/src/lib/app-utils/operator-alerts.js.map +1 -1
  65. package/src/lib/app-utils/plan-entitlements.d.ts +164 -178
  66. package/src/lib/app-utils/plan-entitlements.js +200 -224
  67. package/src/lib/app-utils/plan-entitlements.js.map +1 -1
  68. package/src/lib/app-utils/platform-marketing-consent.d.ts +56 -2
  69. package/src/lib/app-utils/platform-marketing-consent.js +6 -5
  70. package/src/lib/app-utils/platform-marketing-consent.js.map +1 -1
  71. package/src/lib/app-utils/plugin-starter-templates.generated.d.ts +12 -0
  72. package/src/lib/app-utils/plugin-starter-templates.generated.js +711 -0
  73. package/src/lib/app-utils/plugin-starter-templates.generated.js.map +1 -0
  74. package/src/lib/app-utils/repeat-query.d.ts +56 -0
  75. package/src/lib/app-utils/{dataset-query.js → repeat-query.js} +16 -16
  76. package/src/lib/app-utils/repeat-query.js.map +1 -0
  77. package/src/lib/app-utils/server.d.ts +7 -9
  78. package/src/lib/app-utils/server.js +18 -26
  79. package/src/lib/app-utils/server.js.map +1 -1
  80. package/src/lib/app-utils/starter-template-nodes.d.ts +137 -0
  81. package/src/lib/app-utils/starter-template-nodes.js +129 -0
  82. package/src/lib/app-utils/starter-template-nodes.js.map +1 -0
  83. package/src/lib/app-utils/starter-templates.d.ts +11 -34
  84. package/src/lib/app-utils/starter-templates.js +19 -230
  85. package/src/lib/app-utils/starter-templates.js.map +1 -1
  86. package/src/lib/app-utils/{email-topics.d.ts → subscription-topics.d.ts} +64 -48
  87. package/src/lib/app-utils/{email-topics.js → subscription-topics.js} +39 -109
  88. package/src/lib/app-utils/subscription-topics.js.map +1 -0
  89. package/src/lib/app-utils/usage-budget.d.ts +73 -188
  90. package/src/lib/app-utils/usage-budget.js +58 -194
  91. package/src/lib/app-utils/usage-budget.js.map +1 -1
  92. package/src/lib/app-utils/utm-attribution.d.ts +1 -1
  93. package/src/lib/app-utils/utm-attribution.js +1 -1
  94. package/src/lib/app-utils/utm-attribution.js.map +1 -1
  95. package/src/lib/app-utils/{campaign-touch.d.ts → utm-touch.d.ts} +21 -13
  96. package/src/lib/app-utils/{campaign-touch.js → utm-touch.js} +30 -22
  97. package/src/lib/app-utils/utm-touch.js.map +1 -0
  98. package/src/lib/app-utils/variables.d.ts +9 -9
  99. package/src/lib/app-utils/variables.js.map +1 -1
  100. package/src/lib/app-utils/visitor-record-ceiling.d.ts +32 -0
  101. package/src/lib/app-utils/visitor-record-ceiling.js +39 -0
  102. package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -1
  103. package/src/lib/foundation/definitions/org-billing.types.d.ts +2 -2
  104. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
  105. package/src/lib/plugin-manager/feature-plugins.d.ts +80 -79
  106. package/src/lib/plugin-manager/feature-plugins.js +22 -35
  107. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  108. package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +12 -1
  109. package/src/lib/plugin-manager/first-party-plugins.generated.js +58 -2
  110. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  111. package/src/lib/plugin-manager/index.d.ts +0 -1
  112. package/src/lib/plugin-manager/index.js +0 -1
  113. package/src/lib/plugin-manager/index.js.map +1 -1
  114. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +9 -4
  115. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
  116. package/src/lib/plugin-manager/plugin-containers.d.ts +68 -0
  117. package/src/lib/plugin-manager/plugin-containers.generated.d.ts +10 -0
  118. package/src/lib/plugin-manager/plugin-containers.generated.js +20 -0
  119. package/src/lib/plugin-manager/plugin-containers.generated.js.map +1 -0
  120. package/src/lib/plugin-manager/plugin-containers.js +27 -0
  121. package/src/lib/plugin-manager/plugin-containers.js.map +1 -0
  122. package/src/lib/plugin-manager/plugin-entitlements.d.ts +8 -0
  123. package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -1
  124. package/src/lib/plugin-manager/plugin-membership-detach.js +1 -1
  125. package/src/lib/plugin-manager/plugin-membership-detach.js.map +1 -1
  126. package/src/lib/plugin-manager/plugin-record-email-state.d.ts +69 -0
  127. package/src/lib/plugin-manager/plugin-record-email-state.js +32 -0
  128. package/src/lib/plugin-manager/plugin-record-email-state.js.map +1 -1
  129. package/src/lib/plugin-manager/plugin-record-lists.d.ts +115 -0
  130. package/src/lib/plugin-manager/plugin-record-lists.js +75 -0
  131. package/src/lib/plugin-manager/plugin-record-lists.js.map +1 -0
  132. package/src/lib/plugin-manager/plugin-record-timeline.d.ts +42 -8
  133. package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -1
  134. package/src/lib/plugin-manager/plugin-site-beacons.d.ts +71 -0
  135. package/src/lib/plugin-manager/plugin-site-beacons.js +54 -0
  136. package/src/lib/plugin-manager/plugin-site-beacons.js.map +1 -0
  137. package/src/lib/plugin-manager/plugin-subscription-topics.generated.d.ts +11 -0
  138. package/src/lib/plugin-manager/plugin-subscription-topics.generated.js +41 -0
  139. package/src/lib/plugin-manager/plugin-subscription-topics.generated.js.map +1 -0
  140. package/src/lib/plugin-manager/plugin-usage-axes.d.ts +212 -0
  141. package/src/lib/plugin-manager/plugin-usage-axes.js +82 -3
  142. package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -1
  143. package/src/lib/plugin-manager/plugin-usage-meters.d.ts +108 -0
  144. package/src/lib/plugin-manager/plugin-usage-meters.js +105 -0
  145. package/src/lib/plugin-manager/plugin-usage-meters.js.map +1 -0
  146. package/src/lib/plugin-manager/usage-alert-contributors.d.ts +76 -6
  147. package/src/lib/plugin-manager/usage-alert-contributors.js +23 -10
  148. package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -1
  149. package/src/lib/app-utils/assist-credits.d.ts +0 -482
  150. package/src/lib/app-utils/assist-credits.js +0 -510
  151. package/src/lib/app-utils/assist-credits.js.map +0 -1
  152. package/src/lib/app-utils/campaign-membership.d.ts +0 -121
  153. package/src/lib/app-utils/campaign-membership.js +0 -189
  154. package/src/lib/app-utils/campaign-membership.js.map +0 -1
  155. package/src/lib/app-utils/campaign-touch.js.map +0 -1
  156. package/src/lib/app-utils/console-record-links.d.ts +0 -84
  157. package/src/lib/app-utils/console-record-links.js +0 -76
  158. package/src/lib/app-utils/console-record-links.js.map +0 -1
  159. package/src/lib/app-utils/crm-booking.d.ts +0 -75
  160. package/src/lib/app-utils/crm-booking.js +0 -133
  161. package/src/lib/app-utils/crm-booking.js.map +0 -1
  162. package/src/lib/app-utils/crm-inbound.d.ts +0 -245
  163. package/src/lib/app-utils/crm-inbound.js +0 -407
  164. package/src/lib/app-utils/crm-inbound.js.map +0 -1
  165. package/src/lib/app-utils/crm-reports.d.ts +0 -479
  166. package/src/lib/app-utils/crm-reports.js +0 -680
  167. package/src/lib/app-utils/crm-reports.js.map +0 -1
  168. package/src/lib/app-utils/dataset-csv.js.map +0 -1
  169. package/src/lib/app-utils/dataset-query.d.ts +0 -57
  170. package/src/lib/app-utils/dataset-query.js.map +0 -1
  171. package/src/lib/app-utils/email-topics.js.map +0 -1
  172. package/src/lib/app-utils/list-assignment-policy.js.map +0 -1
  173. package/src/lib/plugin-manager/plugin-metered-lines.d.ts +0 -97
  174. package/src/lib/plugin-manager/plugin-metered-lines.js +0 -123
  175. package/src/lib/plugin-manager/plugin-metered-lines.js.map +0 -1
@@ -223,7 +223,7 @@ export const PLAN_ENTITLEMENTS = {
223
223
  scheduledPublishing: false,
224
224
  // The two AI flags point opposite ways on Free, on purpose (AGL-2925).
225
225
  // `aiAssist` is the guided rung of the console assistant — page-aware
226
- // level-2 answers and the copy assistant — and stays Pro and up; Free
226
+ // level-2 answers and the copy assistant — and starts at Starter; Free
227
227
  // keeps the docs-grounded level-1 chat it always had. `aiGenerative`
228
228
  // is the generative door the taste is FOR: a Free workspace may
229
229
  // generate against its 300-credit band, behind the wall and every
@@ -294,7 +294,8 @@ export const PLAN_ENTITLEMENTS = {
294
294
  managersPerOrg: 2,
295
295
  maxManagersPerOrg: 5,
296
296
  maxMembersPerHost: 10,
297
- bandwidthGb: 50,
297
+ // Sized by the same annual-price invariant as Pro's — see that band.
298
+ bandwidthGb: 20,
298
299
  formsPerHost: FORMS_PER_HOST_CEILING,
299
300
  variablesPerHost: 25,
300
301
  functionsPerHost: 10,
@@ -398,20 +399,26 @@ export const PLAN_ENTITLEMENTS = {
398
399
  managersPerOrg: 5,
399
400
  maxManagersPerOrg: 20,
400
401
  maxMembersPerHost: 25,
401
- // Page views are the largest term of this tier's modeled COGS — one GB
402
- // is 1,035 views at `ESTIMATED_PAGE_TRANSFER_BYTES`, or $0.167 of
403
- // measured cost — so the bandwidth band is what decides whether the tier
404
- // survives a customer spending the whole allowance it was sold.
402
+ // Page views are the largest term of this tier's modeled COGS. One GB is
403
+ // 1,035 views at `ESTIMATED_PAGE_TRANSFER_BYTES`, priced where the CDN is
404
+ // dearest because it bills in the region that serves a visitor: $0.367 of
405
+ // weight (`perPageView`, transfer at $0.35 a GB) and $0.190 of CDN
406
+ // requests once the hosting plan's monthly allowance is spent
407
+ // (`PAGE_VIEW_CDN_REQUEST_COST_USD`, $3.20 per million). So a GB of
408
+ // included bandwidth costs $0.557, and the bandwidth band is what decides
409
+ // whether the tier survives a customer spending the whole allowance it
410
+ // was sold.
405
411
  //
406
412
  // The invariant the band is sized against is the ANNUAL price ($39 a
407
413
  // month) net of Stripe's fee, with every band at 100% and the CRM seat
408
- // and one-to-one email terms counted — the 2026-09-07 pricing decision.
409
- // At 225 GB that price was 44% under water; 125 GB is what it carries.
414
+ // and one-to-one email terms counted — the 2026-09-07 pricing decision —
415
+ // at that all-in gigabyte (AGL-3444). 35 GB is what it carries; every
416
+ // paid band on the ladder is sized the same way.
410
417
  // `tier-margin-floor.spec.ts` holds the model and pins the figure.
411
418
  //
412
419
  // `meteredInfraPassThrough` is true here, so traffic past the band BILLS
413
420
  // at the page-view pass-through rather than being refused or absorbed.
414
- bandwidthGb: 125,
421
+ bandwidthGb: 35,
415
422
  formsPerHost: FORMS_PER_HOST_CEILING,
416
423
  variablesPerHost: 100,
417
424
  functionsPerHost: 50,
@@ -503,7 +510,7 @@ export const PLAN_ENTITLEMENTS = {
503
510
  maxManagersPerOrg: 100,
504
511
  maxMembersPerHost: 100,
505
512
  // Sized by the same annual-price invariant as Pro's — see that band.
506
- bandwidthGb: 185,
513
+ bandwidthGb: 55,
507
514
  formsPerHost: FORMS_PER_HOST_CEILING,
508
515
  variablesPerHost: 1000,
509
516
  functionsPerHost: 250,
@@ -574,7 +581,8 @@ export const PLAN_ENTITLEMENTS = {
574
581
  managersPerOrg: 25,
575
582
  maxManagersPerOrg: 150,
576
583
  maxMembersPerHost: 150,
577
- bandwidthGb: 290,
584
+ // Sized by the same annual-price invariant as Pro's — see that band.
585
+ bandwidthGb: 90,
578
586
  formsPerHost: FORMS_PER_HOST_CEILING,
579
587
  variablesPerHost: 5000,
580
588
  functionsPerHost: 500,
@@ -642,7 +650,8 @@ export const PLAN_ENTITLEMENTS = {
642
650
  managersPerOrg: 50,
643
651
  maxManagersPerOrg: 250,
644
652
  maxMembersPerHost: 250,
645
- bandwidthGb: 345,
653
+ // Sized by the same annual-price invariant as Pro's — see that band.
654
+ bandwidthGb: 105,
646
655
  formsPerHost: FORMS_PER_HOST_CEILING,
647
656
  variablesPerHost: UNLIMITED,
648
657
  functionsPerHost: 1000,
@@ -714,7 +723,8 @@ export const PLAN_ENTITLEMENTS = {
714
723
  managersPerOrg: 100,
715
724
  maxManagersPerOrg: 500,
716
725
  maxMembersPerHost: 1000,
717
- bandwidthGb: 1540,
726
+ // Sized by the same annual-price invariant as Pro's — see that band.
727
+ bandwidthGb: 485,
718
728
  formsPerHost: FORMS_PER_HOST_CEILING,
719
729
  variablesPerHost: UNLIMITED,
720
730
  functionsPerHost: UNLIMITED,
@@ -823,7 +833,7 @@ export const PLAN_ENTITLEMENTS = {
823
833
  managersPerOrg: 200,
824
834
  maxManagersPerOrg: 1000,
825
835
  maxMembersPerHost: 2000,
826
- bandwidthGb: 3080,
836
+ bandwidthGb: 970,
827
837
  formsPerHost: FORMS_PER_HOST_CEILING,
828
838
  variablesPerHost: UNLIMITED,
829
839
  functionsPerHost: UNLIMITED,
@@ -1758,6 +1768,74 @@ export const STRIPE_PROCESSOR_FEE_FIXED_USD = 0.3;
1758
1768
  *
1759
1769
  * The console keeps importing it from `usage-metering` unchanged.
1760
1770
  */ export const METERED_MARKUP = 1.3;
1771
+ /**
1772
+ * What the CDN requests behind ONE page view cost once the platform is past
1773
+ * its hosting plan's included request allowance, at the DEAREST region the
1774
+ * CDN bills, in USD per view (AGL-1879, AGL-3444). A page view billed past a
1775
+ * plan's band is priced at `(perPageView + this) × METERED_MARKUP` — $0.70
1776
+ * per 1,000 — and every `bandwidthGb` band is sized with it counted.
1777
+ *
1778
+ * ## Why a page view needs a second cost term
1779
+ *
1780
+ * `perPageView` prices what a view WEIGHS: the bytes it transfers at the
1781
+ * CDN's per-GB rate, plus the Firestore reads behind the render. The CDN also
1782
+ * charges per REQUEST once the team's monthly allowance is spent — Vercel Pro
1783
+ * includes 10,000,000 a month, then bills $2.00 to $3.20 per million by
1784
+ * region. A cold load of `aglyn.com/` makes 57 first-party requests to settle
1785
+ * (`tools/tenant-page-budget.json`, `wireCalibration.measuredAgainst`), so the
1786
+ * allowance is about 175,000 views of that page a month, platform-wide —
1787
+ * about 169 GB of included bandwidth — where the 1 TB transfer allowance
1788
+ * lasts about 1.4 million. The request allowance runs out first.
1789
+ *
1790
+ * ## Why the dearest region
1791
+ *
1792
+ * The CDN bills a request, and the bytes it transfers, in the region that
1793
+ * SERVES it — the one nearest the visitor — so a site whose audience is in
1794
+ * São Paulo or Cape Town costs what those regions cost, and nothing about a
1795
+ * plan can choose otherwise. The platform's rule is that no plan is under
1796
+ * water at any utilization and that "at cost + 30%" is true of every billed
1797
+ * unit, so both terms of a page view are priced where the CDN is dearest:
1798
+ * transfer at $0.35 per GB (in `perPageView`) and requests at $3.20 per
1799
+ * million (here). Past the allowance a view then costs:
1800
+ *
1801
+ * | | weight | requests | cost / 1k | margin at $0.70 |
1802
+ * |-----------------------------------|------------|-----------|-----------|-----------------|
1803
+ * | the page as measured (748.5 KB) | $0.262148 | $0.182400 | $0.4445 | +36.5% |
1804
+ * | the priced basis (1012.8 KB) | $0.354715 | $0.182400 | $0.5371 | +23.3% |
1805
+ *
1806
+ * — at least 30% over cost in every region, which is the claim `/pricing`
1807
+ * makes.
1808
+ *
1809
+ * ## The figure
1810
+ *
1811
+ * $0.00018374681 is 57.42 requests at $3.20 per million — the measured 57,
1812
+ * with the basis set ABOVE the measurement as the weight basis is — pinned so
1813
+ * the billed price lands on a whole cent: ($0.354715 + $0.1824) × 1.3 is
1814
+ * $0.6982 per 1,000, rounded up to $0.70, and ($0.354715 + $0.183747) × 1.3
1815
+ * is exactly $0.70. Published as "Our cost $0.538462 / You pay $0.70", the
1816
+ * "at cost + 30%" claim stays true of the cost a billed view incurs in the
1817
+ * dearest region, and so in every other.
1818
+ *
1819
+ * ## ⛔ Not in {@link ORG_COGS_UNIT_RATES_USD}, deliberately
1820
+ *
1821
+ * `perPageView` is half of a pair with `ESTIMATED_PAGE_TRANSFER_BYTES` — one
1822
+ * page weight in dollars and in bytes — and `npm run check:page-view-rate`
1823
+ * recovers that weight from the rate. A per-request charge is not
1824
+ * proportional to weight, so folding it into `perPageView` would make the
1825
+ * gate read a page nobody serves.
1826
+ *
1827
+ * ## The INCLUDED bands carry it too
1828
+ *
1829
+ * A view inside a plan's band makes the same requests as one past it, and
1830
+ * once the platform is past the allowance the CDN bills them either way, so
1831
+ * the `bandwidthGb` bands are sized with this term counted: $0.190 per
1832
+ * included GB on top of $0.367 of weight. `tier-margin-floor.spec.ts`
1833
+ * multiplies it in and pins every tier at 100% of every band.
1834
+ *
1835
+ * Lives HERE, beside the markup, for the reason the markup does: the
1836
+ * `/pricing` generator publishes the cost column from it and cannot import
1837
+ * anything under `apps/console`.
1838
+ */ export const PAGE_VIEW_CDN_REQUEST_COST_USD = 0.00018374681;
1761
1839
  /**
1762
1840
  * Monthly unit costs for the meters the rollup already records (AGL-1134).
1763
1841
  *
@@ -1781,6 +1859,23 @@ export const STRIPE_PROCESSOR_FEE_FIXED_USD = 0.3;
1781
1859
  * infrastructure pass-through, and the customer is charged those figures
1782
1860
  * times `METERED_MARKUP`. They therefore appear in `METERED_UNIT_RATES_USD`
1783
1861
  * as well, with identical values, and must be changed in both places at once.
1862
+ * A page view billed past its band also carries
1863
+ * {@link PAGE_VIEW_CDN_REQUEST_COST_USD}, which neither table holds: it is
1864
+ * the per-request CDN charge past the hosting plan's allowance, and this
1865
+ * table's `perPageView` is the weight-proportional half of what a band is
1866
+ * sized against — the request term is the other.
1867
+ *
1868
+ * ## Every Vercel-billed input is priced at Vercel's dearest region
1869
+ *
1870
+ * The CDN and functions bill by region, from $0.15 to $0.35 per GB of
1871
+ * transfer, $2.00 to $3.20 per million requests, and $0.128 to $0.221 per
1872
+ * hour of active CPU (memory $0.0106 to $0.0183 per GB-hour), and a plan
1873
+ * cannot choose where its visitors are. So the Vercel share of every rate
1874
+ * here is priced at the dearest region (AGL-3444): the transfer inside
1875
+ * `perPageView`, the function invocation inside `perFormSubmission`, and the
1876
+ * compute inside `perRun`. The GCP shares — object and Firestore storage, and
1877
+ * the Firestore reads and writes behind a render, a submission or a run —
1878
+ * are priced as they were.
1784
1879
  *
1785
1880
  * Everything else here is a cost-model input ONLY. `dataStoragePerGbMonth`,
1786
1881
  * `perApiRequest`, `perContactMonth` and `perEmailSend` price what an org
@@ -1790,9 +1885,11 @@ export const STRIPE_PROCESSOR_FEE_FIXED_USD = 0.3;
1790
1885
  * "at cost + 30%" claim, which is true of exactly three meters.
1791
1886
  *
1792
1887
  * The first three were corrected 2026-08-09 (AGL-1280) — storage $0.03 →
1793
- * $0.026 and form submissions $0.0005 → $0.00005 — and `perPageView` was
1794
- * re-pegged 2026-09-09 (AGL-2711), from $0.0001 to $0.00016153846. All three
1795
- * MUST be changed here and in `METERED_UNIT_RATES_USD` together. See that
1888
+ * $0.026 and form submissions $0.0005 → $0.00005 — `perPageView` was
1889
+ * re-pegged 2026-09-09 (AGL-2711), from $0.0001 to $0.00016153846, and the
1890
+ * last two were re-priced at Vercel's dearest region on 2026-10-01
1891
+ * (AGL-3444): page views $0.00035471473, form submissions $0.000053846154.
1892
+ * All three MUST be changed here and in `METERED_UNIT_RATES_USD` together. See that
1796
1893
  * table for each figure's basis; it is the one a customer is billed against,
1797
1894
  * so it is the one that carries the working.
1798
1895
  *
@@ -1812,10 +1909,10 @@ export const STRIPE_PROCESSOR_FEE_FIXED_USD = 0.3;
1812
1909
  * ⛔ HALF OF A PAIR — see ESTIMATED_PAGE_TRANSFER_BYTES, which states the
1813
1910
  * same page in bytes as this states it in dollars. Both are calibrated
1814
1911
  * against a 1012.8 KB page and neither may move alone: their quotient is
1815
- * the cost of a gigabyte, and that is what every bandwidthGb band on the
1816
- * ladder was sized against.
1817
- */ perPageView: 0.00016153846,
1818
- perFormSubmission: 0.00005,
1912
+ * the cost of a gigabyte's weight, and that — with the request term — is
1913
+ * what every bandwidthGb band on the ladder is sized against.
1914
+ */ perPageView: 0.00035471473,
1915
+ perFormSubmission: 0.000053846154,
1819
1916
  /** Firestore-backed dataset bytes — an order pricier than object storage. */ dataStoragePerGbMonth: 0.18,
1820
1917
  perApiRequest: 0.000002,
1821
1918
  perContactMonth: 0.0002,
@@ -1834,7 +1931,7 @@ export const STRIPE_PROCESSOR_FEE_FIXED_USD = 0.3;
1834
1931
  * both concern a smaller quantity; this is the whole bill.
1835
1932
  */ perEmailSend: 0.0009,
1836
1933
  /**
1837
- * One workflow or action run — $12 per million.
1934
+ * One workflow or action run — $13 per million.
1838
1935
  *
1839
1936
  * Derived from the run paths at list prices (2026-09-07), because nothing
1840
1937
  * measurable exists yet: runs execute inside the Vercel request that fired
@@ -1853,9 +1950,11 @@ export const STRIPE_PROCESSOR_FEE_FIXED_USD = 0.3;
1853
1950
  * - writes: the activity row and the counter increment — 2, at $0.18 per
1854
1951
  * 100,000 = $0.0000036
1855
1952
  * - compute: a few milliseconds of active CPU and memory on Fluid compute
1856
- * for the expression evaluation, about $0.000001
1953
+ * for the expression evaluation, about $0.000001 in the cheapest
1954
+ * region and $0.0000017 in the dearest ($0.221 against $0.128 an
1955
+ * hour of active CPU), which is where it is priced
1857
1956
  *
1858
- * $0.0000118, carried as $0.000012. It scales with the catalog a site holds
1957
+ * $0.0000125, carried as $0.000013. It scales with the catalog a site holds
1859
1958
  * — every `limit(100)` read above is a query that costs one read per
1860
1959
  * document returned — and a step that writes a dataset record or calls a
1861
1960
  * webhook adds to it, so this is the typical figure, not the ceiling.
@@ -1864,7 +1963,7 @@ export const STRIPE_PROCESSOR_FEE_FIXED_USD = 0.3;
1864
1963
  * band on a metered plan. It exists so a tier's run bands reach
1865
1964
  * `orgMonthlyCogsUsd` and `tier-margin-floor.spec.ts` — Agency's 3,000,000
1866
1965
  * runs are $36 a month that read as nothing while this was absent.
1867
- */ perRun: 0.000012
1966
+ */ perRun: 0.000013
1868
1967
  };
1869
1968
  /**
1870
1969
  * The unit rate a declared cost axis names, or 1 where it names none (the
@@ -2773,10 +2872,10 @@ function resolvePlan(org) {
2773
2872
  return 0;
2774
2873
  }
2775
2874
  /**
2776
- * Stripe's processing cost on ONE storefront destination charge, in cents
2777
- * (AGL-2152) — the dearest enabled method's percentage plus the fixed 30¢,
2778
- * rounded UP on the percentage so the answer is never a cent short of what
2779
- * Stripe actually debits.
2875
+ * Stripe's processing cost on ONE destination-charge sale, in cents
2876
+ * (AGL-2152) — a storefront order, a booking deposit — the dearest enabled
2877
+ * method's percentage plus the fixed 30¢, rounded UP on the percentage so the
2878
+ * answer is never a cent short of what Stripe actually debits.
2780
2879
  *
2781
2880
  * WHO PAYS THIS. Every storefront charge is a DESTINATION charge:
2782
2881
  * `payment_intent_data[transfer_data][destination]` names the merchant's
@@ -2802,23 +2901,23 @@ function resolvePlan(org) {
2802
2901
  * decides the set — and per AGL-2343 that configuration has `klarna` and
2803
2902
  * `affirm` on beside the card family. Pricing this at the card rate would be
2804
2903
  * wrong exactly on the orders where it cost the most, which is the same
2805
- * reasoning `marketplaceBreakEvenUsd` already applies to the listing floor;
2806
- * these are deliberately the SAME two constants so the two cannot drift.
2904
+ * reasoning the marketplace listing floor applies; both are defined from the
2905
+ * SAME processing constants so the two cannot drift.
2807
2906
  *
2808
2907
  * THE ONE-LINE LEVER. On a card-family order this over-recovers by the spread
2809
2908
  * between the two rates. Pinning `payment_method_types` on the storefront
2810
2909
  * sessions to the card family would make 2.9% the true binding rate, and
2811
- * `STOREFRONT_PROCESSING_PERCENT` below is then the single identifier to
2910
+ * `SALE_PROCESSING_PERCENT` below is then the single identifier to
2812
2911
  * repoint. That is a product decision about which payment methods a storefront
2813
2912
  * offers, not this one; it is recorded on AGL-2152 and in the Pricing Decision
2814
2913
  * Log so it can be taken deliberately.
2815
- */ // `STOREFRONT_PROCESSING_PERCENT` and `STOREFRONT_PROCESSING_FIXED_CENTS` are
2816
- // declared beside the marketplace processing constants they are defined from,
2817
- // further down this file — a `const` cannot be read before its declaration is
2818
- // evaluated, and this function is only ever CALLED, never evaluated at import.
2819
- export function storefrontProcessingCostCents(chargeCents) {
2914
+ */ // `SALE_PROCESSING_PERCENT` and `SALE_PROCESSING_FIXED_CENTS` are declared
2915
+ // beside the processing constants they are defined from, further down this
2916
+ // file — a `const` cannot be read before its declaration is evaluated, and
2917
+ // this function is only ever CALLED, never evaluated at import.
2918
+ export function saleProcessingCostCents(chargeCents) {
2820
2919
  if (!Number.isFinite(chargeCents) || chargeCents <= 0) return 0;
2821
- return Math.ceil(chargeCents * STOREFRONT_PROCESSING_PERCENT / 100) + STOREFRONT_PROCESSING_FIXED_CENTS;
2920
+ return Math.ceil(chargeCents * SALE_PROCESSING_PERCENT / 100) + SALE_PROCESSING_FIXED_CENTS;
2822
2921
  }
2823
2922
  /**
2824
2923
  * The Stripe Connect `application_fee_amount` for ONE storefront sale
@@ -2852,7 +2951,7 @@ export function storefrontProcessingCostCents(chargeCents) {
2852
2951
  // The `Math.max(1, …)` every door already applied: a rate above zero on
2853
2952
  // goods the shopper pays for is a fee however small it rounds to.
2854
2953
  const take = pct > 0 && base > 0 ? Math.max(1, Math.round(base * pct / 100)) : 0;
2855
- return Math.min(charge, take + storefrontProcessingCostCents(charge));
2954
+ return Math.min(charge, take + saleProcessingCostCents(charge));
2856
2955
  }
2857
2956
  /**
2858
2957
  * Stripe's processing cost on a RECURRING charge of `amountCents`, as the
@@ -2860,7 +2959,7 @@ export function storefrontProcessingCostCents(chargeCents) {
2860
2959
  *
2861
2960
  * A Stripe Subscription accepts no `application_fee_amount` — only
2862
2961
  * `application_fee_percent`, a rate with at most two decimal places — so the
2863
- * fixed 30¢ that `storefrontProcessingCostCents` adds in cents has to be
2962
+ * fixed 30¢ that `saleProcessingCostCents` adds in cents has to be
2864
2963
  * folded into the rate. `(rate × amount + fixed) ÷ amount` is, with the
2865
2964
  * amount cancelled,
2866
2965
  *
@@ -2877,15 +2976,15 @@ export function storefrontProcessingCostCents(chargeCents) {
2877
2976
  * decimal outright, so the only honest direction is over. Integers, because
2878
2977
  * `0.09 * 100` is not `9` in floating point and a percent one ulp above a
2879
2978
  * whole hundredth would round up to the next one. The same two constants as
2880
- * the one-time path, so repointing `STOREFRONT_PROCESSING_PERCENT` moves
2979
+ * the one-time path, so repointing `SALE_PROCESSING_PERCENT` moves
2881
2980
  * both surfaces together. Zero for a charge that is not a charge.
2882
- */ export function storefrontProcessingPassThroughPercent(amountCents) {
2981
+ */ export function saleProcessingPassThroughPercent(amountCents) {
2883
2982
  const amount = Number.isFinite(amountCents) && amountCents > 0 ? Math.round(amountCents) : 0;
2884
2983
  if (amount <= 0) return 0;
2885
- const rateHundredths = Math.round(STOREFRONT_PROCESSING_PERCENT * 100);
2984
+ const rateHundredths = Math.round(SALE_PROCESSING_PERCENT * 100);
2886
2985
  // 30¢ ÷ amount is a fraction; × 100 makes it a percent, × 100 again makes
2887
2986
  // it hundredths of one.
2888
- const fixedHundredths = STOREFRONT_PROCESSING_FIXED_CENTS * 10000 / amount;
2987
+ const fixedHundredths = SALE_PROCESSING_FIXED_CENTS * 10000 / amount;
2889
2988
  return Math.min(100, Math.ceil(rateHundredths + fixedHundredths) / 100);
2890
2989
  }
2891
2990
  /**
@@ -2910,7 +3009,7 @@ export function storefrontProcessingCostCents(chargeCents) {
2910
3009
  const amount = Number.isFinite(amountCents) && amountCents > 0 ? Math.round(amountCents) : 0;
2911
3010
  if (amount <= 0) return 0;
2912
3011
  const take = resolveTransactionFeePct(org, productType);
2913
- const passThrough = storefrontProcessingPassThroughPercent(amount);
3012
+ const passThrough = saleProcessingPassThroughPercent(amount);
2914
3013
  return Math.min(100, (Math.round(take * 100) + Math.round(passThrough * 100)) / 100);
2915
3014
  }
2916
3015
  /**
@@ -2960,180 +3059,40 @@ export function storefrontProcessingCostCents(chargeCents) {
2960
3059
  }).filter((pct)=>Number.isFinite(pct) && pct > 0));
2961
3060
  }
2962
3061
  /**
2963
- * What one marketplace sale costs the PLATFORM to process (AGL-2343).
3062
+ * What one destination-charge sale costs the PLATFORM to process (AGL-2343):
3063
+ * a storefront order, a booking deposit, a marketplace listing. On a
3064
+ * destination charge Stripe debits its fee from the PLATFORM's balance, so
3065
+ * every surface that sells through the platform's account prices its take
3066
+ * against these.
2964
3067
  *
2965
- * NONE OF THESE IS AN AGLYN PRICE. They are Stripe's published US rates and
2966
- * the Texas rate the platform remits, and they exist only as inputs to a
2967
- * figure shown to a publisher while they type. Nothing is billed from them —
2968
- * the charged amounts are `resolveMarketplaceFeePct` and Stripe's own
2969
- * invoicing — so changing one changes a sentence, never a bill.
2970
- *
2971
- * WHY THE PLATFORM PAYS THE PROCESSING FEE AT ALL. Marketplace checkout is a
2972
- * DESTINATION charge with a fixed `transfer_data[amount]` and deliberately no
2973
- * `application_fee_amount`, so that the sales tax stays with the platform that
2974
- * owes it (AGL-1544). On a destination charge Stripe debits its fee from the
2975
- * PLATFORM's balance, and it is charged on `amount_total` — the listing price
2976
- * plus the tax added on top of it.
2977
- *
2978
- * So a $1 listing at a 20% take rate is a loss: the buyer pays $1.08, the
2979
- * seller is transferred $0.80, $0.08 is owed to the state, Aglyn keeps $0.20
2980
- * and pays Stripe about $0.33. Net −$0.13.
3068
+ * NONE OF THESE IS AN AGLYN PRICE. They are Stripe's published US rates, and
3069
+ * they exist only as inputs to a platform take and to figures a seller reads;
3070
+ * changing one never changes a price list.
2981
3071
  *
2982
3072
  * THE BNPL RATE IS THE ONE THAT BINDS, and it is not hypothetical: the live
2983
3073
  * default payment method configuration has `klarna` and `affirm` enabled
2984
- * alongside `card`, `cashapp`, `link`, `amazon_pay` and `apple_pay`, and the
2985
- * marketplace session pins no `payment_method_types`, so a buyer may pay by
2986
- * either. Break-even is computed from the dearest enabled method, because a
2987
- * figure computed from the cheapest would be wrong exactly when it mattered.
2988
- * IF BNPL IS EVER TURNED OFF FOR MARKETPLACE SESSIONS, this constant is what
2989
- * has to move with it.
2990
- */ export const MARKETPLACE_PROCESSING_PERCENT_CARD = 2.9;
2991
- /** Klarna/Affirm, roughly. See the note above — this is the binding one. */ export const MARKETPLACE_PROCESSING_PERCENT_BNPL = 6;
2992
- /** Stripe's per-transaction fixed component, in cents. */ export const MARKETPLACE_PROCESSING_FIXED_CENTS = 30;
2993
- /**
2994
- * The tax rate the break-even figure assumes. Tax is added ON TOP of the
2995
- * listing price and enlarges the base Stripe charges its percentage against,
2996
- * so it makes the platform's cost slightly worse; the real rate is whatever
2997
- * `automatic_tax` computes for the buyer's address, and this is the
2998
- * platform's own Texas rate as a representative figure.
2999
- */ export const MARKETPLACE_ASSUMED_TAX_PERCENT = 8.25;
3000
- /**
3001
- * The processing rate a STOREFRONT destination charge is priced against
3002
- * (AGL-2152), and the one identifier to repoint if the storefront's payment
3003
- * method set changes.
3004
- *
3005
- * Deliberately its OWN name rather than a direct use of the marketplace
3006
- * constants, while being defined as one of them: the two surfaces share
3007
- * Stripe's published rates and today share the platform's payment method
3008
- * configuration, but they are separate product decisions. A future choice to
3009
- * pin `payment_method_types` on storefront sessions to the card family — and
3010
- * so to recover at `MARKETPLACE_PROCESSING_PERCENT_CARD` — must not silently
3011
- * lower the marketplace listing floor, which is derived from the same figure
3012
- * for a different flow.
3013
- */ export const STOREFRONT_PROCESSING_PERCENT = MARKETPLACE_PROCESSING_PERCENT_BNPL;
3014
- /** Stripe's per-transaction fixed component, in cents. Not rate-dependent. */ export const STOREFRONT_PROCESSING_FIXED_CENTS = MARKETPLACE_PROCESSING_FIXED_CENTS;
3015
- /**
3016
- * The funds flow for one sale, mirroring what `checkout.ts` actually builds
3017
- * (AGL-2343): tax exclusive and on top, a fixed transfer of the seller's share
3018
- * of the PRE-tax price, and Stripe's fee charged on the buyer's total.
3019
- */ export function marketplaceSaleEconomics(priceUsd, feePercent, processingPercent = MARKETPLACE_PROCESSING_PERCENT_BNPL) {
3020
- const priceCents = Math.max(0, Math.round(priceUsd * 100));
3021
- const taxCents = Math.round(priceCents * MARKETPLACE_ASSUMED_TAX_PERCENT / 100);
3022
- const buyerPaysCents = priceCents + taxCents;
3023
- const platformFeeCents = Math.round(priceCents * feePercent / 100);
3024
- const sellerReceivesCents = priceCents - platformFeeCents;
3025
- // A zero-price listing takes no payment at all, so there is no fee to pay.
3026
- const processingCents = priceCents === 0 ? 0 : Math.round(buyerPaysCents * processingPercent / 100 + MARKETPLACE_PROCESSING_FIXED_CENTS);
3027
- return {
3028
- priceCents,
3029
- taxCents,
3030
- buyerPaysCents,
3031
- sellerReceivesCents,
3032
- platformFeeCents,
3033
- processingCents,
3034
- platformNetCents: platformFeeCents - processingCents
3035
- };
3036
- }
3037
- /**
3038
- * The cheapest WHOLE-DOLLAR price at which the platform does not lose money on
3039
- * a sale (AGL-2343).
3040
- *
3041
- * Whole dollars because every publish route rounds `priceUsd` to one, so a
3042
- * fractional break-even is not a price anyone can actually set. Searched
3043
- * rather than solved algebraically so that it can never disagree with
3044
- * `marketplaceSaleEconomics` — the rounding in there is what decides the
3045
- * answer at these amounts, and a closed form would drift from it silently.
3046
- *
3047
- * THIS IS THE ENFORCED FLOOR: `marketplaceMinPriceUsd` returns this figure
3048
- * and every publish door refuses a paid listing under it. Recorded in the Pricing
3049
- * Decision Log and on AGL-2343.
3050
- */ export function marketplaceBreakEvenUsd(feePercent = bindingMarketplaceFeePct(), processingPercent = MARKETPLACE_PROCESSING_PERCENT_BNPL) {
3051
- // The listing price ceiling is the marketplace plugin's
3052
- // `MARKETPLACE_MAX_PRICE_USD`, which this library may not import (an app-tier
3053
- // lib cannot depend on an addon). The loop only needs SOME bound, and the
3054
- // answer is single digits at every rate the table holds, so a generous local
3055
- // one costs nothing and cannot drift into a wrong figure.
3056
- const searchCeilingUsd = 1000;
3057
- for(let priceUsd = 1; priceUsd <= searchCeilingUsd; priceUsd += 1){
3058
- if (marketplaceSaleEconomics(priceUsd, feePercent, processingPercent).platformNetCents >= 0) {
3059
- return priceUsd;
3060
- }
3061
- }
3062
- return searchCeilingUsd;
3063
- }
3064
- /**
3065
- * THE MINIMUM PRICE a paid marketplace listing may carry (AGL-2343).
3066
- *
3067
- * The floor is not a chosen round number — it is the break-even price itself,
3068
- * the
3069
- * cheapest whole dollar at which `marketplaceSaleEconomics` stops returning a
3070
- * negative platform net, computed at the take rate that breaks even LATEST and
3071
- * the dearest payment method the live configuration enables. Today that is $3.
3072
- *
3073
- * DERIVED, NEVER RESTATED. A second hand-written copy of this number in a
3074
- * publish route or a form is the artifact that decays into a route refusing a
3075
- * price a form invited, so both the server-side validator
3076
- * (`MARKETPLACE_MIN_PRICE_USD` in the marketplace model) and every price field
3077
- * read this. If the payment method set changes, or the plan table's lowest
3078
- * take rate moves, the floor moves with them on the next call.
3079
- *
3080
- * ZERO IS NOT BELOW THE FLOOR. A free listing takes no payment at all, so it
3081
- * costs nothing to process; the floor is a minimum on PAID listings only.
3082
- */ export function marketplaceMinPriceUsd() {
3083
- return marketplaceBreakEvenUsd();
3084
- }
3085
- /**
3086
- * Whether a price is a PAID price that the floor refuses (AGL-2343) — the one
3087
- * predicate a form uses to mark its price field in error and hold its publish
3088
- * button, so that the button and the server agree about what is publishable.
3089
- *
3090
- * Rounds first because every publish route does, and a form's value is a
3091
- * string mid-type: `'2.6'` is stored as $3 and must read as allowed here, or
3092
- * the button would refuse a price the route accepts.
3093
- */ export function isBelowMarketplacePriceFloor(priceUsd) {
3094
- const price = Math.round(Number(priceUsd) || 0);
3095
- return price > 0 && price < marketplaceMinPriceUsd();
3096
- }
3097
- /**
3098
- * The sentence a publish form shows under its price field when the price is
3099
- * below the floor (AGL-2343), or `undefined` when there is nothing to say.
3100
- *
3101
- * NOT ADVISORY ANY MORE. The same figure is enforced by every publish route
3102
- * through `publishPreconditionRefusal`, so this text has to read as a
3103
- * requirement rather than a warning — a publisher who is told "consider
3104
- * raising it" and then refused has been lied to by the form. It still explains
3105
- * WHY, because a bare minimum with no arithmetic behind it reads as an
3106
- * arbitrary tax on cheap listings.
3107
- *
3108
- * Shared by every publish form rather than written into each, because the
3109
- * figure has to be the same one in all of them and the wording is the only
3110
- * place a publisher ever sees it.
3111
- *
3112
- * Quoted at the take rate that breaks even LATEST, so the sentence holds for a
3113
- * free-plan publisher too.
3114
- */ export function marketplacePriceCostNote(priceUsd) {
3115
- const price = Math.round(Number(priceUsd) || 0);
3116
- const minimum = marketplaceMinPriceUsd();
3117
- // A free listing takes no payment, so it costs nothing to process and has
3118
- // nothing to warn about — silence there is the point, not an oversight.
3119
- if (!isBelowMarketplacePriceFloor(price)) return undefined;
3120
- const { processingCents, platformFeeCents } = marketplaceSaleEconomics(price, bindingMarketplaceFeePct());
3121
- const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
3122
- return `Too low to publish. Processing a $${price} payment costs about ` + `${usd(processingCents)}, more than the ${usd(platformFeeCents)} platform ` + `fee — the sale would lose money. The minimum paid price is ` + `$${minimum}, and a free listing ($0) takes no payment at all.`;
3123
- }
3124
- /**
3125
- * The standing helper sentence a price field shows when there is nothing wrong
3126
- * (AGL-2343): the minimum stated up front, so a publisher meets it while
3127
- * typing instead of discovering it as a refusal.
3128
- *
3129
- * A capability that is not surfaced in the console does not count as shipped,
3130
- * and that applies to a REFUSAL as much as to a feature: a floor only a route
3131
- * knows about is a trap.
3132
- *
3133
- * @param suffix whatever else that particular form needs to say, appended.
3134
- */ export function marketplacePriceFloorHint(suffix) {
3135
- return `0 for free, or $${marketplaceMinPriceUsd()} and up for a paid listing.` + (suffix ? ` ${suffix}` : '');
3136
- }
3074
+ * alongside `card`, `cashapp`, `link`, `amazon_pay` and `apple_pay`, and no
3075
+ * checkout session pins `payment_method_types`, so a buyer may pay by either.
3076
+ * A cost computed from the cheapest method would be wrong exactly when it
3077
+ * mattered.
3078
+ */ export const CARD_PROCESSING_PERCENT = 2.9;
3079
+ /** Klarna/Affirm, roughly. See the note above — this is the binding one. */ export const BNPL_PROCESSING_PERCENT = 6;
3080
+ /** Stripe's per-transaction fixed component, in cents. */ export const PROCESSING_FIXED_CENTS = 30;
3081
+ /**
3082
+ * The processing rate a storefront or booking destination charge is priced
3083
+ * against (AGL-2152), and the one identifier to repoint if those sessions'
3084
+ * payment method set changes.
3085
+ *
3086
+ * Deliberately its OWN name rather than a direct use of the binding rate,
3087
+ * while being defined as it: every seller shares Stripe's published rates and
3088
+ * today shares the platform's payment method configuration, but each surface
3089
+ * is a separate product decision. A future choice to pin
3090
+ * `payment_method_types` on storefront sessions to the card family — and so
3091
+ * to recover at `CARD_PROCESSING_PERCENT` — must not silently lower the
3092
+ * marketplace listing floor, which is derived from the binding rate for a
3093
+ * different flow.
3094
+ */ export const SALE_PROCESSING_PERCENT = BNPL_PROCESSING_PERCENT;
3095
+ /** Stripe's per-transaction fixed component, in cents. Not rate-dependent. */ export const SALE_PROCESSING_FIXED_CENTS = PROCESSING_FIXED_CENTS;
3137
3096
  /**
3138
3097
  * The cheapest plan whose BASE features include `feature`, in ladder order —
3139
3098
  * the answer to "which plan do I need for this?", which is the one thing an
@@ -3193,10 +3152,22 @@ export { PLATFORM_BRANDING_PROFILE } from "./platform-brand.js";
3193
3152
  * entitlement the stored profile is ignored entirely and the full Aglyn
3194
3153
  * brand is returned — so an org that white-labeled and then downgraded
3195
3154
  * reverts cleanly.
3155
+ *
3156
+ * An org WITH the entitlement that has stored no brand value at all gets the
3157
+ * full Aglyn brand too (AGL-3440). The entitlement is the right to a brand,
3158
+ * not a brand: until the org sets one, every name it shows is the platform's
3159
+ * anyway, so the white-label profile's blanks — no logo, no support link, no
3160
+ * home — conceal nothing and only strip the platform's own header and footer
3161
+ * from mail whose every line already names the platform. Every Enterprise
3162
+ * org carries the entitlement, Aglyn's own included. One field set, and the
3163
+ * rules below apply.
3196
3164
  */ export function resolveBrandingProfile(org) {
3197
3165
  var _ref, _cleanBrandString, _cleanBrandString1, _cleanBrandString2, _cleanBrandString3, _cleanBrandString4, _cleanBrandString5, _cleanBrandString6, _cleanBrandString7, _cleanBrandString8, _cleanBrandString9, _cleanBrandString10;
3198
3166
  if (!checkEntitlement(org, 'whiteLabel')) return PLATFORM_BRANDING_PROFILE;
3199
3167
  const profile = (_ref = org == null ? void 0 : org.brandingProfile) != null ? _ref : {};
3168
+ if (!Object.values(profile).some((value)=>cleanBrandString(value) !== undefined)) {
3169
+ return PLATFORM_BRANDING_PROFILE;
3170
+ }
3200
3171
  return {
3201
3172
  productName: (_cleanBrandString = cleanBrandString(profile.productName)) != null ? _cleanBrandString : PLATFORM_BRANDING_PROFILE.productName,
3202
3173
  // NO FALLBACK — and this became load-bearing the moment
@@ -3781,7 +3752,9 @@ export function checkQuota(org, quota, currentUsage) {
3781
3752
  * view costs. They are one physical measurement written in two units, and
3782
3753
  * what every `bandwidthGb` band is actually sized against is neither of them
3783
3754
  * alone but their quotient — `(1 GB ÷ this) × perPageView`, the cost of a
3784
- * gigabyte. That quotient is stable even while the page's weight is not,
3755
+ * gigabyte's WEIGHT, to which the band sizing adds the CDN requests the same
3756
+ * views make ({@link PAGE_VIEW_CDN_REQUEST_COST_USD}). That quotient is
3757
+ * stable even while the page's weight is not,
3785
3758
  * which is precisely why moving one half re-prices every band on the ladder
3786
3759
  * without touching a band: a heavier page costs more per view and buys fewer
3787
3760
  * views per gigabyte, and the two cancel.
@@ -3830,11 +3803,13 @@ export function checkQuota(org, quota, currentUsage) {
3830
3803
  * It is a CAP, not a price, and it is lower than {@link
3831
3804
  * FORM_ABUSE_CEILING_MULTIPLE} because bandwidth is the largest cost line the
3832
3805
  * platform carries and the one a stranger can spend on the customer's behalf.
3833
- * Past the band every 1,000 views bills $0.21 against about $0.16 of real
3834
- * cost, so the tail is no longer a loss — but a scraper, a hotlinked asset or
3835
- * a botnet still bills the account holder for traffic they did not ask for,
3836
- * and the ceiling is what bounds that before staff look at it. At 10× the
3837
- * band that tail was open-ended on Agency; at 3× it is bounded.
3806
+ * Past the band every 1,000 views bills $0.70 against at most $0.54 of real
3807
+ * cost — the dearest region's — once the CDN's request allowance is spent
3808
+ * ({@link PAGE_VIEW_CDN_REQUEST_COST_USD}), so the tail is not a loss — but a
3809
+ * scraper, a hotlinked asset or a botnet still bills the account holder for
3810
+ * traffic they did not ask for, and the ceiling is what bounds that before
3811
+ * staff look at it. At 10× the band that tail was open-ended on Agency; at 3×
3812
+ * it is bounded.
3838
3813
  */ export const BANDWIDTH_ABUSE_CEILING_MULTIPLE = 3;
3839
3814
  /**
3840
3815
  * Absolute floor for the bandwidth ceiling, in PAGE VIEWS per month, so the
@@ -3844,9 +3819,10 @@ export function checkQuota(org, quota, currentUsage) {
3844
3819
  * Free includes 2 GB ≈ 2,071 views; 3× would be ~6,212, which a genuinely
3845
3820
  * successful hobby site (a post that lands on Hacker News) reaches in an
3846
3821
  * afternoon and would be a miserable first experience of the platform.
3847
- * 100,000 views/month ≈ 96.6 GB ≈ **$16 of real COGS** at
3848
- * `METERED_UNIT_RATES_USD.perPageView` — well above the free band, and an
3849
- * order of magnitude below the $162 a million views costs. It is the number
3822
+ * 100,000 views/month ≈ 96.6 GB ≈ **$54 of real COGS** at the dearest
3823
+ * region (`METERED_UNIT_RATES_USD.perPageView` plus
3824
+ * `PAGE_VIEW_CDN_REQUEST_COST_USD`) — well above the free band, and an order
3825
+ * of magnitude below the $538 a million views costs. It is the number
3850
3826
  * that makes "free stays free" true without making it stingy.
3851
3827
  */ export const BANDWIDTH_ABUSE_CEILING_FLOOR = 100000;
3852
3828
  /**