@kerne/react 0.1.3 → 1.0.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/dist/ui/index.cjs CHANGED
@@ -16,7 +16,8 @@
16
16
 
17
17
 
18
18
 
19
- var _chunk7U3QBMA7cjs = require('../chunk-7U3QBMA7.cjs');
19
+
20
+ var _chunkIMZFZ3P5cjs = require('../chunk-IMZFZ3P5.cjs');
20
21
 
21
22
  // src/ui/AuthFlow.tsx
22
23
  var _react = require('react');
@@ -246,6 +247,7 @@ var CSS = `
246
247
 
247
248
  /* ---------- Stack ---------- */
248
249
  .kerne-stack { display: flex; flex-direction: column; gap: 16px; }
250
+ .kerne-inline-actions { display: flex; gap: 8px; flex-wrap: wrap; }
249
251
  .kerne-form { display: flex; flex-direction: column; gap: 12px; }
250
252
  .kerne-field { display: flex; flex-direction: column; gap: 6px; }
251
253
  .kerne-label { font-size: 14px; font-weight: 500; color: var(--kerne-fg); }
@@ -512,6 +514,126 @@ var CSS = `
512
514
  .kerne-meter-note { font-size: 12px; color: var(--kerne-fg-subtle); margin: 0; }
513
515
  @media (prefers-reduced-motion: reduce) { .kerne-meter-fill { transition: none; } }
514
516
 
517
+ /* ---------- Pricing table ---------- */
518
+ .kerne-pricing-skeleton { width: 100%; }
519
+
520
+ .kerne-pricing-switch-wrap { display: flex; justify-content: center; }
521
+ .kerne-pricing-switch {
522
+ position: relative;
523
+ display: inline-flex;
524
+ gap: 2px;
525
+ padding: 4px;
526
+ background: var(--kerne-panel);
527
+ border-radius: 999px;
528
+ }
529
+ .kerne-pricing-switch-thumb {
530
+ position: absolute;
531
+ top: 4px;
532
+ bottom: 4px;
533
+ left: 0;
534
+ border-radius: 999px;
535
+ background: var(--kerne-surface);
536
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
537
+ transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1), width 0.32s cubic-bezier(0.16, 1, 0.3, 1);
538
+ }
539
+ .kerne-pricing-switch-option {
540
+ position: relative;
541
+ z-index: 1;
542
+ display: inline-flex;
543
+ align-items: center;
544
+ gap: 7px;
545
+ font: inherit;
546
+ font-size: 13.5px;
547
+ font-weight: 500;
548
+ white-space: nowrap;
549
+ color: var(--kerne-fg-muted);
550
+ background: none;
551
+ border: none;
552
+ border-radius: 999px;
553
+ padding: 7px 16px;
554
+ cursor: pointer;
555
+ transition: color 0.18s ease;
556
+ }
557
+ .kerne-pricing-switch-option[aria-pressed="true"] { color: var(--kerne-fg); }
558
+ .kerne-pricing-switch-option:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; }
559
+ .kerne-pricing-switch-save {
560
+ font-size: 10.5px;
561
+ font-weight: 600;
562
+ letter-spacing: 0.01em;
563
+ color: var(--kerne-success);
564
+ background: color-mix(in srgb, var(--kerne-success) 14%, transparent);
565
+ padding: 2px 6px;
566
+ border-radius: 999px;
567
+ }
568
+
569
+ .kerne-pricing-grid {
570
+ display: grid;
571
+ grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
572
+ gap: 20px;
573
+ align-items: stretch;
574
+ }
575
+
576
+ .kerne-pricing-card {
577
+ position: relative;
578
+ display: flex;
579
+ flex-direction: column;
580
+ gap: 24px;
581
+ height: 100%;
582
+ padding: 28px 26px;
583
+ background: var(--kerne-surface);
584
+ border: 1px solid var(--kerne-border);
585
+ border-radius: calc(var(--kerne-radius) + 8px);
586
+ }
587
+ .kerne-pricing-card[data-featured="true"] {
588
+ background: var(--kerne-surface-raised);
589
+ border-color: var(--kerne-fg);
590
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 24px 48px -28px rgba(0, 0, 0, 0.22);
591
+ }
592
+ .kerne-pricing-card[data-current="true"]:not([data-featured="true"]) { border-color: var(--kerne-border-strong); }
593
+
594
+ /* A quiet inline pill, not a loud badge escaping the card border - that
595
+ shouted-uppercase-chip-on-the-edge look is the single most template-y
596
+ tell a pricing page can have. Reads as confirmation of what the card's
597
+ own elevation already signals, never as the primary signal itself. */
598
+ .kerne-pricing-badge {
599
+ align-self: flex-start;
600
+ font-size: 11.5px;
601
+ font-weight: 600;
602
+ letter-spacing: 0.01em;
603
+ color: var(--kerne-fg);
604
+ background: var(--kerne-panel);
605
+ padding: 3px 9px;
606
+ border-radius: 999px;
607
+ }
608
+
609
+ .kerne-pricing-card-head { display: flex; flex-direction: column; gap: 8px; }
610
+ .kerne-pricing-card-name { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
611
+ .kerne-pricing-card-desc { margin: 0; font-size: 13px; line-height: 1.5; color: var(--kerne-fg-muted); }
612
+
613
+ .kerne-pricing-price { display: flex; flex-direction: column; gap: 3px; min-height: 52px; justify-content: center; }
614
+ .kerne-pricing-price-row { display: flex; align-items: baseline; gap: 5px; }
615
+ .kerne-pricing-amount {
616
+ font-size: 38px;
617
+ font-weight: 600;
618
+ letter-spacing: -0.025em;
619
+ line-height: 1;
620
+ font-variant-numeric: tabular-nums;
621
+ }
622
+ .kerne-pricing-interval { font-size: 14px; color: var(--kerne-fg-muted); }
623
+ .kerne-pricing-billed { margin: 0; font-size: 12px; color: var(--kerne-fg-subtle); }
624
+
625
+ .kerne-pricing-features { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; flex: 1; }
626
+ .kerne-pricing-feature { display: flex; align-items: flex-start; gap: 9px; font-size: 13.5px; line-height: 1.4; color: var(--kerne-fg); }
627
+ .kerne-pricing-feature svg { flex-shrink: 0; margin-top: 2px; color: var(--kerne-fg-subtle); }
628
+ .kerne-pricing-feature-more { font-size: 12.5px; color: var(--kerne-fg-subtle); padding-left: 24px; }
629
+
630
+ .kerne-pricing-cta { margin-top: auto; }
631
+ .kerne-pricing-cta .kerne-button { width: 100%; }
632
+
633
+ @media (prefers-reduced-motion: reduce) {
634
+ .kerne-pricing-switch-thumb { transition: none; }
635
+ }
636
+
515
637
  /* ---------- Sections (profile, settings) ---------- */
516
638
  .kerne-section-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
517
639
  .kerne-section-title { font-size: 15px; font-weight: 600; margin: 0; }
@@ -781,7 +903,7 @@ function Field({
781
903
  const id = _react.useId.call(void 0, );
782
904
  const errorId = `${id}-error`;
783
905
  const noteId = `${id}-note`;
784
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
906
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
785
907
  const isPassword = type === "password";
786
908
  const [revealed, setRevealed] = _react.useState.call(void 0, false);
787
909
  const [capsLock, setCapsLock] = _react.useState.call(void 0, false);
@@ -909,11 +1031,11 @@ function PasswordStrength({
909
1031
  password,
910
1032
  policy
911
1033
  }) {
912
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1034
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
913
1035
  const resolved = _nullishCoalesce(policy, () => ( resolvePolicy(null)));
914
1036
  if (!meetsPolicy(password, resolved)) {
915
1037
  const labels2 = {
916
- min_length: _chunk7U3QBMA7cjs.fill.call(void 0, l10n.common.passwordHint, { count: resolved.min_length }),
1038
+ min_length: _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.common.passwordHint, { count: resolved.min_length }),
917
1039
  require_uppercase: l10n.common.passwordRequireUppercase,
918
1040
  require_number: l10n.common.passwordRequireNumber,
919
1041
  require_symbol: l10n.common.passwordRequireSymbol
@@ -1109,10 +1231,10 @@ function OAuthButtons({
1109
1231
  disabled,
1110
1232
  onSelect
1111
1233
  }) {
1112
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1234
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1113
1235
  if (providers.length === 0) return null;
1114
1236
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-oauth-group", children: providers.map((provider) => {
1115
- const label = _chunk7U3QBMA7cjs.fill.call(void 0, l10n.common.continueWith, {
1237
+ const label = _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.common.continueWith, {
1116
1238
  provider: _nullishCoalesce(OAUTH_LABELS[provider], () => ( provider))
1117
1239
  });
1118
1240
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
@@ -1133,12 +1255,12 @@ function OAuthButtons({
1133
1255
  }) });
1134
1256
  }
1135
1257
  function KerneBranding({ show }) {
1136
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1258
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1137
1259
  if (!show) return null;
1138
1260
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "a", { className: "kerne-branding", href: "https://kerne.io", target: "_blank", rel: "noopener noreferrer", children: l10n.common.securedByKerne });
1139
1261
  }
1140
1262
  function FormSkeleton() {
1141
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1263
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1142
1264
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-form", "aria-busy": "true", "aria-label": l10n.common.loading, children: [
1143
1265
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-skeleton" }),
1144
1266
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-skeleton" })
@@ -1163,11 +1285,11 @@ function LoginForm({
1163
1285
  logo,
1164
1286
  className
1165
1287
  }) {
1166
- const client = _chunk7U3QBMA7cjs.useClient.call(void 0, );
1167
- const { login, startPasswordless } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
1168
- const { config, isLoading: configLoading, error: configError } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
1169
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1170
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
1288
+ const client = _chunkIMZFZ3P5cjs.useClient.call(void 0, );
1289
+ const { login, startPasswordless } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
1290
+ const { config, isLoading: configLoading, error: configError } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
1291
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1292
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
1171
1293
  const [email, setEmail] = _react.useState.call(void 0, "");
1172
1294
  const [password, setPassword] = _react.useState.call(void 0, "");
1173
1295
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
@@ -1231,7 +1353,7 @@ function LoginForm({
1231
1353
  const signUpLine = canSignUp && (signUpUrl || onSignUpClick) ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
1232
1354
  l10n.signIn.signUpPrompt,
1233
1355
  " ",
1234
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextLink, { href: signUpUrl, onClick: onSignUpClick, children: appName ? _chunk7U3QBMA7cjs.fill.call(void 0, l10n.signIn.signUpForApp, { app: appName }) : l10n.signIn.signUpLink }),
1356
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextLink, { href: signUpUrl, onClick: onSignUpClick, children: appName ? _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.signIn.signUpForApp, { app: appName }) : l10n.signIn.signUpLink }),
1235
1357
  "."
1236
1358
  ] }) : void 0;
1237
1359
  const submitLabel = submitting ? usePassword ? l10n.signIn.submitting : l10n.signIn.magicSubmitting : usePassword ? l10n.signIn.submit : l10n.signIn.magicSubmit;
@@ -1240,7 +1362,7 @@ function LoginForm({
1240
1362
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Masthead, { logo, title: heading, subtitle: _nullishCoalesce(subtitle, () => ( signUpLine)) }),
1241
1363
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-stack", children: [
1242
1364
  error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: error }) : null,
