@authhero/widget 0.19.2 → 0.21.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.
Files changed (37) hide show
  1. package/dist/authhero-widget/authhero-widget.esm.js +1 -1
  2. package/dist/authhero-widget/index.esm.js +1 -1
  3. package/dist/authhero-widget/{p-5hZ8Vbm9.js → p-BFP_5sHV.js} +1 -1
  4. package/dist/authhero-widget/p-c03e5b57.entry.js +1 -0
  5. package/dist/authhero-widget/p-f3a92b50.entry.js +1 -0
  6. package/dist/cjs/authhero-node.cjs.entry.js +448 -5
  7. package/dist/cjs/authhero-widget.cjs.entry.js +200 -21
  8. package/dist/cjs/authhero-widget.cjs.js +2 -2
  9. package/dist/cjs/{index-vX5cgLV-.js → index-CUBT14z-.js} +21 -0
  10. package/dist/cjs/index.cjs.js +1 -1
  11. package/dist/cjs/loader.cjs.js +2 -2
  12. package/dist/collection/components/authhero-node/authhero-node.css +74 -0
  13. package/dist/collection/components/authhero-node/authhero-node.js +219 -6
  14. package/dist/collection/components/authhero-widget/authhero-widget.css +2 -2
  15. package/dist/collection/components/authhero-widget/authhero-widget.js +102 -5
  16. package/dist/collection/utils/branding.js +98 -16
  17. package/dist/collection/utils/country-data.js +235 -0
  18. package/dist/components/authhero-node.js +1 -1
  19. package/dist/components/authhero-widget.js +1 -1
  20. package/dist/components/index.js +1 -1
  21. package/dist/components/p-D9Clv9VJ.js +1 -0
  22. package/dist/components/{p-EokuR0qI.js → p-DpfoRsj0.js} +1 -1
  23. package/dist/esm/authhero-node.entry.js +448 -5
  24. package/dist/esm/authhero-widget.entry.js +200 -21
  25. package/dist/esm/authhero-widget.js +3 -3
  26. package/dist/esm/{index-5hZ8Vbm9.js → index-BFP_5sHV.js} +21 -0
  27. package/dist/esm/index.js +1 -1
  28. package/dist/esm/loader.js +3 -3
  29. package/dist/types/components/authhero-node/authhero-node.d.ts +36 -0
  30. package/dist/types/components/authhero-widget/authhero-widget.d.ts +20 -1
  31. package/dist/types/utils/country-data.d.ts +23 -0
  32. package/hydrate/index.js +668 -25
  33. package/hydrate/index.mjs +668 -25
  34. package/package.json +2 -2
  35. package/dist/authhero-widget/p-18d0a438.entry.js +0 -1
  36. package/dist/authhero-widget/p-bb3657ce.entry.js +0 -1
  37. package/dist/components/p-NKUIiiOq.js +0 -1
package/hydrate/index.js CHANGED
@@ -3264,6 +3264,23 @@ var setAccessor = (elm, memberName, oldValue, newValue, isSvg, flags, initialRen
3264
3264
  classList.remove(...oldClasses.filter((c) => c && !newClasses.includes(c)));
3265
3265
  classList.add(...newClasses.filter((c) => c && !oldClasses.includes(c)));
3266
3266
  }