1243
- linkSentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunk7U3QBMA7cjs.fillNode.call(void 0, l10n.signIn.linkSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1365
+ linkSentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkIMZFZ3P5cjs.fillNode.call(void 0, l10n.signIn.linkSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1244
1366
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1245
1367
  Field,
1246
1368
  {
@@ -1316,13 +1438,13 @@ function RegisterForm({
1316
1438
  logo,
1317
1439
  className
1318
1440
  }) {
1319
- const client = _chunk7U3QBMA7cjs.useClient.call(void 0, );
1320
- const { register, startPasswordless } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
1321
- const { config, isLoading: configLoading, error: configError } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
1441
+ const client = _chunkIMZFZ3P5cjs.useClient.call(void 0, );
1442
+ const { register, startPasswordless } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
1443
+ const { config, isLoading: configLoading, error: configError } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
1322
1444
  const policy = resolvePolicy(config);
1323
1445
  const invitationToken = useUrlToken("token", invitationTokenProp) || void 0;
1324
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1325
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
1446
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1447
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
1326
1448
  const [email, setEmail] = _react.useState.call(void 0, "");
1327
1449
  const [password, setPassword] = _react.useState.call(void 0, "");
1328
1450
  const [name, setName] = _react.useState.call(void 0, "");
@@ -1333,7 +1455,7 @@ function RegisterForm({
1333
1455
  const [passwordMode, setPasswordMode] = _react.useState.call(void 0, false);
1334
1456
  const shell = (children) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, children });
1335
1457
  const appName = _optionalChain([config, 'optionalAccess', _13 => _13.app_name]);
1336
- const heading = _nullishCoalesce(title, () => ( (appName ? _chunk7U3QBMA7cjs.fill.call(void 0, l10n.signUp.titleWithApp, { app: appName }) : l10n.signUp.title)));
1458
+ const heading = _nullishCoalesce(title, () => ( (appName ? _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.signUp.titleWithApp, { app: appName }) : l10n.signUp.title)));
1337
1459
  const signInLine = signInUrl || onSignInClick ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
1338
1460
  l10n.signUp.signInPrompt,
1339
1461
  " ",
@@ -1360,7 +1482,7 @@ function RegisterForm({
1360
1482
  return shell(
1361
1483
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
1362
1484
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Masthead, { logo, title: l10n.signUp.closedTitle, subtitle: signInLine }),
1363
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunk7U3QBMA7cjs.fill.call(void 0, l10n.signUp.closedBody, { app: _nullishCoalesce(appName, () => ( l10n.common.thisApp)) }) }),
1485
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.signUp.closedBody, { app: _nullishCoalesce(appName, () => ( l10n.common.thisApp)) }) }),
1364
1486
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KerneBranding, { show: config.show_kerne_branding })
1365
1487
  ] })
1366
1488
  );
@@ -1422,7 +1544,7 @@ function RegisterForm({
1422
1544
  ),
1423
1545
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-stack", children: [
1424
1546
  error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: error }) : null,
1425
- linkSentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunk7U3QBMA7cjs.fillNode.call(void 0, l10n.signUp.linkSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1547
+ linkSentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkIMZFZ3P5cjs.fillNode.call(void 0, l10n.signUp.linkSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1426
1548
  needsCode ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1427
1549
  Field,
1428
1550
  {
@@ -1527,10 +1649,10 @@ function ForgotPasswordForm({
1527
1649
  logo,
1528
1650
  className
1529
1651
  }) {
1530
- const { requestPasswordReset } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
1531
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
1532
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1533
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
1652
+ const { requestPasswordReset } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
1653
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
1654
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1655
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
1534
1656
  const [email, setEmail] = _react.useState.call(void 0, "");
1535
1657
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
1536
1658
  const [error, setError] = _react.useState.call(void 0, null);
@@ -1566,7 +1688,7 @@ function ForgotPasswordForm({
1566
1688
  ),
1567
1689
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-stack", children: [
1568
1690
  error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: error }) : null,
1569
- sentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunk7U3QBMA7cjs.fillNode.call(void 0, l10n.resetPassword.requestSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: sentTo }) }) }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1691
+ sentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkIMZFZ3P5cjs.fillNode.call(void 0, l10n.resetPassword.requestSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: sentTo }) }) }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1570
1692
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1571
1693
  Field,
1572
1694
  {
@@ -1603,12 +1725,12 @@ function ResetPasswordForm({
1603
1725
  logo,
1604
1726
  className
1605
1727
  }) {
1606
- const { confirmPasswordReset } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
1607
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
1728
+ const { confirmPasswordReset } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
1729
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
1608
1730
  const policy = resolvePolicy(config);
1609
1731
  const token = useUrlToken("token", tokenProp);
1610
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1611
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
1732
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1733
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
1612
1734
  const [password, setPassword] = _react.useState.call(void 0, "");
1613
1735
  const [confirm, setConfirm] = _react.useState.call(void 0, "");
1614
1736
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
@@ -1723,12 +1845,12 @@ function MagicLinkCallback({
1723
1845
  logo,
1724
1846
  className
1725
1847
  }) {
1726
- const client = _chunk7U3QBMA7cjs.useClient.call(void 0, );
1727
- const { loginWithMagicLink } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
1728
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
1848
+ const client = _chunkIMZFZ3P5cjs.useClient.call(void 0, );
1849
+ const { loginWithMagicLink } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
1850
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
1729
1851
  const token = useUrlToken("token", tokenProp);
1730
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1731
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
1852
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1853
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
1732
1854
  const [state, setState] = _react.useState.call(void 0, "pending");
1733
1855
  const [message, setMessage] = _react.useState.call(void 0, null);
1734
1856
  const consumed = _react.useRef.call(void 0, false);
@@ -1782,11 +1904,11 @@ function VerifyEmailForm({
1782
1904
  logo,
1783
1905
  className
1784
1906
  }) {
1785
- const { verifyEmailWithCode, verifyEmailWithLink, sendVerificationEmail, refreshToken } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
1786
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
1907
+ const { verifyEmailWithCode, verifyEmailWithLink, sendVerificationEmail, refreshToken } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
1908
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
1787
1909
  const token = useUrlToken("token", tokenProp);
1788
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1789
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
1910
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
1911
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
1790
1912
  const [code, setCode] = _react.useState.call(void 0, "");
1791
1913
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
1792
1914
  const [error, setError] = _react.useState.call(void 0, null);
@@ -1908,13 +2030,13 @@ function ActivateAccountForm({
1908
2030
  logo,
1909
2031
  className
1910
2032
  }) {
1911
- const client = _chunk7U3QBMA7cjs.useClient.call(void 0, );
1912
- const { getActivationContext, completeActivation } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
1913
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
2033
+ const client = _chunkIMZFZ3P5cjs.useClient.call(void 0, );
2034
+ const { getActivationContext, completeActivation } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
2035
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
1914
2036
  const policy = resolvePolicy(config);
1915
2037
  const token = useUrlToken("token", tokenProp);
1916
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
1917
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
2038
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2039
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
1918
2040
  const [context, setContext] = _react.useState.call(void 0, null);
1919
2041
  const [loadError, setLoadError] = _react.useState.call(void 0, null);
1920
2042
  const [password, setPassword] = _react.useState.call(void 0, "");
@@ -1984,7 +2106,7 @@ function ActivateAccountForm({
1984
2106
  {
1985
2107
  logo,
1986
2108
  title: heading,
1987
- subtitle: _chunk7U3QBMA7cjs.fillNode.call(void 0,
2109
+ subtitle: _chunkIMZFZ3P5cjs.fillNode.call(void 0,
1988
2110
  credentialsMode ? l10n.activate.credentialsSubtitle : l10n.activate.magicSubtitle,
1989
2111
  { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: context.email }) }
1990
2112
  )
@@ -2056,7 +2178,7 @@ function AuthFlow({
2056
2178
  logo,
2057
2179
  className
2058
2180
  }) {
2059
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
2181
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
2060
2182
  const token = useUrlToken("token");
2061
2183
  const [internalStep, setInternalStep] = _react.useState.call(void 0, null);
2062
2184
  const go = (next) => {
@@ -2143,11 +2265,11 @@ function ChangePasswordForm({
2143
2265
  appearance,
2144
2266
  className
2145
2267
  }) {
2146
- const { updatePassword } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
2147
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
2268
+ const { updatePassword } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
2269
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
2148
2270
  const policy = resolvePolicy(config);
2149
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2150
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
2271
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2272
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
2151
2273
  const heading = title === null ? null : _nullishCoalesce(title, () => ( l10n.security.submit));
2152
2274
  const [currentPassword, setCurrentPassword] = _react.useState.call(void 0, "");
2153
2275
  const [newPassword, setNewPassword] = _react.useState.call(void 0, "");
@@ -2253,10 +2375,10 @@ function WaitlistForm({
2253
2375
  logo,
2254
2376
  className
2255
2377
  }) {
2256
- const { join } = _chunk7U3QBMA7cjs.useWaitlist.call(void 0, );
2257
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
2258
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2259
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
2378
+ const { join } = _chunkIMZFZ3P5cjs.useWaitlist.call(void 0, );
2379
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
2380
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2381
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
2260
2382
  const [email, setEmail] = _react.useState.call(void 0, "");
2261
2383
  const [name, setName] = _react.useState.call(void 0, "");
2262
2384
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
@@ -2337,8 +2459,8 @@ function SignOutButton({
2337
2459
  appearance
2338
2460
  }) {
2339
2461
  ensureStylesInjected();
2340
- const { logout } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
2341
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2462
+ const { logout } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
2463
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2342
2464
  const handle = () => {
2343
2465
  logout();
2344
2466
  _optionalChain([onSignedOut, 'optionalCall', _36 => _36()]);
@@ -2372,9 +2494,9 @@ function UserButton({
2372
2494
  className
2373
2495
  }) {
2374
2496
  ensureStylesInjected();
2375
- const { logout, isAuthenticated } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
2376
- const { user, fullName, initials, email } = _chunk7U3QBMA7cjs.useUser.call(void 0, );
2377
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2497
+ const { logout, isAuthenticated } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
2498
+ const { user, fullName, initials, email } = _chunkIMZFZ3P5cjs.useUser.call(void 0, );
2499
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2378
2500
  const [open, setOpen] = _react.useState.call(void 0, false);
2379
2501
  const wrapRef = _react.useRef.call(void 0, null);
2380
2502
  _react.useEffect.call(void 0, () => {
@@ -2465,10 +2587,10 @@ function UserButton({
2465
2587
 
2466
2588
 
2467
2589
  function ProfileSection({ verificationCallbackUrl, onSaved }) {
2468
- const { updateProfile, sendVerificationEmail, getVerificationStatus } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
2469
- const { user, fullName, initials, email } = _chunk7U3QBMA7cjs.useUser.call(void 0, );
2470
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2471
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
2590
+ const { updateProfile, sendVerificationEmail, getVerificationStatus } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
2591
+ const { user, fullName, initials, email } = _chunkIMZFZ3P5cjs.useUser.call(void 0, );
2592
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2593
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
2472
2594
  const [firstName, setFirstName] = _react.useState.call(void 0, "");
2473
2595
  const [lastName, setLastName] = _react.useState.call(void 0, "");
2474
2596
  const [saving, setSaving] = _react.useState.call(void 0, false);
@@ -2579,10 +2701,10 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
2579
2701
 
2580
2702
 
2581
2703
  function SecuritySection({ showDeleteAccount = true, onDeleted }) {
2582
- const { requestAccountDeletion } = _chunk7U3QBMA7cjs.useAuth.call(void 0, );
2583
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
2584
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2585
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
2704
+ const { requestAccountDeletion } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
2705
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
2706
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2707
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
2586
2708
  const [error, setError] = _react.useState.call(void 0, null);
2587
2709
  const [confirmingDelete, setConfirmingDelete] = _react.useState.call(void 0, false);
2588
2710
  const [deleting, setDeleting] = _react.useState.call(void 0, false);
@@ -2677,15 +2799,45 @@ function formatInterval(interval, labels) {
2677
2799
  return interval;
2678
2800
  }
2679
2801
  }
2802
+ var ENCODED_INTERVAL_UNIT = { D: 12 / 365, M: 1, Y: 12 };
2803
+ function approxMonths(value) {
2804
+ const match = ENCODED_INTERVAL.exec(value);
2805
+ if (!match) return 0;
2806
+ return parseInt(match[1], 10) * ENCODED_INTERVAL_UNIT[match[2]];
2807
+ }
2808
+ function resolvePriceDisplay(price, displayInterval) {
2809
+ if (!displayInterval || displayInterval === price.interval) {
2810
+ return {
2811
+ billedAmount: price.amount,
2812
+ billedInterval: price.interval,
2813
+ displayAmount: price.amount,
2814
+ displayInterval: price.interval
2815
+ };
2816
+ }
2817
+ const ratio = approxMonths(displayInterval) / approxMonths(price.interval);
2818
+ return {
2819
+ billedAmount: price.amount,
2820
+ billedInterval: price.interval,
2821
+ displayAmount: Math.round(price.amount * ratio),
2822
+ displayInterval
2823
+ };
2824
+ }
2680
2825
 
2681
2826
  // src/ui/UsageMeters.tsx
2682
2827
 
2683
- function levelFor(ratio, overageAllowed) {
2684
- if (overageAllowed) return ratio >= 1 ? "warning" : "normal";
2828
+ function isSoftOverage(overage) {
2829
+ return overage === "ALLOW" || overage === "BILL";
2830
+ }
2831
+ function levelFor(ratio, softOverage) {
2832
+ if (softOverage) return ratio >= 1 ? "warning" : "normal";
2685
2833
  if (ratio >= 0.9) return "critical";
2686
2834
  if (ratio >= 0.75) return "warning";
2687
2835
  return "normal";
2688
2836
  }
2837
+ function quotaRatio(used, limit) {
2838
+ if (limit <= 0) return used > 0 ? 1 : 0;
2839
+ return Math.min(used / limit, 1);
2840
+ }
2689
2841
  function UsageMeters({
2690
2842
  title,
2691
2843
  featureKeys,
@@ -2696,9 +2848,9 @@ function UsageMeters({
2696
2848
  appearance,
2697
2849
  className
2698
2850
  }) {
2699
- const { entitlements, isLoading, error } = _chunk7U3QBMA7cjs.useEntitlements.call(void 0, );
2700
- const { subscription } = _chunk7U3QBMA7cjs.useSubscription.call(void 0, );
2701
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2851
+ const { entitlements, isLoading, error } = _chunkIMZFZ3P5cjs.useEntitlements.call(void 0, );
2852
+ const { subscription } = _chunkIMZFZ3P5cjs.useSubscription.call(void 0, );
2853
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2702
2854
  const heading = title === null ? null : _nullishCoalesce(title, () => ( l10n.usage.title));
2703
2855
  const nameFor = (key) => {
2704
2856
  if (_optionalChain([labels, 'optionalAccess', _51 => _51[key]])) return labels[key];
@@ -2739,14 +2891,17 @@ function UsageMeters({
2739
2891
  return shell(
2740
2892
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-meters", children: rows.map((e) => {
2741
2893
  const unlimited = e.limit === null || e.limit === void 0;
2742
- const overageAllowed = e.overage_behavior === "ALLOW";
2743
- const ratio = unlimited ? 0 : Math.min(e.used / e.limit, 1);
2744
- const level = unlimited ? "normal" : levelFor(e.used / e.limit, overageAllowed);
2894
+ const softOverage = isSoftOverage(e.overage_behavior);
2895
+ const billed = e.overage_behavior === "BILL";
2896
+ const ratio = unlimited ? 0 : quotaRatio(e.used, e.limit);
2897
+ const level = unlimited ? "normal" : levelFor(ratio, softOverage);
2745
2898
  const over = !unlimited && e.used > e.limit;
2899
+ const standing = billed ? _optionalChain([subscription, 'optionalAccess', _56 => _56.entitlements, 'access', _57 => _57.find, 'call', _58 => _58((se) => se.feature_key === e.feature_key)]) : void 0;
2900
+ const capReached = billed && (_nullishCoalesce(_optionalChain([standing, 'optionalAccess', _59 => _59.cap_reached]), () => ( false)));
2746
2901
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-meter", "data-level": level, children: [
2747
2902
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-meter-head", children: [
2748
2903
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-meter-name", children: nameFor(e.feature_key) }),
2749
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-meter-value", children: unlimited ? _chunk7U3QBMA7cjs.fill.call(void 0, l10n.usage.used, { count: formatNumber(e.used) }) : `${formatNumber(e.used)} / ${formatNumber(e.limit)}` })
2904
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-meter-value", children: unlimited ? _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.usage.used, { count: formatNumber(e.used) }) : `${formatNumber(e.used)} / ${formatNumber(e.limit)}` })
2750
2905
  ] }),
2751
2906
  !unlimited ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2752
2907
  "div",
@@ -2760,9 +2915,18 @@ function UsageMeters({
2760
2915
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-meter-fill", style: { width: `${ratio * 100}%` } })
2761
2916
  }
2762
2917
  ) : null,
2763
- over && overageAllowed ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note", children: _chunk7U3QBMA7cjs.fill.call(void 0, l10n.usage.overage, {
2764
- count: formatNumber(e.used - e.limit)
2765
- }) }) : over ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note", children: l10n.usage.limitReached }) : null
2918
+ over && softOverage ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
2919
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note", children: _chunkIMZFZ3P5cjs.fill.call(void 0,
2920
+ // capReached inverts overageBilled's meaning (billing stopped,
2921
+ // access didn't) - a variant string would say the opposite of
2922
+ // the truth for the one plan that just stopped costing money.
2923
+ capReached ? l10n.usage.capReached : billed ? l10n.usage.overageBilled : l10n.usage.overage,
2924
+ { count: formatNumber(e.used - e.limit) }
2925
+ ) }),
2926
+ billed && _optionalChain([standing, 'optionalAccess', _60 => _60.billed_amount]) != null && standing.currency ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note kerne-meter-cost", children: _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.usage.billedAmount, {
2927
+ amount: formatMoney(standing.billed_amount, standing.currency)
2928
+ }) }) : null
2929
+ ] }) : over ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note", children: l10n.usage.limitReached }) : null
2766
2930
  ] }, e.feature_key);
2767
2931
  }) })
2768
2932
  );
@@ -2786,13 +2950,15 @@ function SubscriptionCard({
2786
2950
  upgradeUrl,
2787
2951
  onUpgradeClick,
2788
2952
  hidePortalButton = false,
2953
+ hideCancelButton = false,
2789
2954
  appearance,
2790
2955
  className
2791
2956
  }) {
2792
- const { subscription, isLoading, error } = _chunk7U3QBMA7cjs.useSubscription.call(void 0, productSlug);
2793
- const { openPortal } = _chunk7U3QBMA7cjs.usePortal.call(void 0, );
2794
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2795
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
2957
+ const { subscription, isLoading, error, refetch } = _chunkIMZFZ3P5cjs.useSubscription.call(void 0, productSlug);
2958
+ const { openPortal } = _chunkIMZFZ3P5cjs.usePortal.call(void 0, );
2959
+ const { cancelSubscription, isCanceling } = _chunkIMZFZ3P5cjs.useCancelSubscription.call(void 0, );
2960
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
2961
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
2796
2962
  const heading = title === null ? null : _nullishCoalesce(title, () => ( l10n.billing.plan));
2797
2963
  const statusLabels = {
2798
2964
  ACTIVE: l10n.billing.statusActive,
@@ -2804,6 +2970,8 @@ function SubscriptionCard({
2804
2970
  };
2805
2971
  const [opening, setOpening] = _react.useState.call(void 0, false);
2806
2972
  const [portalError, setPortalError] = _react.useState.call(void 0, null);
2973
+ const [confirmingCancel, setConfirmingCancel] = _react.useState.call(void 0, false);
2974
+ const [cancelError, setCancelError] = _react.useState.call(void 0, null);
2807
2975
  const handlePortal = async () => {
2808
2976
  setPortalError(null);
2809
2977
  setOpening(true);
@@ -2814,6 +2982,17 @@ function SubscriptionCard({
2814
2982
  setOpening(false);
2815
2983
  }
2816
2984
  };
2985
+ const handleCancel = async () => {
2986
+ if (!subscription) return;
2987
+ setCancelError(null);
2988
+ try {
2989
+ await cancelSubscription(subscription.id, false);
2990
+ setConfirmingCancel(false);
2991
+ refetch();
2992
+ } catch (e) {
2993
+ setCancelError(showError(e));
2994
+ }
2995
+ };
2817
2996
  const shell = (children) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-stack", children: [
2818
2997
  heading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: "kerne-section-title", children: heading }) : null,
2819
2998
  children
@@ -2879,7 +3058,27 @@ function SubscriptionCard({
2879
3058
  ] }) : null
2880
3059
  ] }),