3267
+ } else if (memberName === "style") {
3268
+ {
3269
+ for (const prop in oldValue) {
3270
+ if (!newValue || newValue[prop] == null) {
3271
+ {
3272
+ elm.style[prop] = "";
3273
+ }
3274
+ }
3275
+ }
3276
+ }
3277
+ for (const prop in newValue) {
3278
+ if (!oldValue || newValue[prop] !== oldValue[prop]) {
3279
+ {
3280
+ elm.style[prop] = newValue[prop];
3281
+ }
3282
+ }
3283
+ }
3267
3284
  } else if (memberName === "key") ; else if ((!isProp ) && memberName[0] === "o" && memberName[1] === "n") {
3268
3285
  if (memberName[2] === "-") {
3269
3286
  memberName = memberName.slice(3);
@@ -4947,7 +4964,243 @@ var setScopedSSR = (opts) => {
4947
4964
  var needsScopedSSR = () => scopedSSR;
4948
4965
  var scopedSSR = false;
4949
4966
 
4950
- const authheroNodeCss = () => `:host{display:block}.input-wrapper{display:flex;flex-direction:column;position:relative;margin-top:8px;margin-bottom:16px}.input-container{position:relative;width:100%}.input-label{position:absolute;left:16px;top:50%;transform:translateY(-50%);font-size:16px;font-weight:var(--ah-font-weight-label, 400);color:var(--ah-color-text-muted, #65676e);pointer-events:none;transition:all 0.15s ease-out;background-color:transparent;padding:0;z-index:1}.input-label.floating,.input-field:focus+.input-label,.input-field:not(:placeholder-shown)+.input-label,select.input-field+.input-label,input[type="date"].input-field+.input-label{top:-8px;transform:translateY(0);font-size:12px;background-color:var(--ah-color-bg, #ffffff);padding:0 4px;left:12px;color:var(--ah-color-text-muted, #65676e)}.input-field:focus+.input-label{color:var(--ah-color-primary, #635dff)}.required{color:var(--ah-color-error, #d03c38);margin-left:2px}.input-field{width:100%;padding:16px;font-size:16px;font-family:inherit;color:var(--ah-color-text, #1e212a);background-color:var(--ah-color-input-bg, #ffffff);border:1px solid var(--ah-color-border, #c9cace);border-radius:var(--ah-input-radius, 3px);outline:none;transition:border-color 0.15s ease-out,\\n box-shadow 0.15s ease-out;box-sizing:border-box}.input-field::placeholder{color:transparent}.input-field:hover{border-color:var(--ah-color-border-hover, #65676e)}.input-field:focus{border-color:var(--ah-color-primary, #635dff);box-shadow:inset 0 0 0 1px var(--ah-color-primary, #635dff)}.input-field.has-error{border-color:var(--ah-color-error, #d03c38)}.input-field.has-error:focus{box-shadow:inset 0 0 0 1px var(--ah-color-error, #d03c38)}.input-field:disabled{background-color:var(--ah-color-bg-disabled, #f5f5f5);border-color:var(--ah-color-border-disabled, #e0e1e3);cursor:not-allowed;opacity:0.7}.password-container{position:relative;display:flex;align-items:center}.password-container .input-field{padding-right:48px}.password-toggle{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--ah-color-text-muted, #65676e);transition:color 0.15s ease}.password-toggle:hover{color:var(--ah-color-text, #1e212a)}.password-toggle svg{width:20px;height:20px}.error-text{font-size:12px;color:var(--ah-color-error, #d03c38);margin-top:4px;line-height:1.4}.helper-text{font-size:12px;color:var(--ah-color-text-muted, #65676e);margin-top:4px;line-height:1.4}.field-link{display:block;text-align:left;margin-top:8px;margin-bottom:16px}.field-link a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400)}.field-link a:hover{text-decoration:underline}.checkbox-wrapper{display:flex;align-items:flex-start;gap:10px;cursor:pointer;margin-bottom:16px}.checkbox-wrapper input[type="checkbox"]{width:18px;height:18px;margin:0;accent-color:var(--ah-color-primary, #635dff);cursor:pointer;flex-shrink:0}.checkbox-label{font-size:14px;color:var(--ah-color-text, #1e212a);line-height:1.5}.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:14px 20px;font-size:16px;font-weight:var(--ah-font-weight-btn, 400);font-family:inherit;line-height:1.25;text-align:center;text-decoration:none;border:none;border-radius:var(--ah-btn-radius, 3px);cursor:pointer;transition:background-color 0.15s ease,\\n border-color 0.15s ease,\\n transform 0.1s ease;box-sizing:border-box}.btn:disabled{opacity:0.6;cursor:not-allowed}.btn:not(:disabled):active{transform:scale(0.98)}.btn:focus-visible{outline:2px solid var(--ah-color-primary, #635dff);outline-offset:2px}.btn-primary{background-color:var(--ah-color-primary, #635dff);color:var(--ah-color-text-on-primary, #ffffff);margin-top:12px}.btn-primary:not(:disabled):hover{filter:brightness(0.85)}.btn-secondary{background-color:var(--ah-color-bg, #ffffff);color:var(--ah-color-text, #1e212a);border:1px solid var(--ah-color-border, #c9cace)}.btn-secondary:not(:disabled):hover{background-color:var(--ah-color-bg-hover, #f5f5f5);border-color:var(--ah-color-border-hover, #65676e)}.btn-link{background:none;border:none;color:var(--ah-color-link, #635dff);padding:8px 0;font-weight:var(--ah-font-weight-link, 400);text-decoration:none}.btn-link:hover{text-decoration:underline}.social-buttons{display:flex;flex-direction:column;gap:12px}.btn-social{display:flex;align-items:center;justify-content:center;gap:12px}.btn-social-content{display:flex;flex-direction:column;align-items:center;text-align:center}.btn-social-subtitle{font-size:12px;font-style:italic;opacity:0.8;margin-top:2px}.btn-social-subtitle:empty{display:none}.social-icon{width:20px;height:20px;flex-shrink:0}@media (max-width: 480px){.social-buttons:has(.btn-social:nth-child(3)){flex-direction:row;flex-wrap:nowrap;justify-content:stretch;gap:8px}.social-buttons:has(.btn-social:nth-child(3)) .btn-social{width:auto;min-width:0;padding:12px;flex:1 1 0}.social-buttons:has(.btn-social:nth-child(3)) .btn-social span{display:none}.social-buttons:has(.btn-social:nth-child(3)) .social-icon{width:24px;height:24px}}.btn-icon{width:20px;height:20px;flex-shrink:0}.btn-icon img{width:100%;height:100%;object-fit:contain}.text-title{font-size:20px;font-weight:400;color:var(--ah-color-text, #1e212a);margin:8px 0;line-height:1.3}.text-title.text-success{color:var(--ah-color-success, #13a769)}.text-description{font-size:14px;color:var(--ah-color-text-muted, #65676e);margin:4px 0;line-height:1.5}.image{display:block;max-width:100%;height:auto;border-radius:4px}.image-centered{margin:0 auto 16px;width:52px;height:52px;object-fit:contain}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;transition:color 0.15s ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.node-error{padding:12px 16px;background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38);border-radius:0;font-size:14px;margin-bottom:16px}.node-success{padding:12px 16px;background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769);border-radius:0;font-size:14px;margin-bottom:16px}.divider{display:flex;align-items:center;text-align:center;margin:16px 0}.divider::before,.divider::after{content:"";flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0.5px}.rich-text{font-size:14px;line-height:1.5;color:var(--ah-color-text, #1e212a)}.rich-text a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);transition:color 0.15s ease}.rich-text a:hover{text-decoration:underline}.rich-text .terms-text{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.rich-text .terms-text a{font-size:12px}.rich-text .forgot-password-link{text-align:right;font-size:13px;margin-top:4px}.rich-text .forgot-password-link a{font-size:13px;color:var(--ah-color-link, #635dff)}.rich-text .signup-link{margin-top:16px;text-align:center;font-size:14px;color:var(--ah-color-text, #1e212a)}`;
4967
+ /**
4968
+ * Country data for phone number input with country code selector.
4969
+ * Each entry contains:
4970
+ * - code: ISO 3166-1 alpha-2 country code
4971
+ * - dialCode: International dialing prefix
4972
+ * - flag: Unicode flag emoji
4973
+ * - name: Country name in English
4974
+ */
4975
+ /**
4976
+ * Get flag emoji from ISO 3166-1 alpha-2 country code.
4977
+ * Uses Unicode Regional Indicator Symbol Letters.
4978
+ */
4979
+ function flagEmoji(code) {
4980
+ return String.fromCodePoint(...code
4981
+ .toUpperCase()
4982
+ .split("")
4983
+ .map((c) => 0x1f1e6 - 65 + c.charCodeAt(0)));
4984
+ }
4985
+ /**
4986
+ * Full list of countries with dial codes, sorted by dial code (numerically)
4987
+ * with secondary sort by country code for ties.
4988
+ */
4989
+ const countries = [
4990
+ { code: "US", dialCode: "+1", name: "United States" },
4991
+ { code: "CA", dialCode: "+1", name: "Canada" },
4992
+ { code: "AG", dialCode: "+1", name: "Antigua and Barbuda" },
4993
+ { code: "BS", dialCode: "+1", name: "Bahamas" },
4994
+ { code: "BB", dialCode: "+1", name: "Barbados" },
4995
+ { code: "DM", dialCode: "+1", name: "Dominica" },
4996
+ { code: "DO", dialCode: "+1", name: "Dominican Republic" },
4997
+ { code: "GD", dialCode: "+1", name: "Grenada" },
4998
+ { code: "JM", dialCode: "+1", name: "Jamaica" },
4999
+ { code: "KN", dialCode: "+1", name: "Saint Kitts and Nevis" },
5000
+ { code: "LC", dialCode: "+1", name: "Saint Lucia" },
5001
+ { code: "VC", dialCode: "+1", name: "Saint Vincent and the Grenadines" },
5002
+ { code: "TT", dialCode: "+1", name: "Trinidad and Tobago" },
5003
+ { code: "PR", dialCode: "+1", name: "Puerto Rico" },
5004
+ { code: "KZ", dialCode: "+7", name: "Kazakhstan" },
5005
+ { code: "RU", dialCode: "+7", name: "Russia" },
5006
+ { code: "EG", dialCode: "+20", name: "Egypt" },
5007
+ { code: "ZA", dialCode: "+27", name: "South Africa" },
5008
+ { code: "GR", dialCode: "+30", name: "Greece" },
5009
+ { code: "NL", dialCode: "+31", name: "Netherlands" },
5010
+ { code: "BE", dialCode: "+32", name: "Belgium" },
5011
+ { code: "FR", dialCode: "+33", name: "France" },
5012
+ { code: "ES", dialCode: "+34", name: "Spain" },
5013
+ { code: "HU", dialCode: "+36", name: "Hungary" },
5014
+ { code: "IT", dialCode: "+39", name: "Italy" },
5015
+ { code: "RO", dialCode: "+40", name: "Romania" },
5016
+ { code: "CH", dialCode: "+41", name: "Switzerland" },
5017
+ { code: "AT", dialCode: "+43", name: "Austria" },
5018
+ { code: "GB", dialCode: "+44", name: "United Kingdom" },
5019
+ { code: "DK", dialCode: "+45", name: "Denmark" },
5020
+ { code: "SE", dialCode: "+46", name: "Sweden" },
5021
+ { code: "NO", dialCode: "+47", name: "Norway" },
5022
+ { code: "PL", dialCode: "+48", name: "Poland" },
5023
+ { code: "DE", dialCode: "+49", name: "Germany" },
5024
+ { code: "PE", dialCode: "+51", name: "Peru" },
5025
+ { code: "MX", dialCode: "+52", name: "Mexico" },
5026
+ { code: "CU", dialCode: "+53", name: "Cuba" },
5027
+ { code: "AR", dialCode: "+54", name: "Argentina" },
5028
+ { code: "BR", dialCode: "+55", name: "Brazil" },
5029
+ { code: "CL", dialCode: "+56", name: "Chile" },
5030
+ { code: "CO", dialCode: "+57", name: "Colombia" },
5031
+ { code: "VE", dialCode: "+58", name: "Venezuela" },
5032
+ { code: "MY", dialCode: "+60", name: "Malaysia" },
5033
+ { code: "AU", dialCode: "+61", name: "Australia" },
5034
+ { code: "ID", dialCode: "+62", name: "Indonesia" },
5035
+ { code: "PH", dialCode: "+63", name: "Philippines" },
5036
+ { code: "NZ", dialCode: "+64", name: "New Zealand" },
5037
+ { code: "SG", dialCode: "+65", name: "Singapore" },
5038
+ { code: "TH", dialCode: "+66", name: "Thailand" },
5039
+ { code: "JP", dialCode: "+81", name: "Japan" },
5040
+ { code: "KR", dialCode: "+82", name: "South Korea" },
5041
+ { code: "VN", dialCode: "+84", name: "Vietnam" },
5042
+ { code: "CN", dialCode: "+86", name: "China" },
5043
+ { code: "TR", dialCode: "+90", name: "Turkey" },
5044
+ { code: "IN", dialCode: "+91", name: "India" },
5045
+ { code: "PK", dialCode: "+92", name: "Pakistan" },
5046
+ { code: "AF", dialCode: "+93", name: "Afghanistan" },
5047
+ { code: "LK", dialCode: "+94", name: "Sri Lanka" },
5048
+ { code: "MM", dialCode: "+95", name: "Myanmar" },
5049
+ { code: "IR", dialCode: "+98", name: "Iran" },
5050
+ { code: "SS", dialCode: "+211", name: "South Sudan" },
5051
+ { code: "MA", dialCode: "+212", name: "Morocco" },
5052
+ { code: "DZ", dialCode: "+213", name: "Algeria" },
5053
+ { code: "TN", dialCode: "+216", name: "Tunisia" },
5054
+ { code: "LY", dialCode: "+218", name: "Libya" },
5055
+ { code: "GM", dialCode: "+220", name: "Gambia" },
5056
+ { code: "SN", dialCode: "+221", name: "Senegal" },
5057
+ { code: "MR", dialCode: "+222", name: "Mauritania" },
5058
+ { code: "ML", dialCode: "+223", name: "Mali" },
5059
+ { code: "GN", dialCode: "+224", name: "Guinea" },
5060
+ { code: "CI", dialCode: "+225", name: "Ivory Coast" },
5061
+ { code: "BF", dialCode: "+226", name: "Burkina Faso" },
5062
+ { code: "NE", dialCode: "+227", name: "Niger" },
5063
+ { code: "TG", dialCode: "+228", name: "Togo" },
5064
+ { code: "BJ", dialCode: "+229", name: "Benin" },
5065
+ { code: "MU", dialCode: "+230", name: "Mauritius" },
5066
+ { code: "LR", dialCode: "+231", name: "Liberia" },
5067
+ { code: "SL", dialCode: "+232", name: "Sierra Leone" },
5068
+ { code: "GH", dialCode: "+233", name: "Ghana" },
5069
+ { code: "NG", dialCode: "+234", name: "Nigeria" },
5070
+ { code: "TD", dialCode: "+235", name: "Chad" },
5071
+ { code: "CF", dialCode: "+236", name: "Central African Republic" },
5072
+ { code: "CM", dialCode: "+237", name: "Cameroon" },
5073
+ { code: "CV", dialCode: "+238", name: "Cape Verde" },
5074
+ { code: "ST", dialCode: "+239", name: "São Tomé and Príncipe" },
5075
+ { code: "GQ", dialCode: "+240", name: "Equatorial Guinea" },
5076
+ { code: "GA", dialCode: "+241", name: "Gabon" },
5077
+ { code: "CG", dialCode: "+242", name: "Congo" },
5078
+ { code: "CD", dialCode: "+243", name: "Congo (DRC)" },
5079
+ { code: "AO", dialCode: "+244", name: "Angola" },
5080
+ { code: "GW", dialCode: "+245", name: "Guinea-Bissau" },
5081
+ { code: "SC", dialCode: "+248", name: "Seychelles" },
5082
+ { code: "SD", dialCode: "+249", name: "Sudan" },
5083
+ { code: "RW", dialCode: "+250", name: "Rwanda" },
5084
+ { code: "ET", dialCode: "+251", name: "Ethiopia" },
5085
+ { code: "SO", dialCode: "+252", name: "Somalia" },
5086
+ { code: "DJ", dialCode: "+253", name: "Djibouti" },
5087
+ { code: "KE", dialCode: "+254", name: "Kenya" },
5088
+ { code: "TZ", dialCode: "+255", name: "Tanzania" },
5089
+ { code: "UG", dialCode: "+256", name: "Uganda" },
5090
+ { code: "BI", dialCode: "+257", name: "Burundi" },
5091
+ { code: "MZ", dialCode: "+258", name: "Mozambique" },
5092
+ { code: "ZM", dialCode: "+260", name: "Zambia" },
5093
+ { code: "MG", dialCode: "+261", name: "Madagascar" },
5094
+ { code: "RE", dialCode: "+262", name: "Réunion" },
5095
+ { code: "ZW", dialCode: "+263", name: "Zimbabwe" },
5096
+ { code: "NA", dialCode: "+264", name: "Namibia" },
5097
+ { code: "MW", dialCode: "+265", name: "Malawi" },
5098
+ { code: "LS", dialCode: "+266", name: "Lesotho" },
5099
+ { code: "BW", dialCode: "+267", name: "Botswana" },
5100
+ { code: "SZ", dialCode: "+268", name: "Eswatini" },
5101
+ { code: "KM", dialCode: "+269", name: "Comoros" },
5102
+ { code: "ER", dialCode: "+291", name: "Eritrea" },
5103
+ { code: "PT", dialCode: "+351", name: "Portugal" },
5104
+ { code: "LU", dialCode: "+352", name: "Luxembourg" },
5105
+ { code: "IE", dialCode: "+353", name: "Ireland" },
5106
+ { code: "IS", dialCode: "+354", name: "Iceland" },
5107
+ { code: "AL", dialCode: "+355", name: "Albania" },
5108
+ { code: "MT", dialCode: "+356", name: "Malta" },
5109
+ { code: "CY", dialCode: "+357", name: "Cyprus" },
5110
+ { code: "FI", dialCode: "+358", name: "Finland" },
5111
+ { code: "BG", dialCode: "+359", name: "Bulgaria" },
5112
+ { code: "LT", dialCode: "+370", name: "Lithuania" },
5113
+ { code: "LV", dialCode: "+371", name: "Latvia" },
5114
+ { code: "EE", dialCode: "+372", name: "Estonia" },
5115
+ { code: "MD", dialCode: "+373", name: "Moldova" },
5116
+ { code: "AM", dialCode: "+374", name: "Armenia" },
5117
+ { code: "BY", dialCode: "+375", name: "Belarus" },
5118
+ { code: "AD", dialCode: "+376", name: "Andorra" },
5119
+ { code: "MC", dialCode: "+377", name: "Monaco" },
5120
+ { code: "SM", dialCode: "+378", name: "San Marino" },
5121
+ { code: "UA", dialCode: "+380", name: "Ukraine" },
5122
+ { code: "RS", dialCode: "+381", name: "Serbia" },
5123
+ { code: "ME", dialCode: "+382", name: "Montenegro" },
5124
+ { code: "XK", dialCode: "+383", name: "Kosovo" },
5125
+ { code: "HR", dialCode: "+385", name: "Croatia" },
5126
+ { code: "SI", dialCode: "+386", name: "Slovenia" },
5127
+ { code: "BA", dialCode: "+387", name: "Bosnia and Herzegovina" },
5128
+ { code: "MK", dialCode: "+389", name: "North Macedonia" },
5129
+ { code: "CZ", dialCode: "+420", name: "Czech Republic" },
5130
+ { code: "SK", dialCode: "+421", name: "Slovakia" },
5131
+ { code: "LI", dialCode: "+423", name: "Liechtenstein" },
5132
+ { code: "FK", dialCode: "+500", name: "Falkland Islands" },
5133
+ { code: "BZ", dialCode: "+501", name: "Belize" },
5134
+ { code: "GT", dialCode: "+502", name: "Guatemala" },
5135
+ { code: "SV", dialCode: "+503", name: "El Salvador" },
5136
+ { code: "HN", dialCode: "+504", name: "Honduras" },
5137
+ { code: "NI", dialCode: "+505", name: "Nicaragua" },
5138
+ { code: "CR", dialCode: "+506", name: "Costa Rica" },
5139
+ { code: "PA", dialCode: "+507", name: "Panama" },
5140
+ { code: "HT", dialCode: "+509", name: "Haiti" },
5141
+ { code: "GP", dialCode: "+590", name: "Guadeloupe" },
5142
+ { code: "BO", dialCode: "+591", name: "Bolivia" },
5143
+ { code: "GY", dialCode: "+592", name: "Guyana" },
5144
+ { code: "EC", dialCode: "+593", name: "Ecuador" },
5145
+ { code: "GF", dialCode: "+594", name: "French Guiana" },
5146
+ { code: "PY", dialCode: "+595", name: "Paraguay" },
5147
+ { code: "MQ", dialCode: "+596", name: "Martinique" },
5148
+ { code: "SR", dialCode: "+597", name: "Suriname" },
5149
+ { code: "UY", dialCode: "+598", name: "Uruguay" },
5150
+ { code: "TL", dialCode: "+670", name: "Timor-Leste" },
5151
+ { code: "BN", dialCode: "+673", name: "Brunei" },
5152
+ { code: "NR", dialCode: "+674", name: "Nauru" },
5153
+ { code: "PG", dialCode: "+675", name: "Papua New Guinea" },
5154
+ { code: "TO", dialCode: "+676", name: "Tonga" },
5155
+ { code: "SB", dialCode: "+677", name: "Solomon Islands" },
5156
+ { code: "VU", dialCode: "+678", name: "Vanuatu" },
5157
+ { code: "FJ", dialCode: "+679", name: "Fiji" },
5158
+ { code: "PW", dialCode: "+680", name: "Palau" },
5159
+ { code: "WS", dialCode: "+685", name: "Samoa" },
5160
+ { code: "KI", dialCode: "+686", name: "Kiribati" },
5161
+ { code: "FM", dialCode: "+691", name: "Micronesia" },
5162
+ { code: "MH", dialCode: "+692", name: "Marshall Islands" },
5163
+ { code: "KP", dialCode: "+850", name: "North Korea" },
5164
+ { code: "HK", dialCode: "+852", name: "Hong Kong" },
5165
+ { code: "MO", dialCode: "+853", name: "Macau" },
5166
+ { code: "KH", dialCode: "+855", name: "Cambodia" },
5167
+ { code: "LA", dialCode: "+856", name: "Laos" },
5168
+ { code: "BD", dialCode: "+880", name: "Bangladesh" },
5169
+ { code: "TW", dialCode: "+886", name: "Taiwan" },
5170
+ { code: "MV", dialCode: "+960", name: "Maldives" },
5171
+ { code: "LB", dialCode: "+961", name: "Lebanon" },
5172
+ { code: "JO", dialCode: "+962", name: "Jordan" },
5173
+ { code: "SY", dialCode: "+963", name: "Syria" },
5174
+ { code: "IQ", dialCode: "+964", name: "Iraq" },
5175
+ { code: "KW", dialCode: "+965", name: "Kuwait" },
5176
+ { code: "SA", dialCode: "+966", name: "Saudi Arabia" },
5177
+ { code: "YE", dialCode: "+967", name: "Yemen" },
5178
+ { code: "OM", dialCode: "+968", name: "Oman" },
5179
+ { code: "PS", dialCode: "+970", name: "Palestine" },
5180
+ { code: "AE", dialCode: "+971", name: "United Arab Emirates" },
5181
+ { code: "IL", dialCode: "+972", name: "Israel" },
5182
+ { code: "BH", dialCode: "+973", name: "Bahrain" },
5183
+ { code: "QA", dialCode: "+974", name: "Qatar" },
5184
+ { code: "BT", dialCode: "+975", name: "Bhutan" },
5185
+ { code: "MN", dialCode: "+976", name: "Mongolia" },
5186
+ { code: "NP", dialCode: "+977", name: "Nepal" },
5187
+ { code: "TJ", dialCode: "+992", name: "Tajikistan" },
5188
+ { code: "TM", dialCode: "+993", name: "Turkmenistan" },
5189
+ { code: "AZ", dialCode: "+994", name: "Azerbaijan" },
5190
+ { code: "GE", dialCode: "+995", name: "Georgia" },
5191
+ { code: "KG", dialCode: "+996", name: "Kyrgyzstan" },
5192
+ { code: "UZ", dialCode: "+998", name: "Uzbekistan" },
5193
+ ].map((c) => ({ ...c, flag: flagEmoji(c.code) }));
5194
+ /**
5195
+ * Find a country by its ISO code, defaulting to US.
5196
+ */
5197
+ function getCountryByCode(code) {
5198
+ const upper = code.toUpperCase();
5199
+ return (countries.find((c) => c.code === upper) ||
5200
+ countries.find((c) => c.code === "US"));
5201
+ }
5202
+
5203
+ const authheroNodeCss = () => `:host{display:block}.input-wrapper{display:flex;flex-direction:column;position:relative;margin-top:8px;margin-bottom:16px}.input-container{position:relative;width:100%}.input-label{position:absolute;left:16px;top:50%;transform:translateY(-50%);font-size:16px;font-weight:var(--ah-font-weight-label, 400);color:var(--ah-color-text-muted, #65676e);pointer-events:none;transition:all 0.15s ease-out;background-color:transparent;padding:0;z-index:1}.input-label.floating,.input-field:focus+.input-label,.input-field:not(:placeholder-shown)+.input-label,select.input-field+.input-label,input[type="date"].input-field+.input-label{top:-8px;transform:translateY(0);font-size:12px;background-color:var(--ah-color-bg, #ffffff);padding:0 4px;left:12px;color:var(--ah-color-text-muted, #65676e)}.input-field:focus+.input-label{color:var(--ah-color-primary, #635dff)}.required{color:var(--ah-color-error, #d03c38);margin-left:2px}.input-field{width:100%;padding:16px;font-size:16px;font-family:inherit;color:var(--ah-color-text, #1e212a);background-color:var(--ah-color-input-bg, #ffffff);border:1px solid var(--ah-color-border, #c9cace);border-radius:var(--ah-input-radius, 3px);outline:none;transition:border-color 0.15s ease-out,\\n box-shadow 0.15s ease-out;box-sizing:border-box}.input-field::placeholder{color:transparent}.input-field:hover{border-color:var(--ah-color-border-hover, #65676e)}.input-field:focus{border-color:var(--ah-color-primary, #635dff);box-shadow:inset 0 0 0 1px var(--ah-color-primary, #635dff)}.input-field.has-error{border-color:var(--ah-color-error, #d03c38)}.input-field.has-error:focus{box-shadow:inset 0 0 0 1px var(--ah-color-error, #d03c38)}.input-field:disabled{background-color:var(--ah-color-bg-disabled, #f5f5f5);border-color:var(--ah-color-border-disabled, #e0e1e3);cursor:not-allowed;opacity:0.7}.password-container{position:relative;display:flex;align-items:center}.password-container .input-field{padding-right:48px}.password-toggle{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--ah-color-text-muted, #65676e);transition:color 0.15s ease}.password-toggle:hover{color:var(--ah-color-text, #1e212a)}.password-toggle svg{width:20px;height:20px}.error-text{font-size:12px;color:var(--ah-color-error, #d03c38);margin-top:4px;line-height:1.4}.helper-text{font-size:12px;color:var(--ah-color-text-muted, #65676e);margin-top:4px;line-height:1.4}.field-link{display:block;text-align:left;margin-top:8px;margin-bottom:16px}.field-link a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400)}.field-link a:hover{text-decoration:underline}.checkbox-wrapper{display:flex;align-items:flex-start;gap:10px;cursor:pointer;margin-bottom:16px}.checkbox-wrapper input[type="checkbox"]{width:18px;height:18px;margin:0;accent-color:var(--ah-color-primary, #635dff);cursor:pointer;flex-shrink:0}.checkbox-label{font-size:14px;color:var(--ah-color-text, #1e212a);line-height:1.5}.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:14px 20px;font-size:16px;font-weight:var(--ah-font-weight-btn, 400);font-family:inherit;line-height:1.25;text-align:center;text-decoration:none;border:none;border-radius:var(--ah-btn-radius, 3px);cursor:pointer;transition:background-color 0.15s ease,\\n border-color 0.15s ease,\\n transform 0.1s ease;box-sizing:border-box}.btn:disabled{opacity:0.6;cursor:not-allowed}.btn:not(:disabled):active{transform:scale(0.98)}.btn:focus-visible{outline:2px solid var(--ah-color-primary, #635dff);outline-offset:2px}.btn-primary{background-color:var(--ah-color-primary, #635dff);color:var(--ah-color-text-on-primary, #ffffff);margin-top:12px}.btn-primary:not(:disabled):hover{filter:brightness(0.85)}.btn-secondary{background-color:var(--ah-color-bg, #ffffff);color:var(--ah-color-text, #1e212a);border:1px solid var(--ah-color-border, #c9cace)}.btn-secondary:not(:disabled):hover{background-color:var(--ah-color-bg-hover, #f5f5f5);border-color:var(--ah-color-border-hover, #65676e)}.btn-link{background:none;border:none;color:var(--ah-color-link, #635dff);padding:8px 0;font-weight:var(--ah-font-weight-link, 400);text-decoration:none}.btn-link:hover{text-decoration:underline}.phone-input-wrapper{display:flex;align-items:stretch;gap:0;width:100%}.country-select{flex-shrink:0;width:auto;padding:12px 8px;font-size:var(--ah-font-size-body, 16px);font-family:var(--ah-font-family, inherit);border:1px solid var(--ah-color-border, #c9cace);border-right:none;border-radius:var(--ah-border-radius-input, 4px) 0 0 var(--ah-border-radius-input, 4px);background-color:var(--ah-color-surface, #fff);color:var(--ah-color-text, #1e212a);cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 6px center;padding-right:22px}.country-select:focus{outline:none;border-color:var(--ah-color-primary, #635dff);box-shadow:0 0 0 1px var(--ah-color-primary, #635dff)}.country-select:disabled{opacity:0.5;cursor:not-allowed}.phone-input-wrapper .input-container{flex:1;min-width:0}.phone-input-wrapper .input-field{border-radius:0 var(--ah-border-radius-input, 4px) var(--ah-border-radius-input, 4px) 0}.phone-input-wrapper .input-label.floating,.phone-input-wrapper .input-field:focus+.input-label,.phone-input-wrapper .input-field:not(:placeholder-shown)+.input-label{left:8px}.social-buttons{display:flex;flex-direction:column;gap:12px}.btn-social{display:flex;align-items:center;justify-content:center;gap:12px}.btn-social-content{display:flex;flex-direction:column;align-items:center;text-align:center}.btn-social-subtitle{font-size:12px;font-style:italic;opacity:0.8;margin-top:2px}.btn-social-subtitle:empty{display:none}.social-icon{width:20px;height:20px;flex-shrink:0}@media (max-width: 480px){.social-buttons:has(.btn-social:nth-child(3)){flex-direction:row;flex-wrap:nowrap;justify-content:stretch;gap:8px}.social-buttons:has(.btn-social:nth-child(3)) .btn-social{width:auto;min-width:0;padding:12px;flex:1 1 0}.social-buttons:has(.btn-social:nth-child(3)) .btn-social span{display:none}.social-buttons:has(.btn-social:nth-child(3)) .social-icon{width:24px;height:24px}}.btn-icon{width:20px;height:20px;flex-shrink:0}.btn-icon img{width:100%;height:100%;object-fit:contain}.text-title{font-size:20px;font-weight:400;color:var(--ah-color-text, #1e212a);margin:8px 0;line-height:1.3}.text-title.text-success{color:var(--ah-color-success, #13a769)}.text-description{font-size:14px;color:var(--ah-color-text-muted, #65676e);margin:4px 0;line-height:1.5}.image{display:block;max-width:100%;height:auto;border-radius:4px}.image-centered{margin:0 auto 16px;width:52px;height:52px;object-fit:contain}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;transition:color 0.15s ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.node-error{padding:12px 16px;background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38);border-radius:0;font-size:14px;margin-bottom:16px}.node-success{padding:12px 16px;background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769);border-radius:0;font-size:14px;margin-bottom:16px}.divider{display:flex;align-items:center;text-align:center;margin:16px 0}.divider::before,.divider::after{content:"";flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0.5px}.rich-text{font-size:14px;line-height:1.5;color:var(--ah-color-text, #1e212a)}.rich-text a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);transition:color 0.15s ease}.rich-text a:hover{text-decoration:underline}.rich-text .terms-text{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.rich-text .terms-text a{font-size:12px}.rich-text .forgot-password-link{text-align:right;font-size:13px;margin-top:4px}.rich-text .forgot-password-link a{font-size:13px;color:var(--ah-color-link, #635dff)}.rich-text .passwordless-link{margin-top:16px;text-align:center;font-size:14px}.rich-text .passwordless-link a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none)}.rich-text .passwordless-link a:hover{text-decoration:underline}.rich-text .signup-link{margin-top:16px;text-align:center;font-size:14px;color:var(--ah-color-text, #1e212a)}`;
4951
5204
 
4952
5205
  class AuthheroNode {
4953
5206
  constructor(hostRef) {
@@ -4972,6 +5225,23 @@ class AuthheroNode {
4972
5225
  * Whether the password field is visible.
4973
5226
  */
4974
5227
  passwordVisible = false;
5228
+ /**
5229
+ * Selected country for TEL input with country selector.
5230
+ */
5231
+ selectedCountry = getCountryByCode("US");
5232
+ /**
5233
+ * Local phone number (without dial code) for TEL input.
5234
+ */
5235
+ localPhoneNumber = "";
5236
+ /**
5237
+ * Whether the country dropdown is open.
5238
+ */
5239
+ countryDropdownOpen = false;
5240
+ /**
5241
+ * Whether the TEL field is currently in email mode (allow_email config).
5242
+ * When true, the value is emitted as-is without dial code prefix.
5243
+ */
5244
+ telEmailMode = false;
4975
5245
  /**
4976
5246
  * Emitted when a field value changes.
4977
5247
  */
@@ -4980,6 +5250,155 @@ class AuthheroNode {
4980
5250
  * Emitted when a button is clicked.
4981
5251
  */
4982
5252
  buttonClick;
5253
+ componentChanged() {
5254
+ this.initCountryFromConfig();
5255
+ this.initTelValue();
5256
+ }
5257
+ valueChanged() {
5258
+ this.initTelValue();
5259
+ }
5260
+ componentWillLoad() {
5261
+ this.initCountryFromConfig();
5262
+ this.initTelValue();
5263
+ }
5264
+ initCountryFromConfig() {
5265
+ if (this.component?.type === "TEL") {
5266
+ const config = this.component.config;
5267
+ const defaultCountry = config?.default_country;
5268
+ if (defaultCountry) {
5269
+ this.selectedCountry = getCountryByCode(defaultCountry);
5270
+ }
5271
+ // For allow_email mode, start in email/text mode (no country picker)
5272
+ // until the user starts typing digits
5273
+ if (config?.allow_email === true) {
5274
+ this.telEmailMode = true;
5275
+ }
5276
+ }
5277
+ }
5278
+ /**
5279
+ * Hydrate localPhoneNumber (and selectedCountry) from the effective value
5280
+ * for TEL fields. The full value is stored as `{dialCode}{localNumber}`,
5281
+ * e.g. "+15551234567".
5282
+ */
5283
+ initTelValue() {
5284
+ if (this.component?.type !== "TEL")
5285
+ return;
5286
+ const config = this.component.config;
5287
+ const allowEmail = config?.allow_email === true;
5288
+ const fullValue = this.getEffectiveValue();
5289
+ if (!fullValue) {
5290
+ this.localPhoneNumber = "";
5291
+ if (allowEmail) {
5292
+ this.telEmailMode = true;
5293
+ }
5294
+ return;
5295
+ }
5296
+ // Try to match a country by dial code (longest match first)
5297
+ if (fullValue.startsWith("+")) {
5298
+ const sorted = [...countries].sort((a, b) => b.dialCode.length - a.dialCode.length);
5299
+ for (const country of sorted) {
5300
+ if (fullValue.startsWith(country.dialCode)) {
5301
+ this.selectedCountry = country;
5302
+ this.localPhoneNumber = fullValue.slice(country.dialCode.length);
5303
+ if (allowEmail) {
5304
+ this.telEmailMode = false;
5305
+ }
5306
+ return;
5307
+ }
5308
+ }
5309
+ }
5310
+ // No dial code match — check if it looks like an email or a phone number
5311
+ this.localPhoneNumber = fullValue;
5312
+ if (allowEmail) {
5313
+ const looksLikePhone = /^[+\d]/.test(fullValue);
5314
+ this.telEmailMode = !looksLikePhone;
5315
+ }
5316
+ }
5317
+ handleCountryChange = (e) => {
5318
+ const target = e.target;
5319
+ this.selectedCountry = getCountryByCode(target.value);
5320
+ // Re-emit the full phone number with new dial code
5321
+ const fullNumber = this.localPhoneNumber
5322
+ ? `${this.selectedCountry.dialCode}${this.localPhoneNumber}`
5323
+ : "";
5324
+ this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5325
+ };
5326
+ /**
5327
+ * Try to detect a dial code prefix in the raw input (e.g. "+46", "0046")
5328
+ * and update selectedCountry accordingly. Returns the local number portion
5329
+ * (after the dial code) if a match was found, or null if no match.
5330
+ */
5331
+ detectDialCodeFromInput(raw) {
5332
+ // Normalise "00" international prefix to "+"
5333
+ const normalized = raw.startsWith("00") ? "+" + raw.slice(2) : raw;
5334
+ if (!normalized.startsWith("+"))
5335
+ return null;
5336
+ // Match longest dial code first
5337
+ const sorted = [...countries].sort((a, b) => b.dialCode.length - a.dialCode.length);
5338
+ for (const country of sorted) {
5339
+ if (normalized.startsWith(country.dialCode)) {
5340
+ this.selectedCountry = country;
5341
+ return normalized.slice(country.dialCode.length);
5342
+ }
5343
+ }
5344
+ return null;
5345
+ }
5346
+ handlePhoneInput = (e) => {
5347
+ const target = e.target;
5348
+ const value = target.value;
5349
+ const config = this.component.config;
5350
+ const allowEmail = config?.allow_email === true;
5351
+ if (allowEmail) {
5352
+ // Detect phone mode: value starts with digit or '+', and no '@'.
5353
+ // When the field is empty, revert to neutral (email) mode so the
5354
+ // country picker disappears and the user can start fresh.
5355
+ const looksLikePhone = value.length > 0 && /^[+\d]/.test(value) && !value.includes("@");
5356
+ this.telEmailMode = !looksLikePhone;
5357
+ if (!this.telEmailMode) {
5358
+ // Phone mode — first try dial code detection before stripping '+'
5359
+ // so that typing +46 or 0046 can match a country
5360
+ const dialLocal = this.detectDialCodeFromInput(value);
5361
+ if (dialLocal !== null) {
5362
+ // Dial code matched — strip it from the input and show only local part
5363
+ const cleanedLocal = dialLocal
5364
+ .replace(/[^+\d\s\-()]/g, "")
5365
+ .replace(/\+/g, "");
5366
+ target.value = cleanedLocal;
5367
+ this.localPhoneNumber = cleanedLocal;
5368
+ const fullNumber = `${this.selectedCountry.dialCode}${cleanedLocal}`;
5369
+ this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5370
+ }
5371
+ else {
5372
+ // No dial code — strip non-phone chars and '+' (picker provides the prefix)
5373
+ const cleaned = value.replace(/[^+\d\s\-()]/g, "").replace(/\+/g, "");
5374
+ if (cleaned !== value) {
5375
+ target.value = cleaned;
5376
+ }
5377
+ this.localPhoneNumber = cleaned;
5378
+ const fullNumber = cleaned
5379
+ ? `${this.selectedCountry.dialCode}${cleaned}`
5380
+ : "";
5381
+ this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5382
+ }
5383
+ }
5384
+ else {
5385
+ // Email or text — emit as-is
5386
+ this.localPhoneNumber = value;
5387
+ this.fieldChange.emit({ id: this.component.id, value });
5388
+ }
5389
+ return;
5390
+ }
5391
+ // Standard phone-only mode — strip '+' since the picker provides the prefix
5392
+ const cleaned = value.replace(/[^\d\s\-()]/g, "");
5393
+ if (cleaned !== value) {
5394
+ target.value = cleaned;
5395
+ }
5396
+ this.localPhoneNumber = cleaned;
5397
+ const fullNumber = cleaned
5398
+ ? `${this.selectedCountry.dialCode}${cleaned}`
5399
+ : "";
5400
+ this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5401
+ };
4983
5402
  handleInput = (e) => {
4984
5403
  const target = e.target;
4985
5404
  this.fieldChange.emit({ id: this.component.id, value: target.value });
@@ -4989,6 +5408,16 @@ class AuthheroNode {
4989
5408
  e.preventDefault();
4990
5409
  this.buttonClick.emit({ id: "submit", type: "submit", value: "next" });
4991
5410
  }
5411
+ // In combined TEL+email mode, backspace on an empty field exits phone mode
5412
+ if (e.key === "Backspace" && !this.telEmailMode) {
5413
+ const target = e.target;
5414
+ const config = this.component.config;
5415
+ if (config?.allow_email === true && target.value.length === 0) {
5416
+ this.telEmailMode = true;
5417
+ this.localPhoneNumber = "";
5418
+ this.fieldChange.emit({ id: this.component.id, value: "" });
5419
+ }
5420
+ }
4992
5421
  };
4993
5422
  handleCheckbox = (e) => {
4994
5423
  const target = e.target;
@@ -5161,8 +5590,17 @@ class AuthheroNode {
5161
5590
  renderTelField(component) {
5162
5591
  const inputId = `input-${component.id}`;
5163
5592
  const errors = this.getErrors();
5164
- const effectiveValue = this.getEffectiveValue();
5165
- return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "tel", name: component.id, value: effectiveValue ?? "", placeholder: component.config?.placeholder, required: component.required, disabled: this.disabled, autocomplete: "tel", onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
5593
+ const config = component.config;
5594
+ const allowEmail = config?.allow_email === true;
5595
+ const hasValue = this.localPhoneNumber.length > 0;
5596
+ // In allow_email mode, show the country picker only when the user is typing a phone number
5597
+ const showCountryPicker = allowEmail ? !this.telEmailMode : true;
5598
+ // Calculate dynamic width: flag + space + dial code + small padding for dropdown arrow
5599
+ const selectedText = `${this.selectedCountry.flag} ${this.selectedCountry.dialCode}`;
5600
+ const selectWidth = `${selectedText.length + 1}ch`;
5601
+ const countrySelect = showCountryPicker ? (hAsync("select", { class: "country-select", part: "country-select", style: { width: selectWidth, minWidth: "0" }, onChange: this.handleCountryChange, disabled: this.disabled, "aria-label": "Country code" }, countries.map((country) => (hAsync("option", { value: country.code, selected: this.selectedCountry.code === country.code, key: country.code }, `${country.flag} ${country.dialCode}`))))) : null;
5602
+ const inputType = allowEmail && this.telEmailMode ? "text" : "tel";
5603
+ return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: showCountryPicker ? "phone-input-wrapper" : "", part: "phone-input-wrapper" }, countrySelect, hAsync("div", { class: "input-container" }, hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: inputType, name: component.id, "data-input-name": component.id, value: this.localPhoneNumber, placeholder: " ", required: component.required, disabled: this.disabled, autocomplete: allowEmail && this.telEmailMode ? "email" : "tel-national", onInput: this.handlePhoneInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue))), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
5166
5604
  }
5167
5605
  renderUrlField(component) {
5168
5606
  const inputId = `input-${component.id}`;
@@ -5239,7 +5677,21 @@ class AuthheroNode {
5239
5677
  const safeProvider = this.sanitizeForCssToken(provider);
5240
5678
  const strategy = getProviderStrategy(provider);
5241
5679
  const icon = getProviderIcon(provider);
5242
- return (hAsync("button", { type: "button", class: `btn btn-secondary btn-social btn-social-${safeProvider}${icon ? "" : " no-icon"}`, part: `button button-secondary button-social button-social-${safeProvider}`, "data-connection-name": provider, "data-strategy": strategy, disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "SOCIAL", provider), key: provider }, icon, hAsync("span", { class: "btn-social-content", part: `button-social-content button-social-content-${safeProvider}` }, hAsync("span", { part: `button-social-text button-social-text-${safeProvider}` }, getButtonText(provider)), hAsync("span", { class: "btn-social-subtitle", part: `button-social-subtitle button-social-subtitle-${safeProvider}` }))));
5680
+ const details = detailsMap.get(provider);
5681
+ const btnClass = `btn btn-secondary btn-social btn-social-${safeProvider}${icon ? "" : " no-icon"}`;
5682
+ const btnPart = `button button-secondary button-social button-social-${safeProvider}`;
5683
+ const content = [
5684
+ icon,
5685
+ hAsync("span", { class: "btn-social-content", part: `button-social-content button-social-content-${safeProvider}` }, hAsync("span", { part: `button-social-text button-social-text-${safeProvider}` }, getButtonText(provider)), hAsync("span", { class: "btn-social-subtitle", part: `button-social-subtitle button-social-subtitle-${safeProvider}` })),
5686
+ ];
5687
+ if (details?.href) {
5688
+ return (hAsync("a", { href: this.disabled ? undefined : details.href, class: btnClass, part: btnPart, "data-connection-name": provider, "data-strategy": strategy, key: provider, "aria-disabled": this.disabled ? "true" : undefined, tabindex: this.disabled ? -1 : undefined, onClick: (e) => {
5689
+ if (this.disabled) {
5690
+ e.preventDefault();
5691
+ }
5692
+ } }, content));
5693
+ }
5694
+ return (hAsync("button", { type: "button", class: btnClass, part: btnPart, "data-connection-name": provider, "data-strategy": strategy, disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "SOCIAL", provider), key: provider }, content));
5243
5695
  })));
5244
5696
  }
5245
5697
  // ===========================================================================
@@ -5314,6 +5766,14 @@ class AuthheroNode {
5314
5766
  return null;
5315
5767
  }
5316
5768
  }