2881
3060
  canceled_at ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: l10n.billing.cancelNotice }) : null,
2882
- !hidePortalButton ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { variant: "outline", type: "button", onClick: handlePortal, loading: opening, children: opening ? l10n.billing.opening : l10n.billing.managePortal }) : null
3061
+ cancelError ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: cancelError }) : null,
3062
+ confirmingCancel ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Panel, { children: [
3063
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: l10n.billing.cancelConfirmTitle }),
3064
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-subsection-desc", children: l10n.billing.cancelConfirmBody }),
3065
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-inline-actions", children: [
3066
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3067
+ Button,
3068
+ {
3069
+ variant: "outline",
3070
+ type: "button",
3071
+ onClick: () => setConfirmingCancel(false),
3072
+ disabled: isCanceling,
3073
+ children: l10n.billing.cancelConfirmDismiss
3074
+ }
3075
+ ),
3076
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { type: "button", onClick: handleCancel, loading: isCanceling, children: isCanceling ? l10n.billing.canceling : l10n.billing.cancelConfirmButton })
3077
+ ] })
3078
+ ] }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-inline-actions", children: [
3079
+ !hidePortalButton ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { variant: "outline", type: "button", onClick: handlePortal, loading: opening, children: opening ? l10n.billing.opening : l10n.billing.managePortal }) : null,
3080
+ !hideCancelButton && !canceled_at && status !== "CANCELED" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { variant: "outline", type: "button", onClick: () => setConfirmingCancel(true), children: l10n.billing.cancelSubscription }) : null
3081
+ ] })
2883
3082
  ] })
2884
3083
  );
2885
3084
  }
@@ -2906,16 +3105,16 @@ function BillingSection({
2906
3105
  usageFeatureKeys,
2907
3106
  usageLabels
2908
3107
  }) {
2909
- const { subscription } = _chunk7U3QBMA7cjs.useSubscription.call(void 0, productSlug);
2910
- const { plans, isLoading: plansLoading } = _chunk7U3QBMA7cjs.usePlans.call(void 0, pricingProductIdOrSlug);
2911
- const { openCheckout } = _chunk7U3QBMA7cjs.useCheckout.call(void 0, );
2912
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
2913
- const showError = _chunk7U3QBMA7cjs.useErrorResolver.call(void 0, );
3108
+ const { subscription } = _chunkIMZFZ3P5cjs.useSubscription.call(void 0, productSlug);
3109
+ const { plans, isLoading: plansLoading } = _chunkIMZFZ3P5cjs.usePlans.call(void 0, pricingProductIdOrSlug);
3110
+ const { openCheckout } = _chunkIMZFZ3P5cjs.useCheckout.call(void 0, );
3111
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
3112
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
2914
3113
  const [busy, setBusy] = _react.useState.call(void 0, null);
2915
3114
  const [error, setError] = _react.useState.call(void 0, null);
2916
3115
  const intervals = _react.useMemo.call(void 0, () => intervalsOf(plans), [plans]);
2917
3116
  const [interval, setInterval] = _react.useState.call(void 0, null);
2918
- const activeInterval = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(interval, () => ( _optionalChain([subscription, 'optionalAccess', _56 => _56.plan_price, 'access', _57 => _57.interval]))), () => ( intervals[0])), () => ( null));
3117
+ const activeInterval = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(interval, () => ( _optionalChain([subscription, 'optionalAccess', _61 => _61.plan_price, 'access', _62 => _62.interval]))), () => ( intervals[0])), () => ( null));
2919
3118
  const handleSwitch = async (planPriceId) => {
2920
3119
  setError(null);
2921
3120
  setBusy(planPriceId);
@@ -2969,7 +3168,7 @@ function BillingSection({
2969
3168
  ) : null,
2970
3169
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-plan-list", children: plans.map((plan) => {
2971
3170
  const price = _nullishCoalesce(plan.prices.find((p) => p.interval === activeInterval), () => ( plan.prices[0]));
2972
- const isCurrent = _optionalChain([subscription, 'optionalAccess', _58 => _58.plan, 'access', _59 => _59.slug]) === plan.slug;
3171
+ const isCurrent = _optionalChain([subscription, 'optionalAccess', _63 => _63.plan, 'access', _64 => _64.slug]) === plan.slug;
2973
3172
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
2974
3173
  "div",
2975
3174
  {
@@ -3027,7 +3226,7 @@ function UserProfile({
3027
3226
  appearance,
3028
3227
  className
3029
3228
  }) {
3030
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
3229
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
3031
3230
  const builtins = {
3032
3231
  profile: {
3033
3232
  label: l10n.profile.tabProfile,
@@ -3054,15 +3253,15 @@ function UserProfile({
3054
3253
  description: t.description
3055
3254
  }))
3056
3255
  ];
3057
- const [internalTab, setInternalTab] = _react.useState.call(void 0, _nullishCoalesce(_nullishCoalesce(defaultTab, () => ( _optionalChain([allTabs, 'access', _60 => _60[0], 'optionalAccess', _61 => _61.id]))), () => ( "profile")));
3256
+ const [internalTab, setInternalTab] = _react.useState.call(void 0, _nullishCoalesce(_nullishCoalesce(defaultTab, () => ( _optionalChain([allTabs, 'access', _65 => _65[0], 'optionalAccess', _66 => _66.id]))), () => ( "profile")));
3058
3257
  const current = _nullishCoalesce(controlledTab, () => ( internalTab));
3059
3258
  const select = (id) => {
3060
3259
  if (controlledTab === void 0) setInternalTab(id);
3061
- _optionalChain([onTabChange, 'optionalCall', _62 => _62(id)]);
3260
+ _optionalChain([onTabChange, 'optionalCall', _67 => _67(id)]);
3062
3261
  };
3063
3262
  const active = _nullishCoalesce(allTabs.find((t) => t.id === current), () => ( allTabs[0]));
3064
3263
  const renderPane = () => {
3065
- switch (_optionalChain([active, 'optionalAccess', _63 => _63.id])) {
3264
+ switch (_optionalChain([active, 'optionalAccess', _68 => _68.id])) {
3066
3265
  case "profile":
3067
3266
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ProfileSection, { verificationCallbackUrl, onSaved });
3068
3267
  case "security":
@@ -3083,7 +3282,7 @@ function UserProfile({
3083
3282
  }
3084
3283
  );
3085
3284
  default:
3086
- return _nullishCoalesce(_optionalChain([extraTabs, 'access', _64 => _64.find, 'call', _65 => _65((t) => t.id === _optionalChain([active, 'optionalAccess', _66 => _66.id])), 'optionalAccess', _67 => _67.content]), () => ( null));
3285
+ return _nullishCoalesce(_optionalChain([extraTabs, 'access', _69 => _69.find, 'call', _70 => _70((t) => t.id === _optionalChain([active, 'optionalAccess', _71 => _71.id])), 'optionalAccess', _72 => _72.content]), () => ( null));
3087
3286
  }
3088
3287
  };
3089
3288
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, size: "wide", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-profile", children: [
@@ -3092,7 +3291,7 @@ function UserProfile({
3092
3291
  {
3093
3292
  type: "button",
3094
3293
  className: "kerne-nav-item",
3095
- "aria-current": tab.id === _optionalChain([active, 'optionalAccess', _68 => _68.id]) ? "page" : void 0,
3294
+ "aria-current": tab.id === _optionalChain([active, 'optionalAccess', _73 => _73.id]) ? "page" : void 0,
3096
3295
  onClick: () => select(tab.id),
3097
3296
  children: tab.label
3098
3297
  },
@@ -3108,6 +3307,314 @@ function UserProfile({
3108
3307
  ] }) });
3109
3308
  }
3110
3309
 
3310
+ // src/ui/PricingTable.tsx
3311
+
3312
+
3313
+ function formatEntitlement(e, unlimitedLabel) {
3314
+ if (e.feature_type === "BOOLEAN") return e.feature_name;
3315
+ if (e.feature_type === "STATIC")
3316
+ return e.value ? `${e.feature_name}: ${e.value}` : e.feature_name;
3317
+ if (e.limit === null) return e.feature_name;
3318
+ if (e.limit === -1) return `${unlimitedLabel} ${e.feature_name.toLowerCase()}`;
3319
+ return `${formatNumber(e.limit)} ${e.feature_name.toLowerCase()}`;
3320
+ }
3321
+ function collectLabels(plans) {
3322
+ const bySlug = /* @__PURE__ */ new Map();
3323
+ for (const plan of plans) {
3324
+ for (const price of plan.prices) {
3325
+ if (!bySlug.has(price.label.slug)) bySlug.set(price.label.slug, price.label);
3326
+ }
3327
+ }
3328
+ return Array.from(bySlug.values()).sort(
3329
+ (a, b) => a.sort_order - b.sort_order || a.name.localeCompare(b.name)
3330
+ );
3331
+ }
3332
+ var ENCODED_INTERVAL_MONTHS = { D: 12 / 365, M: 1, Y: 12 };
3333
+ function approxMonths2(value) {
3334
+ const match = /^([1-9]\d*)([DMY])$/.exec(value);
3335
+ if (!match) return 0;
3336
+ return parseInt(match[1], 10) * ENCODED_INTERVAL_MONTHS[match[2]];
3337
+ }
3338
+ function labelSavings(plans, labels) {
3339
+ if (labels.length < 2) return null;
3340
+ const priceFor = (labelSlug) => {
3341
+ for (const plan of plans) {
3342
+ const price = plan.prices.find((p) => p.label.slug === labelSlug);
3343
+ if (price) return price;
3344
+ }
3345
+ return void 0;
3346
+ };
3347
+ const sorted = [...labels].sort(
3348
+ (a, b) => approxMonths2(_nullishCoalesce(_optionalChain([priceFor, 'call', _74 => _74(a.slug), 'optionalAccess', _75 => _75.interval]), () => ( "1M"))) - approxMonths2(_nullishCoalesce(_optionalChain([priceFor, 'call', _76 => _76(b.slug), 'optionalAccess', _77 => _77.interval]), () => ( "1M")))
3349
+ );
3350
+ const shortest = sorted[0];
3351
+ const longest = sorted[sorted.length - 1];
3352
+ if (shortest.slug === longest.slug) return null;
3353
+ for (const plan of plans) {
3354
+ const shortPrice = plan.prices.find((p) => p.label.slug === shortest.slug);
3355
+ const longPrice = plan.prices.find((p) => p.label.slug === longest.slug);
3356
+ if (!_optionalChain([shortPrice, 'optionalAccess', _78 => _78.amount]) || !longPrice) continue;
3357
+ const shortMonths = approxMonths2(shortPrice.interval);
3358
+ const longMonths = approxMonths2(longPrice.interval);
3359
+ if (!shortMonths || !longMonths) continue;
3360
+ const savings = 1 - longPrice.amount / longMonths / (shortPrice.amount / shortMonths);
3361
+ if (savings > 0.01) return { labelSlug: longest.slug, percent: Math.round(savings * 100) };
3362
+ }
3363
+ return null;
3364
+ }
3365
+ var PricingTableContext = _react.createContext.call(void 0, null);
3366
+ function usePricingTable() {
3367
+ const ctx = _react.useContext.call(void 0, PricingTableContext);
3368
+ if (!ctx) {
3369
+ throw new Error("usePricingTable() must be called inside <PricingTable>.");
3370
+ }
3371
+ return ctx;
3372
+ }
3373
+ function PricingTable({
3374
+ product,
3375
+ defaultLabel,
3376
+ checkoutSuccessUrl,
3377
+ checkoutCancelUrl,
3378
+ onSelectPlan,
3379
+ appearance,
3380
+ className,
3381
+ children
3382
+ }) {
3383
+ const { plans, isLoading, error } = _chunkIMZFZ3P5cjs.usePlans.call(void 0, product);
3384
+ const { subscription } = _chunkIMZFZ3P5cjs.useSubscription.call(void 0, product);
3385
+ const { openCheckout } = _chunkIMZFZ3P5cjs.useCheckout.call(void 0, );
3386
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
3387
+ const showError = _chunkIMZFZ3P5cjs.useErrorResolver.call(void 0, );
3388
+ const [selectedLabelSlug, setSelectedLabelSlug] = _react.useState.call(void 0, _nullishCoalesce(defaultLabel, () => ( null)));
3389
+ const [busyPriceId, setBusyPriceId] = _react.useState.call(void 0, null);
3390
+ const [checkoutError, setCheckoutError] = _react.useState.call(void 0, null);
3391
+ const labels = _react.useMemo.call(void 0, () => collectLabels(plans), [plans]);
3392
+ const savings = _react.useMemo.call(void 0, () => labelSavings(plans, labels), [plans, labels]);
3393
+ _react.useEffect.call(void 0, () => {
3394
+ if (labels.length === 0) return;
3395
+ if (selectedLabelSlug && labels.some((l) => l.slug === selectedLabelSlug)) return;
3396
+ setSelectedLabelSlug(labels[0].slug);
3397
+ }, [labels]);
3398
+ const handleSelectPlan = async (plan) => {
3399
+ const price = _nullishCoalesce(plan.prices.find((p) => p.label.slug === selectedLabelSlug), () => ( plan.prices[0]));
3400
+ if (!price) return;
3401
+ if (onSelectPlan) {
3402
+ onSelectPlan(plan, price);
3403
+ return;
3404
+ }
3405
+ setCheckoutError(null);
3406
+ setBusyPriceId(price.id);
3407
+ try {
3408
+ await openCheckout(price.id, {
3409
+ successUrl: checkoutSuccessUrl,
3410
+ cancelUrl: checkoutCancelUrl
3411
+ });
3412
+ } catch (e) {
3413
+ setCheckoutError(showError(e));
3414
+ setBusyPriceId(null);
3415
+ }
3416
+ };
3417
+ const ctx = {
3418
+ plans,
3419
+ labels,
3420
+ selectedLabelSlug,
3421
+ setSelectedLabelSlug,
3422
+ savings,
3423
+ currentPlanId: _nullishCoalesce(_optionalChain([subscription, 'optionalAccess', _79 => _79.plan, 'optionalAccess', _80 => _80.id]), () => ( null)),
3424
+ busyPriceId,
3425
+ onSelectPlan: handleSelectPlan
3426
+ };
3427
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, size: "wide", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-pricing-skeleton", "aria-busy": "true", "aria-label": l10n.pricing.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-skeleton", style: { height: 320 } }) }) : error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: l10n.pricing.loadFailed }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, PricingTableContext.Provider, { value: ctx, children: [
3428
+ checkoutError ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: checkoutError }) : null,
3429
+ children
3430
+ ] }) });
3431
+ }
3432
+ function LabelSwitch() {
3433
+ const { labels, selectedLabelSlug, setSelectedLabelSlug, savings } = usePricingTable();
3434
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
3435
+ const containerRef = _react.useRef.call(void 0, null);
3436
+ const buttonRefs = _react.useRef.call(void 0, /* @__PURE__ */ new Map());
3437
+ const [thumb, setThumb] = _react.useState.call(void 0, null);
3438
+ _react.useEffect.call(void 0, () => {
3439
+ const measure = () => {
3440
+ const container = containerRef.current;
3441
+ const button = selectedLabelSlug ? buttonRefs.current.get(selectedLabelSlug) : null;
3442
+ if (!container || !button) return;
3443
+ const containerBox = container.getBoundingClientRect();
3444
+ const buttonBox = button.getBoundingClientRect();
3445
+ setThumb({ left: buttonBox.left - containerBox.left, width: buttonBox.width });
3446
+ };
3447
+ measure();
3448
+ window.addEventListener("resize", measure);
3449
+ return () => window.removeEventListener("resize", measure);
3450
+ }, [selectedLabelSlug, labels]);
3451
+ if (labels.length < 2) return null;
3452
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-pricing-switch-wrap", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
3453
+ "div",
3454
+ {
3455
+ ref: containerRef,
3456
+ className: "kerne-pricing-switch",
3457
+ role: "group",
3458
+ "aria-label": l10n.billing.intervalGroupLabel,
3459
+ children: [
3460
+ thumb ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3461
+ "span",
3462
+ {
3463
+ className: "kerne-pricing-switch-thumb",
3464
+ style: { transform: `translateX(${thumb.left}px)`, width: `${thumb.width}px` },
3465
+ "aria-hidden": "true"
3466
+ }
3467
+ ) : null,
3468
+ labels.map((label) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
3469
+ "button",
3470
+ {
3471
+ ref: (el) => {
3472
+ if (el) buttonRefs.current.set(label.slug, el);
3473
+ else buttonRefs.current.delete(label.slug);
3474
+ },
3475
+ type: "button",
3476
+ className: "kerne-pricing-switch-option",
3477
+ "aria-pressed": label.slug === selectedLabelSlug,
3478
+ onClick: () => setSelectedLabelSlug(label.slug),
3479
+ children: [
3480
+ label.name,
3481
+ savings && savings.labelSlug === label.slug ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-pricing-switch-save", children: _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.pricing.save, { percent: savings.percent }) }) : null
3482
+ ]
3483
+ },
3484
+ label.slug
3485
+ ))
3486
+ ]
3487
+ }
3488
+ ) });
3489
+ }
3490
+ function useAnimatedAmount(target, durationMs = 420) {
3491
+ const [displayed, setDisplayed] = _react.useState.call(void 0, target);
3492
+ const displayedRef = _react.useRef.call(void 0, target);
3493
+ const frameRef = _react.useRef.call(void 0, );
3494
+ _react.useEffect.call(void 0, () => {
3495
+ if (displayedRef.current === target) return;
3496
+ const reduceMotion = typeof window !== "undefined" && _optionalChain([window, 'access', _81 => _81.matchMedia, 'optionalCall', _82 => _82("(prefers-reduced-motion: reduce)"), 'access', _83 => _83.matches]);
3497
+ if (reduceMotion) {
3498
+ displayedRef.current = target;
3499
+ setDisplayed(target);
3500
+ return;
3501
+ }
3502
+ const from = displayedRef.current;
3503
+ const start = performance.now();
3504
+ const tick = (now) => {
3505
+ const t = Math.min((now - start) / durationMs, 1);
3506
+ const eased = 1 - Math.pow(1 - t, 3);
3507
+ const value = Math.round(from + (target - from) * eased);
3508
+ displayedRef.current = value;
3509
+ setDisplayed(value);
3510
+ if (t < 1) frameRef.current = requestAnimationFrame(tick);
3511
+ };
3512
+ frameRef.current = requestAnimationFrame(tick);
3513
+ return () => {
3514
+ if (frameRef.current) cancelAnimationFrame(frameRef.current);
3515
+ };
3516
+ }, [target, durationMs]);
3517
+ return displayed;
3518
+ }
3519
+ function Plan({ slug, featured, badge, maxFeatures }) {
3520
+ const { plans, selectedLabelSlug, currentPlanId, busyPriceId, onSelectPlan } = usePricingTable();
3521
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
3522
+ const plan = plans.find((p) => p.slug === slug);
3523
+ _react.useEffect.call(void 0, () => {
3524
+ if (!plan && process.env.NODE_ENV === "development") {
3525
+ console.warn(
3526
+ `[Kerne] <PricingTable.Plan slug="${slug}"> - no plan with this slug on the loaded product.`
3527
+ );
3528
+ }
3529
+ }, [plan, slug]);
3530
+ const price = _nullishCoalesce(_optionalChain([plan, 'optionalAccess', _84 => _84.prices, 'access', _85 => _85.find, 'call', _86 => _86((p) => p.label.slug === selectedLabelSlug)]), () => ( _optionalChain([plan, 'optionalAccess', _87 => _87.prices, 'access', _88 => _88[0]])));
3531
+ const resolved = price ? resolvePriceDisplay(price, price.label.display_interval) : null;
3532
+ const animatedAmount = useAnimatedAmount(_nullishCoalesce(_optionalChain([resolved, 'optionalAccess', _89 => _89.displayAmount]), () => ( 0)));
3533
+ if (!plan) return null;
3534
+ const isCurrent = plan.id === currentPlanId;
3535
+ const isBusy = !!price && busyPriceId === price.id;
3536
+ const shownEntitlements = maxFeatures ? plan.entitlements.slice(0, maxFeatures) : plan.entitlements;
3537
+ const remaining = plan.entitlements.length - shownEntitlements.length;
3538
+ const billedCaption = resolved && resolved.displayInterval !== resolved.billedInterval ? _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.pricing.billedAt, {
3539
+ amount: formatMoney(resolved.billedAmount, price.currency),
3540
+ interval: formatInterval(resolved.billedInterval, l10n.billing)
3541
+ }) : null;
3542
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
3543
+ "div",
3544
+ {
3545
+ className: "kerne-pricing-card",
3546
+ "data-featured": featured ? "true" : void 0,
3547
+ "data-current": isCurrent ? "true" : void 0,
3548
+ children: [
3549
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-pricing-card-head", children: [
3550
+ badge ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-pricing-badge", children: badge }) : null,
3551
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-pricing-card-name", children: plan.name }),
3552
+ plan.description ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-pricing-card-desc", children: plan.description }) : null
3553
+ ] }),
3554
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-pricing-price", children: resolved ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
3555
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-pricing-price-row", children: [
3556
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-pricing-amount", children: formatMoney(animatedAmount, price.currency) }),
3557
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "kerne-pricing-interval", children: [
3558
+ "/ ",
3559
+ formatInterval(resolved.displayInterval, l10n.billing)
3560
+ ] })
3561
+ ] }),
3562
+ billedCaption ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-pricing-billed", children: billedCaption }) : null
3563
+ ] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-pricing-billed", children: l10n.billing.notOnInterval }) }),
3564
+ shownEntitlements.length > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "ul", { className: "kerne-pricing-features", children: [
3565
+ shownEntitlements.map((e) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "li", { className: "kerne-pricing-feature", children: [
3566
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CheckIcon2, {}),
3567
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: formatEntitlement(e, l10n.pricing.unlimited) })
3568
+ ] }, e.feature_key)),
3569
+ remaining > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "li", { className: "kerne-pricing-feature-more", children: _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.pricing.moreFeatures, { count: remaining }) }) : null
3570
+ ] }) : null,
3571
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-pricing-cta", children: isCurrent ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { variant: "outline", type: "button", disabled: true, children: l10n.billing.current }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3572
+ Button,
3573
+ {
3574
+ variant: featured ? "primary" : "outline",
3575
+ type: "button",
3576
+ disabled: !price || busyPriceId !== null && !isBusy,
3577
+ loading: isBusy,
3578
+ onClick: () => onSelectPlan(plan),
3579
+ children: currentPlanId ? l10n.billing.switchTo : l10n.pricing.getStarted
3580
+ }
3581
+ ) })
3582
+ ]
3583
+ }
3584
+ );
3585
+ }
3586
+ function CheckIcon2() {
3587
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3588
+ "svg",
3589
+ {
3590
+ width: "15",
3591
+ height: "15",
3592
+ viewBox: "0 0 24 24",
3593
+ fill: "none",
3594
+ stroke: "currentColor",
3595
+ strokeWidth: "2",
3596
+ "aria-hidden": "true",
3597
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m5 13 4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" })
3598
+ }
3599
+ );
3600
+ }
3601
+ function Plans({ featuredSlug, featuredBadge, maxFeatures }) {
3602
+ const { plans } = usePricingTable();
3603
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-pricing-grid", children: plans.map((plan) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3604
+ Plan,
3605
+ {
3606
+ slug: plan.slug,
3607
+ featured: plan.slug === featuredSlug,
3608
+ badge: plan.slug === featuredSlug ? featuredBadge : void 0,
3609
+ maxFeatures
3610
+ },
3611
+ plan.id
3612
+ )) });
3613
+ }
3614
+ PricingTable.LabelSwitch = LabelSwitch;
3615
+ PricingTable.Plan = Plan;
3616
+ PricingTable.Plans = Plans;
3617
+
3111
3618
  // src/ui/CheckoutResult.tsx