5769
+ static get watchers() { return {
5770
+ "component": [{
5771
+ "componentChanged": 0
5772
+ }],
5773
+ "value": [{
5774
+ "valueChanged": 0
5775
+ }]
5776
+ }; }
5317
5777
  static get style() { return authheroNodeCss(); }
5318
5778
  static get cmpMeta() { return {
5319
5779
  "$flags$": 521,
@@ -5322,7 +5782,11 @@ class AuthheroNode {
5322
5782
  "component": [16],
5323
5783
  "value": [1],
5324
5784
  "disabled": [4],
5325
- "passwordVisible": [32]
5785
+ "passwordVisible": [32],
5786
+ "selectedCountry": [32],
5787
+ "localPhoneNumber": [32],
5788
+ "countryDropdownOpen": [32],
5789
+ "telEmailMode": [32]
5326
5790
  },
5327
5791
  "$listeners$": undefined,
5328
5792
  "$lazyBundleId$": "-",
@@ -5335,6 +5799,76 @@ class AuthheroNode {
5335
5799
  *
5336
5800
  * Converts AuthHero branding and theme configurations to CSS custom properties.
5337
5801
  */
5802
+ // --- Inline WCAG contrast utilities (small footprint, no external deps) ---
5803
+ function parseHexColor(hex) {
5804
+ const match = hex.match(/^#([0-9a-f]{3})$/i) || hex.match(/^#([0-9a-f]{6})$/i);
5805
+ if (!match)
5806
+ return null;
5807
+ let clean = match[1];
5808
+ if (clean.length === 3) {
5809
+ clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];
5810
+ }
5811
+ const num = parseInt(clean, 16);
5812
+ return [(num >> 16) & 255, (num >> 8) & 255, num & 255];
5813
+ }
5814
+ function srgbLuminance(hex) {
5815
+ const rgb = parseHexColor(hex);
5816
+ if (!rgb)
5817
+ return NaN;
5818
+ const [r, g, b] = rgb.map((c) => {
5819
+ const s = c / 255;
5820
+ return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
5821
+ });
5822
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
5823
+ }
5824
+ function wcagContrastRatio(hex1, hex2) {
5825
+ const l1 = srgbLuminance(hex1);
5826
+ const l2 = srgbLuminance(hex2);
5827
+ if (isNaN(l1) || isNaN(l2))
5828
+ return NaN;
5829
+ return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
5830
+ }
5831
+ function autoTextColor(backgroundHex, mode = "light") {
5832
+ const blackContrast = wcagContrastRatio(backgroundHex, "#000000");
5833
+ const whiteContrast = wcagContrastRatio(backgroundHex, "#ffffff");
5834
+ const BIAS = 1.35;
5835
+ if (mode === "light") {
5836
+ return blackContrast > whiteContrast * BIAS ? "#000000" : "#ffffff";
5837
+ }
5838
+ return blackContrast * BIAS > whiteContrast ? "#000000" : "#ffffff";
5839
+ }
5840
+ function darkenHex(hex, percent) {
5841
+ const rgb = parseHexColor(hex);
5842
+ if (!rgb)
5843
+ return hex;
5844
+ const [r, g, b] = rgb;
5845
+ const toHex = (v) => Math.max(0, Math.round(v * (1 - percent)))
5846
+ .toString(16)
5847
+ .padStart(2, "0");
5848
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
5849
+ }
5850
+ function lightenHex(hex, percent) {
5851
+ const rgb = parseHexColor(hex);
5852
+ if (!rgb)
5853
+ return hex;
5854
+ const [r, g, b] = rgb;
5855
+ const toHex = (v) => Math.min(255, Math.round(v + (255 - v) * percent))
5856
+ .toString(16)
5857
+ .padStart(2, "0");
5858
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
5859
+ }
5860
+ function ensureContrastColor(fg, bg, minRatio = 4.5) {
5861
+ if (wcagContrastRatio(fg, bg) >= minRatio)
5862
+ return fg;
5863
+ const shouldDarken = srgbLuminance(bg) > 0.5;
5864
+ let adjusted = fg;
5865
+ for (let i = 1; i <= 10; i++) {
5866
+ adjusted = shouldDarken ? darkenHex(fg, i * 0.1) : lightenHex(fg, i * 0.1);
5867
+ if (wcagContrastRatio(adjusted, bg) >= minRatio)
5868
+ return adjusted;
5869
+ }
5870
+ return shouldDarken ? "#000000" : "#ffffff";
5871
+ }
5338
5872
  /**
5339
5873
  * Convert a style enum to border radius value
5340
5874
  */
@@ -5431,9 +5965,22 @@ function themeToCssVars(theme) {
5431
5965
  if (c.primary_button) {
5432
5966
  vars["--ah-color-primary"] = c.primary_button;
5433
5967
  vars["--ah-color-primary-hover"] = c.primary_button;
5968
+ // Auto-compute text-on-primary for contrast
5969
+ const hasGoodExplicit = c.primary_button_label &&
5970
+ wcagContrastRatio(c.primary_button_label, c.primary_button) >= 4.5;
5971
+ if (hasGoodExplicit) {
5972
+ vars["--ah-color-text-on-primary"] = c.primary_button_label;
5973
+ }
5974
+ else {
5975
+ vars["--ah-color-text-on-primary"] = autoTextColor(c.primary_button, "light");
5976
+ const darkText = autoTextColor(c.primary_button, "dark");
5977
+ if (darkText !== vars["--ah-color-text-on-primary"]) {
5978
+ vars["--ah-color-text-on-primary-dark"] = darkText;
5979
+ }
5980
+ }
5434
5981
  }
5435
- if (c.primary_button_label) {
5436
- vars["--ah-btn-primary-text"] = c.primary_button_label;
5982
+ else if (c.primary_button_label) {
5983
+ vars["--ah-color-text-on-primary"] = c.primary_button_label;
5437
5984
  }
5438
5985
  // Secondary button
5439
5986
  if (c.secondary_button_border) {
@@ -5470,9 +6017,10 @@ function themeToCssVars(theme) {
5470
6017
  if (c.input_border) {
5471
6018
  vars["--ah-color-border"] = c.input_border;
5472
6019
  }
5473
- // Links
6020
+ // Links — ensure contrast against widget background
5474
6021
  if (c.links_focused_components) {
5475
- vars["--ah-color-link"] = c.links_focused_components;
6022
+ const widgetBg = c.widget_background || "#ffffff";
6023
+ vars["--ah-color-link"] = ensureContrastColor(c.links_focused_components, widgetBg);
5476
6024
  }
5477
6025
  // Focus/hover
5478
6026
  if (c.base_focus_color) {
@@ -5498,6 +6046,10 @@ function themeToCssVars(theme) {
5498
6046
  const f = theme.fonts;
5499
6047
  // reference_text_size is the base font size in pixels (default 16px)
5500
6048
  const baseSize = f.reference_text_size || 16;
6049
+ // Font sizes can be stored as percentages (Auth0 convention, e.g. 150 = 150% of base)
6050
+ // or as absolute pixel values (e.g. 24 = 24px). Values >= 50 are treated as
6051
+ // percentages; values < 50 are treated as direct pixel values.
6052
+ const fontSizePx = (size) => size >= 50 ? Math.round((size / 100) * baseSize) : size;
5501
6053
  if (f.font_url) {
5502
6054
  vars["--ah-font-url"] = f.font_url;
5503
6055
  }
@@ -5506,28 +6058,22 @@ function themeToCssVars(theme) {
5506
6058
  }
5507
6059
  // Title, subtitle, etc. sizes are percentages of the base size
5508
6060
  if (f.title?.size) {
5509
- const titlePx = Math.round((f.title.size / 100) * baseSize);
5510
- vars["--ah-font-size-title"] = `${titlePx}px`;
6061
+ vars["--ah-font-size-title"] = `${fontSizePx(f.title.size)}px`;
5511
6062
  }
5512
6063
  if (f.subtitle?.size) {
5513
- const subtitlePx = Math.round((f.subtitle.size / 100) * baseSize);
5514
- vars["--ah-font-size-subtitle"] = `${subtitlePx}px`;
6064
+ vars["--ah-font-size-subtitle"] = `${fontSizePx(f.subtitle.size)}px`;
5515
6065
  }
5516
6066
  if (f.body_text?.size) {
5517
- const bodyPx = Math.round((f.body_text.size / 100) * baseSize);
5518
- vars["--ah-font-size-body"] = `${bodyPx}px`;
6067
+ vars["--ah-font-size-body"] = `${fontSizePx(f.body_text.size)}px`;
5519
6068
  }
5520
6069
  if (f.input_labels?.size) {
5521
- const labelPx = Math.round((f.input_labels.size / 100) * baseSize);
5522
- vars["--ah-font-size-label"] = `${labelPx}px`;
6070
+ vars["--ah-font-size-label"] = `${fontSizePx(f.input_labels.size)}px`;
5523
6071
  }
5524
6072
  if (f.buttons_text?.size) {
5525
- const btnPx = Math.round((f.buttons_text.size / 100) * baseSize);
5526
- vars["--ah-font-size-btn"] = `${btnPx}px`;
6073
+ vars["--ah-font-size-btn"] = `${fontSizePx(f.buttons_text.size)}px`;
5527
6074
  }
5528
6075
  if (f.links?.size) {
5529
- const linkPx = Math.round((f.links.size / 100) * baseSize);
5530
- vars["--ah-font-size-link"] = `${linkPx}px`;
6076
+ vars["--ah-font-size-link"] = `${fontSizePx(f.links.size)}px`;
5531
6077
  }
5532
6078
  if (f.links_style === "underlined") {
5533
6079
  vars["--ah-link-decoration"] = "underline";
@@ -5752,7 +6298,7 @@ function escapeAttr(value) {
5752
6298
  .replace(/>/g, "&gt;");
5753
6299
  }
5754
6300
 
5755
- const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 14px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{max-width:var(--ah-widget-max-width, 400px);width:100%;margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0 24px);color:var(--ah-color-header, #1e212a);line-height:1.2}.description{font-size:var(--ah-font-size-description, 14px);text-align:var(--ah-title-align, center);margin:var(--ah-description-margin, 0 0 8px);color:var(--ah-color-text, #1e212a);line-height:1.5}.message{padding:12px 16px;border-radius:4px;margin-bottom:16px;font-size:14px;line-height:1.5}.message-error{background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38)}.message-success{background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769)}form{display:flex;flex-direction:column}.form-content{display:flex;flex-direction:column}.social-section{display:flex;flex-direction:column;gap:8px;order:var(--ah-social-order, 2)}.fields-section{display:flex;flex-direction:column;order:var(--ah-fields-order, 0)}.divider{display:flex;align-items:center;text-align:center;margin:16px 0;order:var(--ah-divider-order, 1)}.divider::before,.divider::after{content:'';flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0}.links{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:16px}.link-wrapper{font-size:14px;color:var(--ah-color-text, #1e212a)}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400);transition:color 150ms ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.widget-footer{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.widget-footer a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:12px;transition:color 150ms ease}.widget-footer a:hover{text-decoration:underline}.widget-footer a:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.loading-spinner{width:32px;height:32px;margin:24px auto;border:3px solid var(--ah-color-border-muted, #e0e1e3);border-top-color:var(--ah-color-primary, #635dff);border-radius:50%;animation:spin 0.8s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.error-message{text-align:center;color:var(--ah-color-error, #d03c38);padding:16px;font-size:14px}@media (max-width: 480px){:host{display:block;width:100%;min-height:100vh;background-color:var(--ah-color-bg, #ffffff)}.widget-container{box-shadow:none;border-radius:0;max-width:none;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}`;
6301
+ const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 16px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{max-width:var(--ah-widget-max-width, 400px);width:100%;margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0 24px);color:var(--ah-color-header, #1e212a);line-height:1.2}.description{font-size:var(--ah-font-size-subtitle, 16px);text-align:var(--ah-title-align, center);margin:var(--ah-description-margin, 0 0 8px);color:var(--ah-color-text, #1e212a);line-height:1.5}.message{padding:12px 16px;border-radius:4px;margin-bottom:16px;font-size:14px;line-height:1.5}.message-error{background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38)}.message-success{background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769)}form{display:flex;flex-direction:column}.form-content{display:flex;flex-direction:column}.social-section{display:flex;flex-direction:column;gap:8px;order:var(--ah-social-order, 2)}.fields-section{display:flex;flex-direction:column;order:var(--ah-fields-order, 0)}.divider{display:flex;align-items:center;text-align:center;margin:16px 0;order:var(--ah-divider-order, 1)}.divider::before,.divider::after{content:'';flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0}.links{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:16px}.link-wrapper{font-size:14px;color:var(--ah-color-text, #1e212a)}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400);transition:color 150ms ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.widget-footer{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.widget-footer a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:12px;transition:color 150ms ease}.widget-footer a:hover{text-decoration:underline}.widget-footer a:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.loading-spinner{width:32px;height:32px;margin:24px auto;border:3px solid var(--ah-color-border-muted, #e0e1e3);border-top-color:var(--ah-color-primary, #635dff);border-radius:50%;animation:spin 0.8s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.error-message{text-align:center;color:var(--ah-color-error, #d03c38);padding:16px;font-size:14px}@media (max-width: 480px){:host{display:block;width:100%;min-height:100vh;background-color:var(--ah-color-bg, #ffffff)}.widget-container{box-shadow:none;border-radius:0;max-width:none;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}`;
5756
6302
 
5757
6303
  class AuthheroWidget {
5758
6304
  constructor(hostRef) {
@@ -6115,6 +6661,25 @@ class AuthheroWidget {
6115
6661
  }
6116
6662
  }
6117
6663
  }
6664
+ handlePopState = (event) => {
6665
+ if (!this.apiUrl)
6666
+ return;
6667
+ // Restore the widget state token from history if present
6668
+ if (event.state?.state) {
6669
+ this.state = event.state.state;
6670
+ }
6671
+ // Derive screen from history state or from the current URL
6672
+ const screen = event.state?.screen ?? this.extractScreenIdFromHref(location.href);
6673
+ if (screen) {
6674
+ this.fetchScreen(screen);
6675
+ }
6676
+ };
6677
+ connectedCallback() {
6678
+ window.addEventListener("popstate", this.handlePopState);
6679
+ }
6680
+ disconnectedCallback() {
6681
+ window.removeEventListener("popstate", this.handlePopState);
6682
+ }
6118
6683
  async componentWillLoad() {
6119
6684
  // Parse initial props - this prevents unnecessary state changes during hydration that cause flashes
6120
6685
  // Also check the element attribute as a fallback for hydration scenarios
@@ -6147,7 +6712,7 @@ class AuthheroWidget {
6147
6712
  */
6148
6713
  async fetchScreen(screenIdOverride, nodeId) {
6149
6714
  if (!this.apiUrl)
6150
- return;
6715
+ return false;
6151
6716
  const currentScreenId = screenIdOverride || this.screenId;
6152
6717
  // Build the API URL, replacing {screenId} placeholder if present
6153
6718
  let url = this.apiUrl;
@@ -6202,6 +6767,7 @@ class AuthheroWidget {
6202
6767
  this.updateDataScreenAttribute();
6203
6768
  this.persistState();
6204
6769
  this.focusFirstInput();
6770
+ return true;
6205
6771
  }
6206
6772
  }
6207
6773
  else {
@@ -6222,6 +6788,7 @@ class AuthheroWidget {
6222
6788
  finally {
6223
6789
  this.loading = false;
6224
6790
  }
6791
+ return false;
6225
6792
  }
6226
6793
  handleInputChange = (name, value) => {
6227
6794
  this.formData = {
@@ -6334,6 +6901,18 @@ class AuthheroWidget {
6334
6901
  this.buttonClick.emit(detail);
6335
6902
  // Handle social login if autoNavigate is enabled
6336
6903
  if (detail.type === "SOCIAL" && detail.value && this.shouldAutoNavigate) {
6904
+ // Check if this provider has an href (e.g. passwordless connections)
6905
+ const providerHref = this.getProviderHref(detail.value);
6906
+ if (providerHref) {
6907
+ const screenId = this.extractScreenIdFromHref(providerHref);
6908
+ if (screenId && this.apiUrl) {
6909
+ this.navigateToScreen(screenId, providerHref);
6910
+ }
6911
+ else {
6912
+ window.location.href = providerHref;
6913
+ }
6914
+ return;
6915
+ }
6337
6916
  this.handleSocialLogin(detail.value);
6338
6917
  return;
6339
6918
  }
@@ -6397,6 +6976,30 @@ class AuthheroWidget {
6397
6976
  console.error("Resend failed:", error);
6398
6977
  }
6399
6978
  }
6979
+ /**
6980
+ * Extract screen ID from a u2 link href.
6981
+ * Handles patterns like /u2/login/{screenId}?state=... and /u2/{screenId}?state=...
6982
+ * Returns the screen ID or null if the href doesn't match a known pattern.
6983
+ */
6984
+ extractScreenIdFromHref(href) {
6985
+ try {
6986
+ const url = new URL(href, window.location.origin);
6987
+ const path = url.pathname;
6988
+ // Match /u2/login/{screenId} (e.g., /u2/login/identifier)
6989
+ const loginMatch = path.match(/\/u2\/login\/([^/]+)$/);
6990
+ if (loginMatch)
6991
+ return loginMatch[1];
6992
+ // Match /u2/{screenId} (e.g., /u2/signup, /u2/enter-password)
6993
+ const u2Match = path.match(/\/u2\/([^/]+)$/);
6994
+ if (u2Match && u2Match[1] !== "login" && u2Match[1] !== "screen") {
6995
+ return u2Match[1];
6996
+ }
6997
+ return null;
6998
+ }
6999
+ catch {
7000
+ return null;
7001
+ }
7002
+ }
6400
7003
  handleLinkClick = (e, link) => {
6401
7004
  // Emit the event so the consuming app can handle it
6402
7005
  this.linkClick.emit({
@@ -6404,12 +7007,52 @@ class AuthheroWidget {
6404
7007
  href: link.href,
6405
7008
  text: link.text,
6406
7009
  });
6407
- // If autoNavigate is enabled, let the browser handle the navigation
6408
- // Otherwise, prevent default and let the app decide
7010
+ // If autoNavigate is not enabled, prevent default and let the app decide
6409
7011
  if (!this.shouldAutoNavigate) {
6410
7012
  e.preventDefault();
7013
+ return;
6411
7014
  }
7015
+ // Try client-side navigation: extract screen ID and fetch via API
7016
+ const screenId = this.extractScreenIdFromHref(link.href);
7017
+ if (screenId && this.apiUrl) {
7018
+ e.preventDefault();
7019
+ this.navigateToScreen(screenId, link.href);
7020
+ return;
7021
+ }
7022
+ // Fall back to browser navigation for non-u2 links
6412
7023
  };
7024
+ /**
7025
+ * Navigate to a screen client-side by fetching it from the API.
7026
+ * Updates the widget state and browser URL without a full page reload.
7027
+ */
7028
+ async navigateToScreen(screenId, displayUrl) {
7029
+ const success = await this.fetchScreen(screenId);
7030
+ if (success) {
7031
+ // Push browser history so back/forward works
7032
+ window.history.pushState({ screen: screenId, state: this.state }, "", displayUrl);
7033
+ }
7034
+ else {
7035
+ // On failure, fall back to hard navigation
7036
+ window.location.href = displayUrl;
7037
+ }
7038
+ }
7039
+ /**
7040
+ * Look up the href for a social provider from the current screen's SOCIAL components.
7041
+ * Returns the href if found (e.g. for passwordless connections), or null.
7042
+ */
7043
+ getProviderHref(connectionName) {
7044
+ if (!this._screen)
7045
+ return null;
7046
+ for (const comp of this._screen.components) {
7047
+ const c = comp;
7048
+ if (c.type === "SOCIAL" && c.config?.provider_details) {
7049
+ const match = c.config.provider_details.find((d) => d.name === connectionName);
7050
+ if (match?.href)
7051
+ return match.href;
7052
+ }
7053
+ }
7054
+ return null;
7055
+ }
6413
7056
  /**
6414
7057
  * Check if a component is a social button.
6415
7058
  */