3112
3619
 
3113
3620
 
@@ -3125,9 +3632,9 @@ function CheckoutResult({
3125
3632
  logo,
3126
3633
  className
3127
3634
  }) {
3128
- const client = _chunk7U3QBMA7cjs.useClient.call(void 0, );
3129
- const { config } = _chunk7U3QBMA7cjs.useAuthConfig.call(void 0, );
3130
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
3635
+ const client = _chunkIMZFZ3P5cjs.useClient.call(void 0, );
3636
+ const { config } = _chunkIMZFZ3P5cjs.useAuthConfig.call(void 0, );
3637
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
3131
3638
  const [state, setState] = _react.useState.call(void 0, "checking");
3132
3639
  const confirmed = _react.useRef.call(void 0, false);
3133
3640
  _react.useEffect.call(void 0, () => {
@@ -3141,7 +3648,7 @@ function CheckoutResult({
3141
3648
  if (sub && !stopped) {
3142
3649
  confirmed.current = true;
3143
3650
  setState("confirmed");
3144
- _optionalChain([onConfirmed, 'optionalCall', _69 => _69(sub)]);
3651
+ _optionalChain([onConfirmed, 'optionalCall', _90 => _90(sub)]);
3145
3652
  return;
3146
3653
  }
3147
3654
  } catch (e3) {
@@ -3165,7 +3672,7 @@ function CheckoutResult({
3165
3672
  const shell = (children) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Shell, { appearance, className, children: [
3166
3673
  logo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-masthead", children: logo }) : null,
3167
3674
  children,
3168
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KerneBranding, { show: _nullishCoalesce(_optionalChain([config, 'optionalAccess', _70 => _70.show_kerne_branding]), () => ( false)) })
3675
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KerneBranding, { show: _nullishCoalesce(_optionalChain([config, 'optionalAccess', _91 => _91.show_kerne_branding]), () => ( false)) })
3169
3676
  ] });
3170
3677
  if (outcome === "cancel") {
3171
3678
  return shell(
@@ -3211,20 +3718,23 @@ function UpgradePrompt({
3211
3718
  appearance,
3212
3719
  className
3213
3720
  }) {
3214
- const { details, allowed, isLoading } = _chunk7U3QBMA7cjs.useAccess.call(void 0, featureKey, requested);
3215
- const l10n = _chunk7U3QBMA7cjs.useLocalization.call(void 0, );
3721
+ const { details, allowed, isLoading } = _chunkIMZFZ3P5cjs.useAccess.call(void 0, featureKey, requested);
3722
+ const l10n = _chunkIMZFZ3P5cjs.useLocalization.call(void 0, );
3216
3723
  if (isLoading) {
3217
3724
  if (hideWhileLoading) return null;
3218
3725
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-skeleton", style: { height: 80 }, "aria-label": l10n.upgrade.checking }) });
3219
3726
  }
3220
3727
  if (allowed) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children });
3221
3728
  const name = _nullishCoalesce(featureName, () => ( humanizeFeatureKey(featureKey).toLowerCase()));
3222
- const quotaExhausted = _optionalChain([details, 'optionalAccess', _71 => _71.feature_type]) === "QUOTA" && typeof details.limit === "number" && typeof details.used === "number";
3223
- const heading = _nullishCoalesce(title, () => ( _chunk7U3QBMA7cjs.fill.call(void 0, quotaExhausted ? l10n.upgrade.quotaTitle : l10n.upgrade.featureTitle, { feature: name })));
3224
- const body = _nullishCoalesce(description, () => ( (quotaExhausted ? _chunk7U3QBMA7cjs.fill.call(void 0, l10n.upgrade.quotaBody, {
3729
+ const quotaExhausted = _optionalChain([details, 'optionalAccess', _92 => _92.feature_type]) === "QUOTA" && typeof details.limit === "number" && typeof details.used === "number";
3730
+ const heading = _nullishCoalesce(title, () => ( _chunkIMZFZ3P5cjs.fill.call(void 0, quotaExhausted ? l10n.upgrade.quotaTitle : l10n.upgrade.featureTitle, { feature: name })));
3731
+ const body = _nullishCoalesce(description, () => ( (quotaExhausted ? _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.upgrade.quotaBody, {
3225
3732
  used: formatNumber(details.used),
3226
- limit: formatNumber(details.limit)
3227
- }) : _chunk7U3QBMA7cjs.fill.call(void 0, l10n.upgrade.featureBody, { feature: name }))));
3733
+ // `access_limit` is where access actually stops; `limit` is only the
3734
+ // included allocation and can understate it past an overage ceiling.
3735
+ // Falls back to `limit` defensively - should never be needed here.
3736
+ limit: formatNumber(_nullishCoalesce(details.access_limit, () => ( details.limit)))
3737
+ }) : _chunkIMZFZ3P5cjs.fill.call(void 0, l10n.upgrade.featureBody, { feature: name }))));
3228
3738
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-status", children: [
3229
3739
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-heading", children: [
3230
3740
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: "kerne-section-title", style: { fontSize: 17 }, children: heading }),
@@ -3265,4 +3775,6 @@ function UpgradePrompt({
3265
3775
 
3266
3776
 
3267
3777
 
3268
- exports.ActivateAccountForm = ActivateAccountForm; exports.AuthFlow = AuthFlow; exports.BillingSection = BillingSection; exports.ChangePasswordForm = ChangePasswordForm; exports.CheckoutResult = CheckoutResult; exports.ForgotPasswordForm = ForgotPasswordForm; exports.LoginForm = LoginForm; exports.MagicLinkCallback = MagicLinkCallback; exports.ProfileSection = ProfileSection; exports.RegisterForm = RegisterForm; exports.ResetPasswordForm = ResetPasswordForm; exports.SecuritySection = SecuritySection; exports.SignOutButton = SignOutButton; exports.SubscriptionCard = SubscriptionCard; exports.UpgradePrompt = UpgradePrompt; exports.UsageMeters = UsageMeters; exports.UserButton = UserButton; exports.UserProfile = UserProfile; exports.VerifyEmailForm = VerifyEmailForm; exports.WaitlistForm = WaitlistForm; exports.defaultLocalization = _chunk7U3QBMA7cjs.defaultLocalization; exports.useLocalization = _chunk7U3QBMA7cjs.useLocalization;
3778
+
3779
+
3780
+ exports.ActivateAccountForm = ActivateAccountForm; exports.AuthFlow = AuthFlow; exports.BillingSection = BillingSection; exports.ChangePasswordForm = ChangePasswordForm; exports.CheckoutResult = CheckoutResult; exports.ForgotPasswordForm = ForgotPasswordForm; exports.LoginForm = LoginForm; exports.MagicLinkCallback = MagicLinkCallback; exports.PricingTable = PricingTable; exports.ProfileSection = ProfileSection; exports.RegisterForm = RegisterForm; exports.ResetPasswordForm = ResetPasswordForm; exports.SecuritySection = SecuritySection; exports.SignOutButton = SignOutButton; exports.SubscriptionCard = SubscriptionCard; exports.UpgradePrompt = UpgradePrompt; exports.UsageMeters = UsageMeters; exports.UserButton = UserButton; exports.UserProfile = UserProfile; exports.VerifyEmailForm = VerifyEmailForm; exports.WaitlistForm = WaitlistForm; exports.defaultLocalization = _chunkIMZFZ3P5cjs.defaultLocalization; exports.useLocalization = _chunkIMZFZ3P5cjs.useLocalization; exports.usePricingTable = usePricingTable;