@cshah18/sdk 4.14.0 → 4.16.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.
@@ -840,6 +840,7 @@ var CoBuySDK = (function (exports) {
840
840
  this.onGroupMemberJoined = (event) => {
841
841
  const detail = event.detail || {};
842
842
  console.log("eventttttt", detail);
843
+ const eventType = event.type;
843
844
  const productId = detail.product_id;
844
845
  const groupData = detail.group;
845
846
  // Only process if this is for the current product
@@ -861,6 +862,16 @@ var CoBuySDK = (function (exports) {
861
862
  if (typeof participantsCount === "number") {
862
863
  this.groups[groupIndex].joined = participantsCount;
863
864
  }
865
+ // If this session left the currently joined group, clear stale local membership state.
866
+ const leavingSessionId = detail.session_id;
867
+ if (eventType === "group:member:left" &&
868
+ this.currentSessionId &&
869
+ leavingSessionId === this.currentSessionId &&
870
+ this.currentJoinedGroupId === groupId) {
871
+ this.currentJoinedGroupId = null;
872
+ this.groups[groupIndex].isMember = false;
873
+ this.updateStartButtonState(false, this.isLoading);
874
+ }
864
875
  this.logger.info(`[GroupListModal] Updated group ${groupId} - participants: ${participantsCount}`);
865
876
  // Re-render the specific group card
866
877
  this.updateGroupCard(groupId);
@@ -902,6 +913,7 @@ var CoBuySDK = (function (exports) {
902
913
  return;
903
914
  }
904
915
  window.addEventListener("group:member:joined", this.handleGroupMemberJoinedEvent);
916
+ window.addEventListener("group:member:left", this.handleGroupMemberJoinedEvent);
905
917
  this.socketListenerRegistered = true;
906
918
  this.logger.debug("[GroupListModal] Socket event listeners registered");
907
919
  }
@@ -911,6 +923,7 @@ var CoBuySDK = (function (exports) {
911
923
  return;
912
924
  }
913
925
  window.removeEventListener("group:member:joined", this.handleGroupMemberJoinedEvent);
926
+ window.removeEventListener("group:member:left", this.handleGroupMemberJoinedEvent);
914
927
  this.socketListenerRegistered = false;
915
928
  this.logger.debug("[GroupListModal] Socket event listeners unregistered");
916
929
  }
@@ -1042,13 +1055,11 @@ var CoBuySDK = (function (exports) {
1042
1055
  console.log("groupsss", groups);
1043
1056
  // If API reports membership, prefer it over cached state
1044
1057
  const memberGroup = groups.find((g) => g.isMember);
1045
- if (memberGroup) {
1046
- this.currentJoinedGroupId = memberGroup.groupId;
1047
- }
1058
+ this.currentJoinedGroupId = memberGroup ? memberGroup.groupId : null;
1048
1059
  this.groups = groups;
1049
1060
  this.liveCount = groups.length;
1050
1061
  this.logger.info(`Fetched ${groups.length} active groups`);
1051
- const hasMembership = Boolean(this.currentJoinedGroupId || groups.some((g) => g.isMember));
1062
+ const hasMembership = Boolean(memberGroup);
1052
1063
  this.updateStartButtonState(hasMembership, false);
1053
1064
  // Update and show live count display
1054
1065
  const liveCountText = document.getElementById("cobuy-live-count-text");
@@ -1406,7 +1417,7 @@ var CoBuySDK = (function (exports) {
1406
1417
  createGroupCard(group) {
1407
1418
  const card = document.createElement("div");
1408
1419
  card.className = "cobuy-group-card";
1409
- card.dataset.groupId = group.name || group.groupId;
1420
+ card.dataset.groupId = group.groupId;
1410
1421
  const header = document.createElement("div");
1411
1422
  header.className = "cobuy-group-card-header";
1412
1423
  const groupId = document.createElement("div");
@@ -2159,15 +2170,24 @@ var CoBuySDK = (function (exports) {
2159
2170
  }
2160
2171
  }
2161
2172
 
2162
- var css_248z = ".sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.cb-lobby-modal-container{height:100%;left:0;overflow-y:auto!important;position:fixed;top:0;width:100%;z-index:10000}.cb-lobby-modal-container *{box-sizing:border-box;margin:0;padding:0}.cb-lobby-modal-container body,.cb-lobby-modal-container html{font-family:Inter,sans-serif;height:100%;overflow-x:hidden;width:100%}.cb-lobby-main{backdrop-filter:blur(4px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:50px;box-shadow:0 25px 50px -12px #00000040;padding:80px;position:relative;width:100%;z-index:1}.cb-lobby-bg{background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/cb-back-image.png);background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:-1}.cb-lobby-main-wp{align-items:center;display:flex;justify-content:center;padding:40px 80px}.lobby-indicator{align-items:center;backdrop-filter:blur(8px);background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.3);border-radius:24px;display:flex;gap:12px;margin-bottom:16px;padding:6px 12px;width:fit-content}.lobby-indicator-logo{display:block;flex-shrink:0;height:34px;width:auto}.pulsing-dot{align-items:center;display:flex;height:8px;justify-content:center;position:relative;width:8px}.pulsing-dot:after{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.pulsing-dot:after,.pulsing-dot:before{background:#10b981;border-radius:50%;content:\"\";height:100%;position:absolute;width:100%}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.indicator-text{color:#047857;font-size:11px;font-weight:800;letter-spacing:.5px;line-height:1;text-transform:uppercase}.lobby-status-section{display:flex;flex-direction:column;margin-bottom:24px}.lobby-status{background:#fff;border-radius:4px;color:#000;font-size:10px;font-weight:600;line-height:1.2;padding:6px 10px;text-transform:uppercase}.lobby-status.active{background:#155dfc;color:#fff}.lobby-status.complete{background:#10b981;color:#fff}.lobby-status-wp{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.lobby-number{backdrop-filter:blur(3px);background:hsla(0,0%,100%,.2);border-radius:4px;box-shadow:0 25px 50px -12px #00000040;font-size:12px;font-weight:700;letter-spacing:1px;line-height:1.2;padding:5px 8px;text-transform:uppercase}.title-wp{margin-top:25px}.title-wp h2{font-size:60px;font-weight:900;line-height:1;margin-bottom:15px}.sub-title{-webkit-text-fill-color:transparent;animation:gradient-flow 4s linear infinite;background-clip:text;-webkit-background-clip:text;background-image:linear-gradient(90deg,#1e293b,#2563eb 25%,#1e293b 50%);background-size:200% auto;color:#1e293b;font-size:16px;font-weight:700;line-height:1.5;margin-bottom:0}@keyframes gradient-flow{0%{background-position:200% 0}to{background-position:-200% 0}}.sub-title.completed{-webkit-text-fill-color:unset;background-clip:unset;-webkit-background-clip:unset;background-image:none;color:#1e293b}.connected-section{backdrop-filter:blur(4px);background:hsla(0,0%,100%,.05);border:1px solid hsla(0,0%,100%,.1);border-radius:24px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex-direction:column;gap:20px;margin-top:24px;padding:24px;transition:all .3s ease}.connected-section:hover{background:hsla(0,0%,100%,.1);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.link-share-container{display:flex;flex-direction:column}.link-share-wrapper{align-items:center;display:flex;gap:12px;width:100%}.lobby-link-box{align-items:center;backdrop-filter:blur(8px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.3);border-radius:14px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex:1;gap:16px;justify-content:space-between;min-height:50px;padding:16px 20px;transition:all .2s ease}.lobby-link-box:hover{background-color:hsla(0,0%,100%,.5);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.lobby-link-text{color:#71717a;font-size:10px;font-weight:700;letter-spacing:.6px;line-height:1.2;margin-bottom:6px;text-transform:uppercase}.copy-link-btn{align-items:center;background:transparent;border:none;border-radius:6px;color:#64748b;cursor:pointer;display:flex;flex-shrink:0;font-size:13px;font-weight:600;gap:6px;justify-content:center;padding:0;transition:all .2s ease;white-space:nowrap}.copy-link-btn:hover{color:#1e293b}.copy-link-btn .copy-text{display:none}.copy-link-btn svg{flex-shrink:0;height:18px;width:18px}@media (min-width:640px){.copy-link-btn .copy-text{display:inline}}.lobby-link-url{color:#1e293b;font-size:15px;font-weight:700;line-height:1.4;word-break:break-all}.link-box-container{flex:1;min-width:0}.share-btn{align-items:center;background:#1e293b;border:none;border-radius:14px;box-shadow:0 2px 4px 0 rgba(0,0,0,.1);color:#fff;cursor:pointer;display:flex;flex-shrink:0;font-size:15px;font-weight:600;gap:8px;height:50px;justify-content:center;min-width:auto;padding:35px 24px;transition:all .2s ease}.share-btn .share-text{color:#fff;display:inline;font-size:18px;margin:0!important}.share-btn svg{color:#fff;flex-shrink:0;height:18px;width:18px}@media (max-width:640px){.share-btn{font-size:14px;height:50px;padding:0 18px}.share-btn .share-text{color:#fff;display:inline}}.share-btn:hover{background:#0f172a;box-shadow:0 4px 8px 0 rgba(0,0,0,.15);transform:translateY(-1px)}.share-btn:active{transform:scale(.95)}.lobby-offer-box{align-items:center;backdrop-filter:blur(8px);background-color:rgba(59,130,246,.063);border:1px solid rgba(59,130,246,.125);border-radius:1rem;display:flex;gap:30px;margin-top:30px;padding:15px 20px}.offer-box-icon{align-items:center;background:linear-gradient(135deg,#3b82f6,rgba(59,130,246,.867));border-radius:10px;box-shadow:0 10px 15px -3px rgba(59,130,246,.314);color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;padding:5px;width:56px}.offer-lock-status{align-items:center;display:flex;gap:6px;margin-bottom:2px}.offer-lock-status span{font-size:14px;font-weight:700;line-height:1}.offer-box-content h3{font-size:30px;font-weight:900;line-height:1.2}.cb-lobby-top{display:grid;gap:100px;grid-template-columns:7fr 5fr}.group-info{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:20px;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.progress-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:14px}.progress-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;justify-content:center;letter-spacing:.7px;position:relative}.progress-badge{background:#3b82f6;border-radius:999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;padding:6px 12px}.cb-lobby-modal-container .progress-bar{background:#fff;border-radius:999px;height:14px;overflow:hidden;width:100%}.cb-lobby-modal-container .progress-fill{animation:shimmer 2.7s linear infinite;background:#3b82f6;background-image:linear-gradient(120deg,hsla(0,0%,100%,.15) 25%,hsla(0,0%,100%,.45) 37%,hsla(0,0%,100%,.15) 63%);background-size:200% 100%;border-radius:999px;height:100%;position:relative;transition:width .6s ease;width:0}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.progress-labels{color:#474d56;display:flex;font-size:12px;justify-content:space-between;margin-top:8px}.team-card{margin-top:40px}.team-card .icon-box svg{width:16px}.team-card .team-header{align-items:center;display:flex;justify-content:space-between}.team-card .team-title{align-items:center;display:flex;font-size:14px;font-weight:600;gap:12px;letter-spacing:1px}.team-card .icon-box{align-items:center;background-color:#3b82f6;border-radius:.5rem;box-shadow:0 10px 15px -3px #3b82f64d;color:#fff;display:flex;font-size:18px;height:2rem;justify-content:center;width:2rem}.team-card .team-title span{color:#000;font-size:14px;font-weight:900;letter-spacing:.7px}.team-card .team-count{background-color:#3b82f6;border-radius:9999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;letter-spacing:.7px;padding:6px 12px}.team-card .team-members{align-items:center;display:flex;gap:14px;justify-content:center;margin-top:20px}.team-card .member{border:3px solid #fff;border-radius:50%;box-shadow:0 4px 12px #0000001a;font-size:22px;height:56px;position:relative;width:56px}.team-card .member,.team-card .member:after{align-items:center;color:#fff;display:flex;justify-content:center}.team-card .member:after{background:#3b82f6;background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/tick-mark-icon.svg);background-position:50%;background-repeat:no-repeat;background-size:75%;border:2px solid #fff;border-radius:50%;bottom:-4px;content:\"\";height:12px;padding:2px;position:absolute;right:-4px;width:12px}.team-card .member.blue{background:#2563eb}.team-card .member.purple{background:#9333ea}.team-card .member.pink{background:#ec4899}.team-card .member.orange{background:#f97316}.team-card .member.empty{background:#f8fafc;color:#9ca3af}.team-card .member.empty:after{display:none}.time-card{align-items:center;backdrop-filter:blur(8px);background:hsla(0,0%,100%,.2);border:1px solid hsla(0,0%,100%,.2);border-radius:20px;display:flex;gap:14px;margin-top:30px;padding:15px}.time-card .time-icon{align-items:center;background:#3b82f6;border-radius:14px;color:#fff;display:flex;flex-shrink:0;font-size:22px;height:48px;justify-content:center;width:48px}.time-card .time-content{display:flex;flex-direction:column}.time-card .time-label{color:#4b5563;font-size:12px;font-weight:600;letter-spacing:.6px}.time-card .time-value{color:#111827;font-size:22px;font-weight:900}.group-info-box{backdrop-filter:blur(8px);background:color-mix(in oklab,#fff 5%,transparent);border:1px solid color-mix(in oklab,#fff 20%,transparent);border-radius:1.5rem;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.offer-lock-status svg{height:16px;width:16px}.cb-lobby-modal-container .offer-box-content .reward-text{background:unset;border:none;color:#000;font-size:14px;line-height:1;margin-top:4px}.progress-header .title:before{background:#3b82f6;border-radius:50%;content:\"\";display:inline-block;height:8px;margin-right:8px;position:relative;width:8px}.live-activity-wrapper{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 80%,transparent);border-radius:18px;box-shadow:0 30px 80px rgba(0,0,0,.15);padding:16px}.live-activity-header{display:flex;gap:10px;justify-content:space-between;margin-bottom:12px}.live-activity-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;gap:8px;letter-spacing:.7px}.live-activity-header .dot{background:#3b82f6;border-radius:50%;height:8px;position:relative;width:8px}.live-activity-header .dot:after{animation:livePulse 1.6s ease-out infinite;background:rgba(59,130,246,.6);border-radius:50%;content:\"\";inset:0;position:absolute}@keyframes livePulse{0%{opacity:.8;transform:scale(1)}70%{opacity:0;transform:scale(2.4)}to{opacity:0}}.activity-stats{align-items:center;display:flex;gap:8px}.activity-stats-badge{background-color:rgba(59,130,246,.082);border-radius:999px;color:#3b82f6;font-size:12px;font-weight:500;line-height:1;padding:4px 10px}.activity-stats-badge.light{background-color:#f9f3f4;color:#45556c}.activity-list{height:104px;overflow:hidden;position:relative}.activity-card{align-items:center;background:linear-gradient(90deg,#fff,#f2f6ff);border:1px solid #dbeafe;border-radius:14px;display:flex;gap:10px;height:58px;inset:0;padding:12px 10px;position:absolute;transition:transform .6s cubic-bezier(.22,.61,.36,1),opacity .6s}.activity-card .text{font-size:14px}.activity-card .avatar{align-items:center;border-radius:50%;color:#fff;display:flex;flex:0 0 30px;height:30px;justify-content:center;width:30px}.activity-card .pink{background:linear-gradient(135deg,#ec4899,#f472b6)}.activity-card .purple{background:linear-gradient(135deg,#8b5cf6,#a78bfa)}.activity-card .blue{background:linear-gradient(135deg,#3b82f6,#60a5fa)}.activity-card .green{background:linear-gradient(135deg,#10b981,#34d399)}.activity-card .orange{background:linear-gradient(135deg,#f97316,#fb923c)}.activity-card .text p{color:#6b7280;font-size:12px;margin:2px 0 0}.activity-card .time{color:#94a3b8;font-size:10px;margin-left:auto}.lobby-activity-wp{backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.2);border-radius:25px;box-shadow:0 1px 3px 0 #0000001a;margin-top:50px;padding:8px}.lobby-close-icon{align-items:center;background-color:color-mix(in oklab,#000 80%,transparent);border-radius:50%;color:#fff;cursor:pointer;display:flex;height:34px;justify-content:center;position:fixed;right:30px;top:30px;width:34px;z-index:99}.lobby-close-icon svg{height:20px;width:20px}@media screen and (max-width:1600px){.cb-lobby-main{border-radius:30px;padding:50px}.title-wp h2{font-size:48px;margin-bottom:12px}.sub-title{font-size:16px}.title-wp{margin-top:20px}.lobby-link-section{margin-top:30px}.offer-box-content h3{font-size:26px}.lobby-offer-box{gap:24px;padding:15px}.cb-lobby-top{gap:80px}.group-info-box{border-radius:20px;padding:22px}.team-card{margin-top:30px}.team-card .team-members{margin-top:12px}.lobby-activity-wp{margin-top:40px}.lobby-close-icon{height:30px;top:20px;width:30px}}@media screen and (max-width:1280px){.cb-lobby-main,.cb-lobby-main-wp{padding:40px}.title-wp h2{font-size:42px}.cb-lobby-top{gap:60px}}@media screen and (max-width:1120px){.title-wp h2{font-size:38px}}@media screen and (max-width:991px){.cb-lobby-top{gap:30px;grid-template-columns:1fr}.cb-lobby-main{border-radius:15px;padding:30px}.cb-lobby-main-wp{padding:30px}.lobby-close-icon{right:20px}.lobby-link-box{border-radius:10px}.share-btn{border-radius:8px}.offer-box-content h3{font-size:24px}.lobby-activity-wp,.lobby-offer-box,.time-card{border-radius:15px;margin-top:20px}.live-activity-wrapper{border-radius:10px}.group-info-box{border-radius:15px}}@media screen and (max-width:767px){.link-share-wrapper{flex-direction:column!important}}@media screen and (max-width:575px){.cb-lobby-main,.cb-lobby-main-wp{padding:20px}.title-wp h2{font-size:30px}.lobby-link-text{font-size:11px;margin-bottom:4px}.lobby-link-url{font-size:14px}.lobby-link-box{min-height:48px;padding:12px 16px}.link-share-wrapper{gap:10px}.share-btn{border-radius:8px;font-size:13px;height:48px;min-width:auto;padding:0 14px}.lobby-offer-box{padding:10px}.offer-box-content h3{font-size:20px}.offer-box-content .reward-text{font-size:13px}.group-info-box{padding:13px}.progress-header .title,.team-card .team-title span{font-size:13px}.team-card .member{height:40px;width:40px}.team-card .member:after{height:8px;width:8px}.team-card .team-members{gap:10px;margin-top:12px}.time-card{padding:13px}.team-card{margin-top:22px}.activity-card .text{font-size:12px}.activity-card .text p{font-size:11px}.live-activity-header .title{font-size:12px}.time-card .time-value{font-size:20px}}@media screen and (max-width:480px){.cb-lobby-main-wp{padding:20px 12px}.cb-lobby-main{padding:15px 12px}.lobby-status{font-size:9px}.lobby-number{font-size:10px}.title-wp h2{font-size:28px;margin-bottom:7px}.sub-title{font-size:14px}.lobby-link-section{gap:10px;margin-top:20px}.lobby-offer-box{gap:12px}.offer-box-icon{height:45px;width:45px}.offer-box-content .reward-text,.offer-lock-status span{font-size:12px}.cb-lobby-top{gap:20px}.lobby-close-icon{right:10px;top:10px}.live-activity-header{flex-direction:column;gap:5px}.activity-card{border-radius:9px;padding:6px}}.share-overlay{align-items:center;background:rgba(0,0,0,.45);display:flex;inset:0;justify-content:center;opacity:0;position:fixed;transition:.3s ease;visibility:hidden;z-index:999}.share-overlay.active{opacity:1;visibility:visible}.share-popup{background:#fff;border-radius:18px;max-height:90%;overflow:auto;padding:24px;position:relative;transform:translateY(30px);transition:.35s ease;width:420px}.share-overlay.active .share-popup{transform:translateY(0)}.share-popup .close-btn{align-items:center;background:#f3f4f6;border:none;border-radius:50%;cursor:pointer;display:flex;height:32px;justify-content:center;position:absolute;right:14px;top:14px;transition:.3s;width:32px}.share-popup .close-btn:hover{background:#eeecec}.share-popup h2{font-size:22px;font-weight:600;line-height:1;margin-bottom:10px}.share-popup .subtitle{color:#4a5565;font-size:14px;margin:6px 0 30px}.share-popup .share-grid{display:grid;gap:14px;grid-template-columns:repeat(2,1fr)}.share-popup .share-card{align-items:center;background:#f2f6f9;border-radius:14px;cursor:pointer;display:flex;flex-direction:column;padding:16px;position:relative;text-align:center;transition:.25s ease}.share-popup .share-card:hover{transform:translateY(-3px)}.share-popup .share-card .icon{align-items:center;background:#1877f2;border-radius:50%;color:#fff;display:flex;font-size:30px;height:50px;justify-content:center;margin-bottom:8px;text-align:center;width:50px}.share-popup .share-card span{font-size:13px;font-weight:500}.share-popup .share-card.whatsapp{background:#ecfdf5;border:2px solid #22c55e;color:#22c55e}.share-popup .share-card.whatsapp .icon{background:#22c55e}.share-popup .share-card .badge{background:#2563eb;border-radius:12px;color:#fff;font-size:11px;padding:4px 10px;position:absolute;right:10px;top:-8px}.share-popup .link-box{background:#fbf9fa;border:1px solid #ebe6e7;border-radius:14px;margin-top:18px;padding:14px}.share-popup .link-box label{color:#64748b;font-size:13px}.share-popup .link-row{display:flex;gap:8px;margin-top:6px}.share-popup .link-row input{background:transparent;border:none;color:#334155;flex:1;font-size:13px;letter-spacing:.8px;line-height:1.2;outline:none}.share-popup .link-row button{align-items:center;background:#fff;border:1px solid #ebe6e7;border-radius:10px;cursor:pointer;display:flex;height:35px;justify-content:center;padding:6px 8px;width:35px}.share-popup .success{color:#2563eb;display:block;font-size:12px;margin-top:6px;opacity:0;transition:opacity .3s}.share-popup .success.show{opacity:1}.share-popup .footer-text{color:#64748b;font-size:12px;margin-top:14px;text-align:center}.share-popup .share-card.twitter .icon{background:#000}.share-popup .share-card.facebook .icon{background:#1877f2}.share-popup .share-card.sms .icon{background:#059669}.share-popup .share-card.copied .icon{background:#2563eb}@keyframes entrance-fade-in{0%{opacity:0}to{opacity:1}}@keyframes entrance-scale-in{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes entrance-text-fade-in{0%{opacity:0}to{opacity:1}}@keyframes exit-blur-scale{0%{filter:blur(0);opacity:1;transform:scale(1)}to{filter:blur(10px);opacity:0;transform:scale(1.1)}}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.5}}.entrance-animation-overlay{align-items:center;animation:entrance-fade-in .8s ease-in-out forwards;background-color:#0f172a;color:#fff;display:flex;flex-direction:column;inset:0;justify-content:center;position:fixed;z-index:9999}.entrance-animation-overlay.exit{animation:exit-blur-scale .8s ease-in-out forwards}.entrance-content{align-items:center;animation:entrance-scale-in .5s ease-out .2s both;display:flex;flex-direction:column}.entrance-icon-box{align-items:center;background:#2563eb;border-radius:16px;box-shadow:0 10px 25px rgba(37,99,235,.2);display:flex;height:64px;justify-content:center;margin-bottom:24px;width:64px}.entrance-icon-box svg{color:#fff;height:32px;width:32px}.entrance-title{font-size:32px;font-weight:700;letter-spacing:-.5px;margin-bottom:12px}@media (min-width:768px){.entrance-title{font-size:48px}}.entrance-message{align-items:center;animation:entrance-text-fade-in .5s ease-out .5s both;color:#60a5fa;display:flex;font-size:18px;font-weight:500;gap:8px}.entrance-pulse-dot{animation:pulse-dot 1.5s ease-in-out infinite;background-color:#60a5fa;border-radius:50%;height:8px;width:8px}";
2173
+ var css_248z = ".sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.cb-lobby-modal-container{height:100%;left:0;overflow-y:auto!important;position:fixed;top:0;width:100%;z-index:10000}.cb-lobby-modal-container *{box-sizing:border-box;margin:0;padding:0}.cb-lobby-modal-container body,.cb-lobby-modal-container html{font-family:Inter,sans-serif;height:100%;overflow-x:hidden;width:100%}.cb-lobby-main{backdrop-filter:blur(4px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:50px;box-shadow:0 25px 50px -12px #00000040;padding:80px;position:relative;width:100%;z-index:1}.cb-lobby-bg{background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/cb-back-image.png);background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:-1}.cb-lobby-main-wp{align-items:center;display:flex;justify-content:center;padding:40px 80px}.lobby-indicator{align-items:center;backdrop-filter:blur(8px);background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.3);border-radius:24px;display:flex;gap:12px;margin-bottom:16px;padding:6px 12px;width:fit-content}.lobby-indicator-logo{display:block;flex-shrink:0;height:34px;width:auto}.pulsing-dot{align-items:center;display:flex;height:8px;justify-content:center;position:relative;width:8px}.pulsing-dot:after{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.pulsing-dot:after,.pulsing-dot:before{background:#10b981;border-radius:50%;content:\"\";height:100%;position:absolute;width:100%}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.indicator-text{color:#047857;font-size:11px;font-weight:800;letter-spacing:.5px;line-height:1;text-transform:uppercase}.lobby-status-section{display:flex;flex-direction:column;margin-bottom:24px}.lobby-status{background:#fff;border-radius:4px;color:#000;font-size:10px;font-weight:600;line-height:1.2;padding:6px 10px;text-transform:uppercase}.lobby-status.active{background:#155dfc;color:#fff}.lobby-status.complete{background:#10b981;color:#fff}.lobby-status-wp{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.lobby-number{backdrop-filter:blur(3px);background:hsla(0,0%,100%,.2);border-radius:4px;box-shadow:0 25px 50px -12px #00000040;font-size:12px;font-weight:700;letter-spacing:1px;line-height:1.2;padding:5px 8px;text-transform:uppercase}.title-wp{margin-top:25px}.title-wp h2{font-size:60px;font-weight:900;line-height:1;margin-bottom:15px}.sub-title{-webkit-text-fill-color:transparent;animation:gradient-flow 4s linear infinite;background-clip:text;-webkit-background-clip:text;background-image:linear-gradient(90deg,#1e293b,#2563eb 25%,#1e293b 50%);background-size:200% auto;color:#1e293b;font-size:16px;font-weight:700;line-height:1.5;margin-bottom:0}@keyframes gradient-flow{0%{background-position:200% 0}to{background-position:-200% 0}}.sub-title.completed{-webkit-text-fill-color:unset;background-clip:unset;-webkit-background-clip:unset;background-image:none;color:#1e293b}.connected-section{backdrop-filter:blur(4px);background:hsla(0,0%,100%,.05);border:1px solid hsla(0,0%,100%,.1);border-radius:24px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex-direction:column;gap:20px;margin-top:24px;padding:24px;transition:all .3s ease}.connected-section:hover{background:hsla(0,0%,100%,.1);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.lobby-contact-protection-banner{align-items:center;background:#fffbeb;border:1px solid #f59e0b;border-radius:12px;display:flex;gap:12px;justify-content:space-between;padding:12px 14px}.lobby-contact-protection-copy{color:#7c2d12;font-size:13px;font-weight:600;line-height:1.4}.lobby-contact-protection-btn{background:#1e293b;border:none;border-radius:8px;color:#fff;cursor:pointer;font-size:12px;font-weight:700;padding:8px 12px;white-space:nowrap}.lobby-contact-protection-btn:hover{background:#0f172a}.lobby-protected-leave-container{align-items:center;background:#f0fdf4;border:1px solid #86efac;border-radius:12px;display:none;gap:12px;justify-content:space-between;padding:12px 14px}.lobby-protected-leave-copy{color:#166534;font-size:13px;font-weight:600;line-height:1.4}.lobby-protected-leave-btn{background:#b91c1c;border:none;border-radius:8px;color:#fff;cursor:pointer;font-size:12px;font-weight:700;padding:8px 12px;white-space:nowrap}.lobby-protected-leave-btn:hover{background:#991b1b}.lobby-contact-overlay{align-items:center;background:rgba(2,6,23,.5);display:flex;inset:0;justify-content:center;padding:20px;position:fixed;z-index:10020}.lobby-contact-card{background:#fff;border-radius:16px;box-shadow:0 12px 32px rgba(15,23,42,.25);display:flex;flex-direction:column;gap:12px;padding:20px;width:min(460px,100%)}.lobby-contact-title{color:#0f172a;font-size:22px;font-weight:800;line-height:1.2}.lobby-contact-subtitle{color:#334155;font-size:14px;line-height:1.5}.lobby-contact-input{border:1px solid #cbd5e1;border-radius:10px;font-size:14px;outline:none;padding:11px 12px;width:100%}.lobby-contact-input:focus{border-color:#1d4ed8;box-shadow:0 0 0 3px rgba(59,130,246,.2)}.lobby-contact-actions{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.lobby-contact-danger,.lobby-contact-primary,.lobby-contact-secondary{border:none;border-radius:10px;cursor:pointer;font-size:13px;font-weight:700;padding:10px 14px}.lobby-contact-danger:disabled,.lobby-contact-primary:disabled,.lobby-contact-secondary:disabled{cursor:not-allowed;opacity:.7}.lobby-contact-primary.is-loading{padding-right:34px;position:relative}.lobby-contact-primary.is-loading:after{animation:lobby-spin .8s linear infinite;border:2px solid hsla(0,0%,100%,.45);border-radius:50%;border-top-color:#fff;content:\"\";height:14px;margin-top:-7px;position:absolute;right:12px;top:50%;width:14px}@keyframes lobby-spin{to{transform:rotate(1turn)}}.lobby-contact-primary{background:#1d4ed8;color:#fff}.lobby-contact-primary:hover{background:#1e40af}.lobby-contact-secondary{background:#e2e8f0;color:#1e293b}.lobby-contact-secondary:hover{background:#cbd5e1}.lobby-contact-danger{background:#fee2e2;color:#991b1b}.lobby-contact-danger:hover{background:#fecaca}.lobby-contact-toast{background:#0f172a;border-radius:10px;bottom:30px;box-shadow:0 8px 20px rgba(0,0,0,.25);color:#fff;font-size:13px;font-weight:600;left:50%;padding:10px 14px;position:fixed;transform:translateX(-50%);z-index:10030}.lobby-blocking-loader-overlay{align-items:center;background:rgba(2,6,23,.45);display:flex;inset:0;justify-content:center;padding:20px;position:fixed;z-index:10035}.lobby-blocking-loader-card{align-items:center;background:#fff;border-radius:12px;box-shadow:0 12px 30px rgba(15,23,42,.24);display:flex;gap:10px;padding:16px 18px}.lobby-blocking-loader-spinner{animation:lobby-spin .8s linear infinite;border:2px solid #cbd5e1;border-radius:50%;border-top-color:#1d4ed8;height:16px;width:16px}.lobby-blocking-loader-text{color:#0f172a;font-size:14px;font-weight:600;margin:0}.link-share-container{display:flex;flex-direction:column}.link-share-wrapper{align-items:center;display:flex;gap:12px;width:100%}.lobby-link-box{align-items:center;backdrop-filter:blur(8px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.3);border-radius:14px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex:1;gap:16px;justify-content:space-between;min-height:50px;padding:16px 20px;transition:all .2s ease}.lobby-link-box:hover{background-color:hsla(0,0%,100%,.5);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.lobby-link-text{color:#71717a;font-size:10px;font-weight:700;letter-spacing:.6px;line-height:1.2;margin-bottom:6px;text-transform:uppercase}.copy-link-btn{align-items:center;background:transparent;border:none;border-radius:6px;color:#64748b;cursor:pointer;display:flex;flex-shrink:0;font-size:13px;font-weight:600;gap:6px;justify-content:center;padding:0;transition:all .2s ease;white-space:nowrap}.copy-link-btn:hover{color:#1e293b}.copy-link-btn .copy-text{display:none}.copy-link-btn svg{flex-shrink:0;height:18px;width:18px}@media (min-width:640px){.copy-link-btn .copy-text{display:inline}}@media (max-width:640px){.lobby-contact-protection-banner{align-items:flex-start;flex-direction:column}.lobby-contact-protection-btn{width:100%}.lobby-protected-leave-container{align-items:flex-start;flex-direction:column}.lobby-protected-leave-btn{width:100%}}.lobby-link-url{color:#1e293b;font-size:15px;font-weight:700;line-height:1.4;word-break:break-all}.link-box-container{flex:1;min-width:0}.share-btn{align-items:center;background:#1e293b;border:none;border-radius:14px;box-shadow:0 2px 4px 0 rgba(0,0,0,.1);color:#fff;cursor:pointer;display:flex;flex-shrink:0;font-size:15px;font-weight:600;gap:8px;height:50px;justify-content:center;min-width:auto;padding:35px 24px;transition:all .2s ease}.share-btn .share-text{color:#fff;display:inline;font-size:18px;margin:0!important}.share-btn svg{color:#fff;flex-shrink:0;height:18px;width:18px}@media (max-width:640px){.share-btn{font-size:14px;height:50px;padding:0 18px}.share-btn .share-text{color:#fff;display:inline}}.share-btn:hover{background:#0f172a;box-shadow:0 4px 8px 0 rgba(0,0,0,.15);transform:translateY(-1px)}.share-btn:active{transform:scale(.95)}.lobby-offer-box{align-items:center;backdrop-filter:blur(8px);background-color:rgba(59,130,246,.063);border:1px solid rgba(59,130,246,.125);border-radius:1rem;display:flex;gap:30px;margin-top:30px;padding:15px 20px}.offer-box-icon{align-items:center;background:linear-gradient(135deg,#3b82f6,rgba(59,130,246,.867));border-radius:10px;box-shadow:0 10px 15px -3px rgba(59,130,246,.314);color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;padding:5px;width:56px}.offer-lock-status{align-items:center;display:flex;gap:6px;margin-bottom:2px}.offer-lock-status span{font-size:14px;font-weight:700;line-height:1}.offer-box-content h3{font-size:30px;font-weight:900;line-height:1.2}.cb-lobby-top{display:grid;gap:100px;grid-template-columns:7fr 5fr}.group-info{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:20px;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.progress-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:14px}.progress-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;justify-content:center;letter-spacing:.7px;position:relative}.progress-badge{background:#3b82f6;border-radius:999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;padding:6px 12px}.cb-lobby-modal-container .progress-bar{background:#fff;border-radius:999px;height:14px;overflow:hidden;width:100%}.cb-lobby-modal-container .progress-fill{animation:shimmer 2.7s linear infinite;background:#3b82f6;background-image:linear-gradient(120deg,hsla(0,0%,100%,.15) 25%,hsla(0,0%,100%,.45) 37%,hsla(0,0%,100%,.15) 63%);background-size:200% 100%;border-radius:999px;height:100%;position:relative;transition:width .6s ease;width:0}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.progress-labels{color:#474d56;display:flex;font-size:12px;justify-content:space-between;margin-top:8px}.team-card{margin-top:40px}.team-card .icon-box svg{width:16px}.team-card .team-header{align-items:center;display:flex;justify-content:space-between}.team-card .team-title{align-items:center;display:flex;font-size:14px;font-weight:600;gap:12px;letter-spacing:1px}.team-card .icon-box{align-items:center;background-color:#3b82f6;border-radius:.5rem;box-shadow:0 10px 15px -3px #3b82f64d;color:#fff;display:flex;font-size:18px;height:2rem;justify-content:center;width:2rem}.team-card .team-title span{color:#000;font-size:14px;font-weight:900;letter-spacing:.7px}.team-card .team-count{background-color:#3b82f6;border-radius:9999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;letter-spacing:.7px;padding:6px 12px}.team-card .team-members{align-items:center;display:flex;gap:14px;justify-content:center;margin-top:20px}.team-card .member{border:3px solid #fff;border-radius:50%;box-shadow:0 4px 12px #0000001a;font-size:22px;height:56px;position:relative;width:56px}.team-card .member,.team-card .member:after{align-items:center;color:#fff;display:flex;justify-content:center}.team-card .member:after{background:#3b82f6;background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/tick-mark-icon.svg);background-position:50%;background-repeat:no-repeat;background-size:75%;border:2px solid #fff;border-radius:50%;bottom:-4px;content:\"\";height:12px;padding:2px;position:absolute;right:-4px;width:12px}.team-card .member.blue{background:#2563eb}.team-card .member.purple{background:#9333ea}.team-card .member.pink{background:#ec4899}.team-card .member.orange{background:#f97316}.team-card .member.empty{background:#f8fafc;color:#9ca3af}.team-card .member.empty:after{display:none}.time-card{align-items:center;backdrop-filter:blur(8px);background:hsla(0,0%,100%,.2);border:1px solid hsla(0,0%,100%,.2);border-radius:20px;display:flex;gap:14px;margin-top:30px;padding:15px}.time-card .time-icon{align-items:center;background:#3b82f6;border-radius:14px;color:#fff;display:flex;flex-shrink:0;font-size:22px;height:48px;justify-content:center;width:48px}.time-card .time-content{display:flex;flex-direction:column}.time-card .time-label{color:#4b5563;font-size:12px;font-weight:600;letter-spacing:.6px}.time-card .time-value{color:#111827;font-size:22px;font-weight:900}.group-info-box{backdrop-filter:blur(8px);background:color-mix(in oklab,#fff 5%,transparent);border:1px solid color-mix(in oklab,#fff 20%,transparent);border-radius:1.5rem;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.offer-lock-status svg{height:16px;width:16px}.cb-lobby-modal-container .offer-box-content .reward-text{background:unset;border:none;color:#000;font-size:14px;line-height:1;margin-top:4px}.progress-header .title:before{background:#3b82f6;border-radius:50%;content:\"\";display:inline-block;height:8px;margin-right:8px;position:relative;width:8px}.live-activity-wrapper{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 80%,transparent);border-radius:18px;box-shadow:0 30px 80px rgba(0,0,0,.15);padding:16px}.live-activity-header{display:flex;gap:10px;justify-content:space-between;margin-bottom:12px}.live-activity-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;gap:8px;letter-spacing:.7px}.live-activity-header .dot{background:#3b82f6;border-radius:50%;height:8px;position:relative;width:8px}.live-activity-header .dot:after{animation:livePulse 1.6s ease-out infinite;background:rgba(59,130,246,.6);border-radius:50%;content:\"\";inset:0;position:absolute}@keyframes livePulse{0%{opacity:.8;transform:scale(1)}70%{opacity:0;transform:scale(2.4)}to{opacity:0}}.activity-stats{align-items:center;display:flex;gap:8px}.activity-stats-badge{background-color:rgba(59,130,246,.082);border-radius:999px;color:#3b82f6;font-size:12px;font-weight:500;line-height:1;padding:4px 10px}.activity-stats-badge.light{background-color:#f9f3f4;color:#45556c}.activity-list{height:104px;overflow:hidden;position:relative}.activity-card{align-items:center;background:linear-gradient(90deg,#fff,#f2f6ff);border:1px solid #dbeafe;border-radius:14px;display:flex;gap:10px;height:58px;inset:0;padding:12px 10px;position:absolute;transition:transform .6s cubic-bezier(.22,.61,.36,1),opacity .6s}.activity-card .text{font-size:14px}.activity-card .avatar{align-items:center;border-radius:50%;color:#fff;display:flex;flex:0 0 30px;height:30px;justify-content:center;width:30px}.activity-card .pink{background:linear-gradient(135deg,#ec4899,#f472b6)}.activity-card .purple{background:linear-gradient(135deg,#8b5cf6,#a78bfa)}.activity-card .blue{background:linear-gradient(135deg,#3b82f6,#60a5fa)}.activity-card .green{background:linear-gradient(135deg,#10b981,#34d399)}.activity-card .orange{background:linear-gradient(135deg,#f97316,#fb923c)}.activity-card .text p{color:#6b7280;font-size:12px;margin:2px 0 0}.activity-card .time{color:#94a3b8;font-size:10px;margin-left:auto}.lobby-activity-wp{backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.2);border-radius:25px;box-shadow:0 1px 3px 0 #0000001a;margin-top:50px;padding:8px}.lobby-close-icon{align-items:center;background-color:color-mix(in oklab,#000 80%,transparent);border-radius:50%;color:#fff;cursor:pointer;display:flex;height:34px;justify-content:center;position:fixed;right:30px;top:30px;width:34px;z-index:99}.lobby-close-icon svg{height:20px;width:20px}@media screen and (max-width:1600px){.cb-lobby-main{border-radius:30px;padding:50px}.title-wp h2{font-size:48px;margin-bottom:12px}.sub-title{font-size:16px}.title-wp{margin-top:20px}.lobby-link-section{margin-top:30px}.offer-box-content h3{font-size:26px}.lobby-offer-box{gap:24px;padding:15px}.cb-lobby-top{gap:80px}.group-info-box{border-radius:20px;padding:22px}.team-card{margin-top:30px}.team-card .team-members{margin-top:12px}.lobby-activity-wp{margin-top:40px}.lobby-close-icon{height:30px;top:20px;width:30px}}@media screen and (max-width:1280px){.cb-lobby-main,.cb-lobby-main-wp{padding:40px}.title-wp h2{font-size:42px}.cb-lobby-top{gap:60px}}@media screen and (max-width:1120px){.title-wp h2{font-size:38px}}@media screen and (max-width:991px){.cb-lobby-top{gap:30px;grid-template-columns:1fr}.cb-lobby-main{border-radius:15px;padding:30px}.cb-lobby-main-wp{padding:30px}.lobby-close-icon{right:20px}.lobby-link-box{border-radius:10px}.share-btn{border-radius:8px}.offer-box-content h3{font-size:24px}.lobby-activity-wp,.lobby-offer-box,.time-card{border-radius:15px;margin-top:20px}.live-activity-wrapper{border-radius:10px}.group-info-box{border-radius:15px}}@media screen and (max-width:767px){.link-share-wrapper{flex-direction:column!important}}@media screen and (max-width:575px){.cb-lobby-main,.cb-lobby-main-wp{padding:20px}.title-wp h2{font-size:30px}.lobby-link-text{font-size:11px;margin-bottom:4px}.lobby-link-url{font-size:14px}.lobby-link-box{min-height:48px;padding:12px 16px}.link-share-wrapper{gap:10px}.share-btn{border-radius:8px;font-size:13px;height:48px;min-width:auto;padding:0 14px}.lobby-offer-box{padding:10px}.offer-box-content h3{font-size:20px}.offer-box-content .reward-text{font-size:13px}.group-info-box{padding:13px}.progress-header .title,.team-card .team-title span{font-size:13px}.team-card .member{height:40px;width:40px}.team-card .member:after{height:8px;width:8px}.team-card .team-members{gap:10px;margin-top:12px}.time-card{padding:13px}.team-card{margin-top:22px}.activity-card .text{font-size:12px}.activity-card .text p{font-size:11px}.live-activity-header .title{font-size:12px}.time-card .time-value{font-size:20px}}@media screen and (max-width:480px){.cb-lobby-main-wp{padding:20px 12px}.cb-lobby-main{padding:15px 12px}.lobby-status{font-size:9px}.lobby-number{font-size:10px}.title-wp h2{font-size:28px;margin-bottom:7px}.sub-title{font-size:14px}.lobby-link-section{gap:10px;margin-top:20px}.lobby-offer-box{gap:12px}.offer-box-icon{height:45px;width:45px}.offer-box-content .reward-text,.offer-lock-status span{font-size:12px}.cb-lobby-top{gap:20px}.lobby-close-icon{right:10px;top:10px}.live-activity-header{flex-direction:column;gap:5px}.activity-card{border-radius:9px;padding:6px}}.share-overlay{align-items:center;background:rgba(0,0,0,.45);display:flex;inset:0;justify-content:center;opacity:0;position:fixed;transition:.3s ease;visibility:hidden;z-index:999}.share-overlay.active{opacity:1;visibility:visible}.share-popup{background:#fff;border-radius:18px;max-height:90%;overflow:auto;padding:24px;position:relative;transform:translateY(30px);transition:.35s ease;width:420px}.share-overlay.active .share-popup{transform:translateY(0)}.share-popup .close-btn{align-items:center;background:#f3f4f6;border:none;border-radius:50%;cursor:pointer;display:flex;height:32px;justify-content:center;position:absolute;right:14px;top:14px;transition:.3s;width:32px}.share-popup .close-btn:hover{background:#eeecec}.share-popup h2{font-size:22px;font-weight:600;line-height:1;margin-bottom:10px}.share-popup .subtitle{color:#4a5565;font-size:14px;margin:6px 0 30px}.share-popup .share-grid{display:grid;gap:14px;grid-template-columns:repeat(2,1fr)}.share-popup .share-card{align-items:center;background:#f2f6f9;border-radius:14px;cursor:pointer;display:flex;flex-direction:column;padding:16px;position:relative;text-align:center;transition:.25s ease}.share-popup .share-card:hover{transform:translateY(-3px)}.share-popup .share-card .icon{align-items:center;background:#1877f2;border-radius:50%;color:#fff;display:flex;font-size:30px;height:50px;justify-content:center;margin-bottom:8px;text-align:center;width:50px}.share-popup .share-card span{font-size:13px;font-weight:500}.share-popup .share-card.whatsapp{background:#ecfdf5;border:2px solid #22c55e;color:#22c55e}.share-popup .share-card.whatsapp .icon{background:#22c55e}.share-popup .share-card .badge{background:#2563eb;border-radius:12px;color:#fff;font-size:11px;padding:4px 10px;position:absolute;right:10px;top:-8px}.share-popup .link-box{background:#fbf9fa;border:1px solid #ebe6e7;border-radius:14px;margin-top:18px;padding:14px}.share-popup .link-box label{color:#64748b;font-size:13px}.share-popup .link-row{display:flex;gap:8px;margin-top:6px}.share-popup .link-row input{background:transparent;border:none;color:#334155;flex:1;font-size:13px;letter-spacing:.8px;line-height:1.2;outline:none}.share-popup .link-row button{align-items:center;background:#fff;border:1px solid #ebe6e7;border-radius:10px;cursor:pointer;display:flex;height:35px;justify-content:center;padding:6px 8px;width:35px}.share-popup .success{color:#2563eb;display:block;font-size:12px;margin-top:6px;opacity:0;transition:opacity .3s}.share-popup .success.show{opacity:1}.share-popup .footer-text{color:#64748b;font-size:12px;margin-top:14px;text-align:center}.share-popup .share-card.twitter .icon{background:#000}.share-popup .share-card.facebook .icon{background:#1877f2}.share-popup .share-card.sms .icon{background:#059669}.share-popup .share-card.copied .icon{background:#2563eb}@keyframes entrance-fade-in{0%{opacity:0}to{opacity:1}}@keyframes entrance-scale-in{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes entrance-text-fade-in{0%{opacity:0}to{opacity:1}}@keyframes exit-blur-scale{0%{filter:blur(0);opacity:1;transform:scale(1)}to{filter:blur(10px);opacity:0;transform:scale(1.1)}}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.5}}.entrance-animation-overlay{align-items:center;animation:entrance-fade-in .8s ease-in-out forwards;background-color:#0f172a;color:#fff;display:flex;flex-direction:column;inset:0;justify-content:center;position:fixed;z-index:9999}.entrance-animation-overlay.exit{animation:exit-blur-scale .8s ease-in-out forwards}.entrance-content{align-items:center;animation:entrance-scale-in .5s ease-out .2s both;display:flex;flex-direction:column}.entrance-icon-box{align-items:center;background:#2563eb;border-radius:16px;box-shadow:0 10px 25px rgba(37,99,235,.2);display:flex;height:64px;justify-content:center;margin-bottom:24px;width:64px}.entrance-icon-box svg{color:#fff;height:32px;width:32px}.entrance-title{font-size:32px;font-weight:700;letter-spacing:-.5px;margin-bottom:12px}@media (min-width:768px){.entrance-title{font-size:48px}}.entrance-message{align-items:center;animation:entrance-text-fade-in .5s ease-out .5s both;color:#60a5fa;display:flex;font-size:18px;font-weight:500;gap:8px}.entrance-pulse-dot{animation:pulse-dot 1.5s ease-in-out infinite;background-color:#60a5fa;border-radius:50%;height:8px;width:8px}";
2163
2174
  styleInject(css_248z);
2164
2175
 
2165
2176
  /// <reference lib="dom" />
2177
+ const CONTACT_PROMPT_TITLE = "Protect your group spot";
2178
+ const CONTACT_PROMPT_SUBTITLE = "Add your email or phone so we can help you recover your group membership if you leave accidentally.";
2179
+ const CONTACT_PROMPT_PRIMARY_CTA = "Protect my spot";
2180
+ const CONTACT_PROMPT_REMIND_CTA = "Remind me later";
2181
+ const CONTACT_PROMPT_INVALID_INPUT = "Please enter a valid email or phone number.";
2182
+ const CONTACT_PROMPT_SAVE_ERROR = "Could not save contact right now. Please try again.";
2183
+ const CONTACT_PROTECTED_TOAST = "Spot protected. We can help recover your group membership.";
2184
+ const LEAVE_WARNING_TITLE = "Before you leave";
2185
+ const LEAVE_WARNING_SUBTITLE = "If you leave now without adding contact, you may lose this group spot and may not be able to rejoin the same group.";
2166
2186
  /**
2167
2187
  * LobbyModal - Renders and manages the group buying lobby modal
2168
2188
  */
2169
2189
  class LobbyModal {
2170
- constructor(data, callbacks, apiClient, socketManager = null, debug = false) {
2190
+ constructor(data, callbacks, apiClient, socketManager = null, analyticsClient = null, debug = false) {
2171
2191
  this.modalElement = null;
2172
2192
  this.timerInterval = null;
2173
2193
  this.activityInterval = null;
@@ -2176,6 +2196,17 @@ var CoBuySDK = (function (exports) {
2176
2196
  this.currentGroupId = null;
2177
2197
  this.shareOverlay = null;
2178
2198
  this.keyboardHandler = null;
2199
+ this.leaveFlowInProgress = false;
2200
+ this.leaveSignalSent = false;
2201
+ this.hasContactProtection = false;
2202
+ this.initialContactPromptTimer = null;
2203
+ this.contactPromptOverlay = null;
2204
+ this.leaveLoaderOverlay = null;
2205
+ this.beforeUnloadHandler = null;
2206
+ this.pageHideHandler = null;
2207
+ this.visibilityChangeHandler = null;
2208
+ this.CONTACT_PROTECTION_PREFIX = "cobuy_contact_protection";
2209
+ this.LAST_LEFT_GROUP_PREFIX = "cobuy_last_left_group";
2179
2210
  /**
2180
2211
  * Handle socket group update events
2181
2212
  */
@@ -2263,10 +2294,16 @@ var CoBuySDK = (function (exports) {
2263
2294
  this.updateData(updatePayload);
2264
2295
  // Also update team card to reflect new member count
2265
2296
  this.updateTeamCard(participants, max);
2297
+ // If group just became complete and we need offline codes for THIS user, fetch them
2298
+ if (isComplete && !this.data.offlineRedemption) {
2299
+ this.logger.info("[Socket] Group fulfilled - fetching current user's offline codes");
2300
+ this.fetchCurrentUserOfflineCodesIfNeeded();
2301
+ }
2266
2302
  };
2267
2303
  this.logger = new Logger(debug);
2268
2304
  this.apiClient = apiClient;
2269
2305
  this.socketManager = socketManager;
2306
+ this.analyticsClient = analyticsClient;
2270
2307
  // Log the group data being passed into the modal
2271
2308
  this.logger.info("LobbyModal initialized with group data", {
2272
2309
  groupId: data.groupId,
@@ -2278,14 +2315,385 @@ var CoBuySDK = (function (exports) {
2278
2315
  timeLeft: data.timeLeft,
2279
2316
  offlineRedemption: data.offlineRedemption,
2280
2317
  });
2281
- this.data = Object.assign({ groupNumber: "1000", status: "active", progress: 80, currentMembers: 4, totalMembers: 5, timeLeft: 1390, discount: "20% OFF", isLocked: true, activities: this.getDefaultActivities() }, data);
2318
+ this.data = Object.assign({ groupNumber: "1000", status: "active", progress: 80, currentMembers: 4, totalMembers: 5, timeLeft: 1390, discount: "20% OFF", isLocked: true, activities: this.getDefaultActivities(), redemptionMethod: "online" }, data);
2282
2319
  // Normalize optional flags so undefined values don't override defaults
2283
2320
  this.data.isLocked = this.computeIsLocked(this.data);
2284
2321
  if (!this.data.activities || !Array.isArray(this.data.activities)) {
2285
2322
  this.data.activities = this.getDefaultActivities();
2286
2323
  }
2324
+ this.hasContactProtection = this.readContactProtectionState();
2287
2325
  this.callbacks = callbacks;
2288
2326
  }
2327
+ getSessionId() {
2328
+ var _a;
2329
+ return ((_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId()) || "anonymous";
2330
+ }
2331
+ getContactProtectionStorageKey() {
2332
+ return `${this.CONTACT_PROTECTION_PREFIX}:${this.getSessionId()}:${this.data.productId}`;
2333
+ }
2334
+ getLastLeftGroupStorageKey() {
2335
+ return `${this.LAST_LEFT_GROUP_PREFIX}:${this.getSessionId()}:${this.data.productId}`;
2336
+ }
2337
+ readContactProtectionState() {
2338
+ if (typeof window === "undefined" || !window.localStorage) {
2339
+ return false;
2340
+ }
2341
+ try {
2342
+ return window.localStorage.getItem(this.getContactProtectionStorageKey()) === "1";
2343
+ }
2344
+ catch (_a) {
2345
+ return false;
2346
+ }
2347
+ }
2348
+ persistContactProtectionState(value) {
2349
+ this.hasContactProtection = value;
2350
+ if (typeof window === "undefined" || !window.localStorage) {
2351
+ return;
2352
+ }
2353
+ try {
2354
+ const key = this.getContactProtectionStorageKey();
2355
+ if (value) {
2356
+ window.localStorage.setItem(key, "1");
2357
+ }
2358
+ else {
2359
+ window.localStorage.removeItem(key);
2360
+ }
2361
+ }
2362
+ catch (_a) {
2363
+ // Ignore storage errors (private mode, quota exceeded, etc.)
2364
+ }
2365
+ }
2366
+ persistLastLeftGroup(groupId) {
2367
+ if (typeof window === "undefined" || !window.localStorage || !groupId) {
2368
+ return;
2369
+ }
2370
+ try {
2371
+ window.localStorage.setItem(this.getLastLeftGroupStorageKey(), JSON.stringify({
2372
+ groupId,
2373
+ productId: this.data.productId,
2374
+ leftAt: Date.now(),
2375
+ }));
2376
+ }
2377
+ catch (_a) {
2378
+ // Ignore storage errors
2379
+ }
2380
+ }
2381
+ clearInitialContactPromptTimer() {
2382
+ if (this.initialContactPromptTimer !== null) {
2383
+ window.clearTimeout(this.initialContactPromptTimer);
2384
+ this.initialContactPromptTimer = null;
2385
+ }
2386
+ }
2387
+ clearContactPromptOverlay() {
2388
+ if (this.contactPromptOverlay) {
2389
+ this.contactPromptOverlay.remove();
2390
+ this.contactPromptOverlay = null;
2391
+ }
2392
+ }
2393
+ showBlockingLoader(message) {
2394
+ if (!this.modalElement) {
2395
+ return;
2396
+ }
2397
+ this.hideBlockingLoader();
2398
+ const overlay = document.createElement("div");
2399
+ overlay.className = "lobby-blocking-loader-overlay";
2400
+ const card = document.createElement("div");
2401
+ card.className = "lobby-blocking-loader-card";
2402
+ const spinner = document.createElement("div");
2403
+ spinner.className = "lobby-blocking-loader-spinner";
2404
+ const text = document.createElement("p");
2405
+ text.className = "lobby-blocking-loader-text";
2406
+ text.textContent = message;
2407
+ card.appendChild(spinner);
2408
+ card.appendChild(text);
2409
+ overlay.appendChild(card);
2410
+ this.modalElement.appendChild(overlay);
2411
+ this.leaveLoaderOverlay = overlay;
2412
+ }
2413
+ hideBlockingLoader() {
2414
+ if (this.leaveLoaderOverlay) {
2415
+ this.leaveLoaderOverlay.remove();
2416
+ this.leaveLoaderOverlay = null;
2417
+ }
2418
+ }
2419
+ updateContactProtectionUI() {
2420
+ const banner = document.getElementById("lobbyContactProtectionBanner");
2421
+ const explicitLeaveContainer = document.getElementById("lobbyProtectedLeaveContainer");
2422
+ if (!banner)
2423
+ return;
2424
+ const canShowContactActions = this.data.status !== "complete" && Boolean(this.apiClient);
2425
+ if (!canShowContactActions) {
2426
+ banner.style.display = "none";
2427
+ if (explicitLeaveContainer) {
2428
+ explicitLeaveContainer.style.display = "none";
2429
+ }
2430
+ return;
2431
+ }
2432
+ if (this.hasContactProtection) {
2433
+ banner.style.display = "none";
2434
+ if (explicitLeaveContainer) {
2435
+ explicitLeaveContainer.style.display = "flex";
2436
+ }
2437
+ return;
2438
+ }
2439
+ banner.style.display = "flex";
2440
+ if (explicitLeaveContainer) {
2441
+ explicitLeaveContainer.style.display = "none";
2442
+ }
2443
+ }
2444
+ showToastMessage(message) {
2445
+ if (!this.modalElement) {
2446
+ return;
2447
+ }
2448
+ const existing = this.modalElement.querySelector(".lobby-contact-toast");
2449
+ if (existing) {
2450
+ existing.remove();
2451
+ }
2452
+ const toast = document.createElement("div");
2453
+ toast.className = "lobby-contact-toast";
2454
+ toast.textContent = message;
2455
+ this.modalElement.appendChild(toast);
2456
+ window.setTimeout(() => {
2457
+ toast.remove();
2458
+ }, 2400);
2459
+ }
2460
+ trackAnalyticsEvent(eventName, metadata) {
2461
+ if (!this.analyticsClient) {
2462
+ return;
2463
+ }
2464
+ this.analyticsClient
2465
+ .trackEvent(eventName, this.data.productId, Object.assign({ groupId: this.currentGroupId || this.data.groupId }, metadata))
2466
+ .catch((error) => {
2467
+ this.logger.warn(`[Analytics] Failed to track ${eventName}`, error);
2468
+ });
2469
+ }
2470
+ async saveContactValue(contactValue, promptSource) {
2471
+ const trimmed = contactValue.trim();
2472
+ if (!trimmed || !this.apiClient) {
2473
+ return false;
2474
+ }
2475
+ const contactType = this.inferContactType(trimmed);
2476
+ if (!contactType) {
2477
+ window.alert(CONTACT_PROMPT_INVALID_INPUT);
2478
+ return false;
2479
+ }
2480
+ const payload = contactType === "phone"
2481
+ ? { type: "phone", value: trimmed.replace(/\D/g, "") }
2482
+ : { type: "email", value: trimmed };
2483
+ const result = await this.apiClient.setContact(payload);
2484
+ if (!result.success) {
2485
+ this.logger.warn("Failed to save lobby contact", result.error);
2486
+ window.alert(CONTACT_PROMPT_SAVE_ERROR);
2487
+ return false;
2488
+ }
2489
+ this.persistContactProtectionState(true);
2490
+ this.trackAnalyticsEvent("CONTACT_INFO_SAVED", {
2491
+ promptSource,
2492
+ contactType,
2493
+ protectionState: "protected",
2494
+ });
2495
+ this.updateContactProtectionUI();
2496
+ this.showToastMessage(CONTACT_PROTECTED_TOAST);
2497
+ return true;
2498
+ }
2499
+ openContactProtectionPrompt(source) {
2500
+ if (typeof document === "undefined" || !this.modalElement) {
2501
+ return Promise.resolve("cancel");
2502
+ }
2503
+ this.clearContactPromptOverlay();
2504
+ this.trackAnalyticsEvent("CONTACT_PROMPT_SHOWN", {
2505
+ promptSource: source,
2506
+ protectionState: "unprotected",
2507
+ });
2508
+ return new Promise((resolve) => {
2509
+ var _a;
2510
+ const overlay = document.createElement("div");
2511
+ overlay.className = "lobby-contact-overlay";
2512
+ const card = document.createElement("div");
2513
+ card.className = "lobby-contact-card";
2514
+ const title = document.createElement("h3");
2515
+ title.className = "lobby-contact-title";
2516
+ title.textContent = CONTACT_PROMPT_TITLE;
2517
+ const subtitle = document.createElement("p");
2518
+ subtitle.className = "lobby-contact-subtitle";
2519
+ subtitle.textContent = CONTACT_PROMPT_SUBTITLE;
2520
+ const input = document.createElement("input");
2521
+ input.type = "text";
2522
+ input.className = "lobby-contact-input";
2523
+ input.placeholder = "Email or phone";
2524
+ input.autocomplete = "email";
2525
+ const actions = document.createElement("div");
2526
+ actions.className = "lobby-contact-actions";
2527
+ const primary = document.createElement("button");
2528
+ primary.type = "button";
2529
+ primary.className = "lobby-contact-primary";
2530
+ primary.textContent = CONTACT_PROMPT_PRIMARY_CTA;
2531
+ const secondary = document.createElement("button");
2532
+ secondary.type = "button";
2533
+ secondary.className = "lobby-contact-secondary";
2534
+ secondary.textContent = source === "leave-warning" ? "Cancel" : CONTACT_PROMPT_REMIND_CTA;
2535
+ const cleanupAndResolve = (result) => {
2536
+ this.clearContactPromptOverlay();
2537
+ resolve(result);
2538
+ };
2539
+ primary.addEventListener("click", async () => {
2540
+ const originalLabel = primary.textContent;
2541
+ primary.disabled = true;
2542
+ secondary.disabled = true;
2543
+ input.disabled = true;
2544
+ primary.classList.add("is-loading");
2545
+ primary.textContent = "Saving...";
2546
+ try {
2547
+ const saved = await this.saveContactValue(input.value, source);
2548
+ if (saved) {
2549
+ cleanupAndResolve("saved");
2550
+ }
2551
+ }
2552
+ finally {
2553
+ if (this.contactPromptOverlay) {
2554
+ primary.disabled = false;
2555
+ secondary.disabled = false;
2556
+ input.disabled = false;
2557
+ primary.classList.remove("is-loading");
2558
+ primary.textContent = originalLabel || CONTACT_PROMPT_PRIMARY_CTA;
2559
+ }
2560
+ }
2561
+ });
2562
+ secondary.addEventListener("click", () => {
2563
+ if (source !== "leave-warning") {
2564
+ this.trackAnalyticsEvent("CONTACT_PROMPT_DEFERRED", {
2565
+ promptSource: source,
2566
+ protectionState: "unprotected",
2567
+ });
2568
+ }
2569
+ cleanupAndResolve(source === "leave-warning" ? "cancel" : "later");
2570
+ });
2571
+ overlay.addEventListener("click", (event) => {
2572
+ if (event.target === overlay) {
2573
+ if (source !== "leave-warning") {
2574
+ this.trackAnalyticsEvent("CONTACT_PROMPT_DEFERRED", {
2575
+ promptSource: source,
2576
+ protectionState: "unprotected",
2577
+ });
2578
+ }
2579
+ cleanupAndResolve(source === "leave-warning" ? "cancel" : "later");
2580
+ }
2581
+ });
2582
+ actions.appendChild(primary);
2583
+ actions.appendChild(secondary);
2584
+ card.appendChild(title);
2585
+ card.appendChild(subtitle);
2586
+ card.appendChild(input);
2587
+ card.appendChild(actions);
2588
+ overlay.appendChild(card);
2589
+ (_a = this.modalElement) === null || _a === void 0 ? void 0 : _a.appendChild(overlay);
2590
+ this.contactPromptOverlay = overlay;
2591
+ window.setTimeout(() => input.focus(), 0);
2592
+ });
2593
+ }
2594
+ openUnprotectedLeaveWarning() {
2595
+ if (typeof document === "undefined" || !this.modalElement) {
2596
+ return Promise.resolve("cancel");
2597
+ }
2598
+ this.clearContactPromptOverlay();
2599
+ return new Promise((resolve) => {
2600
+ var _a;
2601
+ const overlay = document.createElement("div");
2602
+ overlay.className = "lobby-contact-overlay";
2603
+ const card = document.createElement("div");
2604
+ card.className = "lobby-contact-card";
2605
+ const title = document.createElement("h3");
2606
+ title.className = "lobby-contact-title";
2607
+ title.textContent = LEAVE_WARNING_TITLE;
2608
+ const subtitle = document.createElement("p");
2609
+ subtitle.className = "lobby-contact-subtitle";
2610
+ subtitle.textContent = LEAVE_WARNING_SUBTITLE;
2611
+ const actions = document.createElement("div");
2612
+ actions.className = "lobby-contact-actions";
2613
+ const addContactBtn = document.createElement("button");
2614
+ addContactBtn.type = "button";
2615
+ addContactBtn.className = "lobby-contact-primary";
2616
+ addContactBtn.textContent = "Add contact first";
2617
+ const leaveBtn = document.createElement("button");
2618
+ leaveBtn.type = "button";
2619
+ leaveBtn.className = "lobby-contact-danger";
2620
+ leaveBtn.textContent = "Leave anyway";
2621
+ const cancelBtn = document.createElement("button");
2622
+ cancelBtn.type = "button";
2623
+ cancelBtn.className = "lobby-contact-secondary";
2624
+ cancelBtn.textContent = "Cancel";
2625
+ const cleanup = (result) => {
2626
+ this.clearContactPromptOverlay();
2627
+ resolve(result);
2628
+ };
2629
+ addContactBtn.addEventListener("click", () => cleanup("add_contact"));
2630
+ leaveBtn.addEventListener("click", () => cleanup("leave"));
2631
+ cancelBtn.addEventListener("click", () => cleanup("cancel"));
2632
+ overlay.addEventListener("click", (event) => {
2633
+ if (event.target === overlay) {
2634
+ cleanup("cancel");
2635
+ }
2636
+ });
2637
+ actions.appendChild(addContactBtn);
2638
+ actions.appendChild(leaveBtn);
2639
+ actions.appendChild(cancelBtn);
2640
+ card.appendChild(title);
2641
+ card.appendChild(subtitle);
2642
+ card.appendChild(actions);
2643
+ overlay.appendChild(card);
2644
+ (_a = this.modalElement) === null || _a === void 0 ? void 0 : _a.appendChild(overlay);
2645
+ this.contactPromptOverlay = overlay;
2646
+ });
2647
+ }
2648
+ scheduleInitialContactPrompt() {
2649
+ if (!this.modalElement ||
2650
+ !this.shouldRunLeaveFlow() ||
2651
+ this.hasContactProtection ||
2652
+ !this.apiClient) {
2653
+ return;
2654
+ }
2655
+ this.clearInitialContactPromptTimer();
2656
+ this.initialContactPromptTimer = window.setTimeout(async () => {
2657
+ this.initialContactPromptTimer = null;
2658
+ if (!this.modalElement || this.hasContactProtection) {
2659
+ return;
2660
+ }
2661
+ await this.openContactProtectionPrompt("initial");
2662
+ this.updateContactProtectionUI();
2663
+ }, 2000);
2664
+ }
2665
+ async leaveGroupExplicitly() {
2666
+ var _a, _b;
2667
+ if (!this.apiClient || !this.currentGroupId || this.leaveFlowInProgress) {
2668
+ return;
2669
+ }
2670
+ this.leaveFlowInProgress = true;
2671
+ try {
2672
+ this.showBlockingLoader("Leaving group...");
2673
+ const leaveResult = await this.apiClient.leaveGroup(this.currentGroupId, {
2674
+ leave_source: "close_icon",
2675
+ leave_reason: "voluntary_with_contact",
2676
+ });
2677
+ if (!leaveResult.success) {
2678
+ this.logger.warn("Explicit leave group failed", leaveResult.error);
2679
+ this.hideBlockingLoader();
2680
+ window.alert("Could not leave the group right now. Please try again.");
2681
+ return;
2682
+ }
2683
+ this.leaveSignalSent = true;
2684
+ this.persistLastLeftGroup(((_b = (_a = leaveResult.data) === null || _a === void 0 ? void 0 : _a.group) === null || _b === void 0 ? void 0 : _b.id) || this.currentGroupId);
2685
+ this.trackAnalyticsEvent("PROTECTED_USER_EXPLICIT_LEAVE", {
2686
+ leaveSource: "close_icon",
2687
+ leaveReason: "voluntary_with_contact",
2688
+ protectionState: "protected",
2689
+ });
2690
+ this.close({ skipLeaveFlow: true });
2691
+ }
2692
+ finally {
2693
+ this.hideBlockingLoader();
2694
+ this.leaveFlowInProgress = false;
2695
+ }
2696
+ }
2289
2697
  /**
2290
2698
  * Derive lock state from data so UI reflects completion
2291
2699
  */
@@ -2602,6 +3010,7 @@ var CoBuySDK = (function (exports) {
2602
3010
  * Create connected section (subtitle + link + share)
2603
3011
  */
2604
3012
  createConnectedSection() {
3013
+ var _a, _b;
2605
3014
  const connectedSection = document.createElement("div");
2606
3015
  connectedSection.className = "connected-section";
2607
3016
  connectedSection.id = "lobbyConnectedSection";
@@ -2612,18 +3021,59 @@ var CoBuySDK = (function (exports) {
2612
3021
  subtitle.id = "lobbySubtitleText";
2613
3022
  subtitle.textContent = titleContent.subtitle;
2614
3023
  connectedSection.appendChild(subtitle);
3024
+ const contactBanner = document.createElement("div");
3025
+ contactBanner.className = "lobby-contact-protection-banner";
3026
+ contactBanner.id = "lobbyContactProtectionBanner";
3027
+ const contactCopy = document.createElement("p");
3028
+ contactCopy.className = "lobby-contact-protection-copy";
3029
+ contactCopy.textContent =
3030
+ "Spot not protected. Add contact so we can help recover your group membership.";
3031
+ const contactAction = document.createElement("button");
3032
+ contactAction.type = "button";
3033
+ contactAction.className = "lobby-contact-protection-btn";
3034
+ contactAction.textContent = "Add contact";
3035
+ contactAction.addEventListener("click", async () => {
3036
+ await this.openContactProtectionPrompt("banner");
3037
+ this.updateContactProtectionUI();
3038
+ });
3039
+ contactBanner.appendChild(contactCopy);
3040
+ contactBanner.appendChild(contactAction);
3041
+ connectedSection.appendChild(contactBanner);
3042
+ const protectedLeaveContainer = document.createElement("div");
3043
+ protectedLeaveContainer.className = "lobby-protected-leave-container";
3044
+ protectedLeaveContainer.id = "lobbyProtectedLeaveContainer";
3045
+ const protectedLeaveText = document.createElement("p");
3046
+ protectedLeaveText.className = "lobby-protected-leave-copy";
3047
+ protectedLeaveText.textContent = "You are protected and still part of this group.";
3048
+ const protectedLeaveAction = document.createElement("button");
3049
+ protectedLeaveAction.type = "button";
3050
+ protectedLeaveAction.className = "lobby-protected-leave-btn";
3051
+ protectedLeaveAction.textContent = "Leave group";
3052
+ protectedLeaveAction.addEventListener("click", () => {
3053
+ void this.leaveGroupExplicitly();
3054
+ });
3055
+ protectedLeaveContainer.appendChild(protectedLeaveText);
3056
+ protectedLeaveContainer.appendChild(protectedLeaveAction);
3057
+ connectedSection.appendChild(protectedLeaveContainer);
2615
3058
  // Check if group is fulfilled and has offline redemption
2616
3059
  const isComplete = !this.computeIsLocked(this.data);
2617
3060
  const hasOfflineRedemption = isComplete &&
2618
3061
  this.data.offlineRedemption &&
2619
- isValidOfflineRedemption(this.data.offlineRedemption);
3062
+ isValidOfflineRedemption(this.data.offlineRedemption) &&
3063
+ ((_a = this.data.redemptionMethod) !== null && _a !== void 0 ? _a : "online") !== "online";
2620
3064
  if (hasOfflineRedemption) {
2621
- // Show offline redemption view with integrated actions
3065
+ // Complete + offline/both: show offline redemption section
2622
3066
  const offlineSection = this.createOfflineRedemptionSection(this.data.offlineRedemption);
2623
3067
  connectedSection.appendChild(offlineSection);
2624
3068
  }
3069
+ else if (isComplete && ((_b = this.data.redemptionMethod) !== null && _b !== void 0 ? _b : "online") !== "offline") {
3070
+ // Complete + online/both (no offline redemption data): show only checkout CTA
3071
+ const checkoutBtn = this.createOnlineCheckoutButton();
3072
+ connectedSection.appendChild(checkoutBtn);
3073
+ this.injectOfflineRedemptionStyles();
3074
+ }
2625
3075
  else {
2626
- // Show link and share container
3076
+ // Group not yet complete: show link and share
2627
3077
  const linkShareContainer = document.createElement("div");
2628
3078
  linkShareContainer.className = "link-share-container";
2629
3079
  const linkWp = this.createLinkSection();
@@ -2667,6 +3117,23 @@ var CoBuySDK = (function (exports) {
2667
3117
  linkShareWrapper.appendChild(shareBtnInner);
2668
3118
  return linkShareWrapper;
2669
3119
  }
3120
+ /**
3121
+ * Create a standalone "Checkout Online" button for the link/share section
3122
+ */
3123
+ createOnlineCheckoutButton() {
3124
+ const btn = document.createElement("button");
3125
+ btn.className = "offline-online-checkout-btn lobby-online-checkout-btn";
3126
+ btn.textContent = "Checkout Online";
3127
+ btn.style.marginTop = "10px";
3128
+ btn.style.width = "100%";
3129
+ btn.addEventListener("click", () => {
3130
+ this.close();
3131
+ window.setTimeout(() => {
3132
+ this.triggerContinueToCheckout();
3133
+ }, 0);
3134
+ });
3135
+ return btn;
3136
+ }
2670
3137
  /**
2671
3138
  * Create offline redemption section
2672
3139
  */
@@ -2731,14 +3198,16 @@ var CoBuySDK = (function (exports) {
2731
3198
  onlineCheckoutBtn.className = "offline-online-checkout-btn";
2732
3199
  onlineCheckoutBtn.textContent = "Checkout Online";
2733
3200
  onlineCheckoutBtn.addEventListener("click", () => {
2734
- this.close();
3201
+ this.close({ skipLeaveFlow: true });
2735
3202
  // Ensure modal closes before triggering checkout intent
2736
3203
  window.setTimeout(() => {
2737
3204
  this.triggerContinueToCheckout();
2738
3205
  }, 0);
2739
3206
  });
2740
3207
  actionsRow.appendChild(downloadQRBtn);
2741
- actionsRow.appendChild(onlineCheckoutBtn);
3208
+ if (this.data.redemptionMethod !== "offline") {
3209
+ actionsRow.appendChild(onlineCheckoutBtn);
3210
+ }
2742
3211
  section.appendChild(topRow);
2743
3212
  section.appendChild(expiryInfo);
2744
3213
  section.appendChild(actionsRow);
@@ -3644,6 +4113,10 @@ var CoBuySDK = (function (exports) {
3644
4113
  this.logger.info(`Lobby modal opened for product: ${this.data.productId}`);
3645
4114
  // Set up keyboard accessibility (focus trap, ESC to close)
3646
4115
  this.setupKeyboardAccessibility();
4116
+ // Register lifecycle-based dropoff handling for browser/tab close.
4117
+ this.registerLifecycleLeaveHandlers();
4118
+ this.updateContactProtectionUI();
4119
+ this.scheduleInitialContactPrompt();
3647
4120
  });
3648
4121
  }
3649
4122
  /**
@@ -3709,19 +4182,166 @@ var CoBuySDK = (function (exports) {
3709
4182
  this.keyboardHandler = null;
3710
4183
  }
3711
4184
  }
4185
+ registerLifecycleLeaveHandlers() {
4186
+ if (typeof window === "undefined" || typeof document === "undefined") {
4187
+ return;
4188
+ }
4189
+ if (!this.shouldRunLeaveFlow()) {
4190
+ return;
4191
+ }
4192
+ if (!this.beforeUnloadHandler) {
4193
+ this.beforeUnloadHandler = () => {
4194
+ this.triggerBestEffortLeave("browser_close");
4195
+ };
4196
+ window.addEventListener("beforeunload", this.beforeUnloadHandler);
4197
+ }
4198
+ if (!this.pageHideHandler) {
4199
+ this.pageHideHandler = () => {
4200
+ this.triggerBestEffortLeave("browser_close");
4201
+ };
4202
+ window.addEventListener("pagehide", this.pageHideHandler);
4203
+ }
4204
+ if (!this.visibilityChangeHandler) {
4205
+ this.visibilityChangeHandler = () => {
4206
+ if (document.visibilityState === "hidden") {
4207
+ this.triggerBestEffortLeave("browser_close");
4208
+ }
4209
+ };
4210
+ document.addEventListener("visibilitychange", this.visibilityChangeHandler);
4211
+ }
4212
+ }
4213
+ unregisterLifecycleLeaveHandlers() {
4214
+ if (typeof window === "undefined" || typeof document === "undefined") {
4215
+ return;
4216
+ }
4217
+ if (this.beforeUnloadHandler) {
4218
+ window.removeEventListener("beforeunload", this.beforeUnloadHandler);
4219
+ this.beforeUnloadHandler = null;
4220
+ }
4221
+ if (this.pageHideHandler) {
4222
+ window.removeEventListener("pagehide", this.pageHideHandler);
4223
+ this.pageHideHandler = null;
4224
+ }
4225
+ if (this.visibilityChangeHandler) {
4226
+ document.removeEventListener("visibilitychange", this.visibilityChangeHandler);
4227
+ this.visibilityChangeHandler = null;
4228
+ }
4229
+ }
4230
+ triggerBestEffortLeave(source) {
4231
+ if (this.leaveSignalSent || this.leaveFlowInProgress) {
4232
+ return;
4233
+ }
4234
+ if (this.hasContactProtection) {
4235
+ return;
4236
+ }
4237
+ if (!this.apiClient || !this.currentGroupId || !this.shouldRunLeaveFlow()) {
4238
+ return;
4239
+ }
4240
+ this.leaveSignalSent = true;
4241
+ this.apiClient.leaveGroupBestEffort(this.currentGroupId, {
4242
+ leave_source: source,
4243
+ leave_reason: "voluntary_no_contact",
4244
+ });
4245
+ }
4246
+ shouldRunLeaveFlow() {
4247
+ if (!this.apiClient || !this.currentGroupId) {
4248
+ return false;
4249
+ }
4250
+ // Dropoff handling applies only while group is still active/forming.
4251
+ return this.data.status !== "complete";
4252
+ }
4253
+ inferContactType(value) {
4254
+ const trimmed = value.trim();
4255
+ if (!trimmed)
4256
+ return null;
4257
+ if (trimmed.includes("@")) {
4258
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4259
+ return emailRegex.test(trimmed) ? "email" : null;
4260
+ }
4261
+ const digitsOnly = trimmed.replace(/\D/g, "");
4262
+ return digitsOnly.length >= 10 && digitsOnly.length <= 15 ? "phone" : null;
4263
+ }
4264
+ async runLeaveFlowAndClose() {
4265
+ var _a, _b;
4266
+ if (this.leaveFlowInProgress)
4267
+ return;
4268
+ this.leaveFlowInProgress = true;
4269
+ try {
4270
+ if (this.hasContactProtection) {
4271
+ this.trackAnalyticsEvent("LOBBY_CLOSED_PROTECTED", {
4272
+ leaveSource: "close_icon",
4273
+ leaveReason: "stay_in_group",
4274
+ protectionState: "protected",
4275
+ });
4276
+ this.close({ skipLeaveFlow: true });
4277
+ return;
4278
+ }
4279
+ let leaveReason = "voluntary_no_contact";
4280
+ const leaveDecision = await this.openUnprotectedLeaveWarning();
4281
+ if (leaveDecision === "cancel") {
4282
+ return;
4283
+ }
4284
+ if (leaveDecision === "add_contact") {
4285
+ const promptResult = await this.openContactProtectionPrompt("leave-warning");
4286
+ if (promptResult === "saved") {
4287
+ this.updateContactProtectionUI();
4288
+ return;
4289
+ }
4290
+ else {
4291
+ return;
4292
+ }
4293
+ }
4294
+ if (this.apiClient && this.currentGroupId) {
4295
+ this.showBlockingLoader("Leaving group...");
4296
+ const leaveResult = await this.apiClient.leaveGroup(this.currentGroupId, {
4297
+ leave_source: "close_icon",
4298
+ leave_reason: leaveReason,
4299
+ });
4300
+ if (!leaveResult.success) {
4301
+ this.logger.warn("Leave group failed", leaveResult.error);
4302
+ this.hideBlockingLoader();
4303
+ window.alert("Could not leave the group right now. Please try again.");
4304
+ return;
4305
+ }
4306
+ this.leaveSignalSent = true;
4307
+ this.persistLastLeftGroup(((_b = (_a = leaveResult.data) === null || _a === void 0 ? void 0 : _a.group) === null || _b === void 0 ? void 0 : _b.id) || this.currentGroupId);
4308
+ this.trackAnalyticsEvent("LOBBY_LEFT_UNPROTECTED", {
4309
+ leaveSource: "close_icon",
4310
+ leaveReason,
4311
+ protectionState: "unprotected",
4312
+ });
4313
+ }
4314
+ this.close({ skipLeaveFlow: true });
4315
+ }
4316
+ finally {
4317
+ this.hideBlockingLoader();
4318
+ this.leaveFlowInProgress = false;
4319
+ }
4320
+ }
3712
4321
  /**
3713
4322
  * Close the modal
3714
4323
  */
3715
- close() {
4324
+ close(options = {}) {
3716
4325
  if (!this.modalElement) {
3717
4326
  this.logger.warn("Modal not open");
3718
4327
  return;
3719
4328
  }
4329
+ if (!options.skipLeaveFlow && this.shouldRunLeaveFlow()) {
4330
+ void this.runLeaveFlowAndClose();
4331
+ return;
4332
+ }
3720
4333
  // Stop timers and animations
3721
4334
  this.stopTimer();
3722
4335
  this.stopActivityAnimation();
3723
4336
  // Remove keyboard event listeners
3724
4337
  this.removeKeyboardAccessibility();
4338
+ // Remove lifecycle handlers for dropoff detection
4339
+ this.unregisterLifecycleLeaveHandlers();
4340
+ this.clearInitialContactPromptTimer();
4341
+ this.clearContactPromptOverlay();
4342
+ this.hideBlockingLoader();
4343
+ // Remove socket listeners to avoid duplicate handlers on reopen
4344
+ this.unsubscribeFromSocketEvents();
3725
4345
  // Remove modal from DOM
3726
4346
  document.body.removeChild(this.modalElement);
3727
4347
  this.modalElement = null;
@@ -3834,29 +4454,47 @@ var CoBuySDK = (function (exports) {
3834
4454
  * Update offline redemption visibility when group is fulfilled
3835
4455
  */
3836
4456
  updateOfflineRedemptionVisibility() {
4457
+ var _a, _b;
3837
4458
  const connectedSection = document.getElementById("lobbyConnectedSection");
3838
4459
  if (!connectedSection)
3839
4460
  return;
3840
4461
  const isComplete = !this.computeIsLocked(this.data);
3841
- const hasOfflineRedemption = this.data.offlineRedemption && isValidOfflineRedemption(this.data.offlineRedemption);
4462
+ const hasOfflineRedemption = this.data.offlineRedemption &&
4463
+ isValidOfflineRedemption(this.data.offlineRedemption) &&
4464
+ ((_a = this.data.redemptionMethod) !== null && _a !== void 0 ? _a : "online") !== "online";
3842
4465
  // Get existing elements
3843
4466
  const existingOffline = connectedSection.querySelector(".offline-redemption-section");
3844
4467
  const existingLink = connectedSection.querySelector(".link-share-container");
4468
+ const existingCheckoutBtn = connectedSection.querySelector(".lobby-online-checkout-btn");
3845
4469
  if (isComplete && hasOfflineRedemption) {
3846
- // Show offline redemption, hide link/share
3847
- if (existingLink) {
4470
+ // Complete + offline/both: show offline section, remove link/share and standalone checkout btn
4471
+ if (existingLink)
3848
4472
  existingLink.remove();
3849
- }
4473
+ if (existingCheckoutBtn)
4474
+ existingCheckoutBtn.remove();
3850
4475
  if (!existingOffline) {
3851
4476
  const offlineSection = this.createOfflineRedemptionSection(this.data.offlineRedemption);
3852
4477
  connectedSection.appendChild(offlineSection);
3853
4478
  }
3854
4479
  }
3855
- else {
3856
- // Show link/share, hide offline redemption
3857
- if (existingOffline) {
4480
+ else if (isComplete && ((_b = this.data.redemptionMethod) !== null && _b !== void 0 ? _b : "online") !== "offline") {
4481
+ // Complete + online/both (no offline data): show only checkout CTA, remove link/share
4482
+ if (existingLink)
4483
+ existingLink.remove();
4484
+ if (existingOffline)
3858
4485
  existingOffline.remove();
4486
+ if (!existingCheckoutBtn) {
4487
+ const checkoutBtn = this.createOnlineCheckoutButton();
4488
+ connectedSection.appendChild(checkoutBtn);
4489
+ this.injectOfflineRedemptionStyles();
3859
4490
  }
4491
+ }
4492
+ else {
4493
+ // Group not yet complete: show link/share, remove offline and checkout btn
4494
+ if (existingOffline)
4495
+ existingOffline.remove();
4496
+ if (existingCheckoutBtn)
4497
+ existingCheckoutBtn.remove();
3860
4498
  if (!existingLink) {
3861
4499
  const linkShareContainer = document.createElement("div");
3862
4500
  linkShareContainer.className = "link-share-container";
@@ -3890,9 +4528,47 @@ var CoBuySDK = (function (exports) {
3890
4528
  }
3891
4529
  window.addEventListener("group:fulfilled", this.handleSocketGroupUpdate);
3892
4530
  window.addEventListener("group:member:joined", this.handleSocketGroupUpdate);
4531
+ window.addEventListener("group:member:left", this.handleSocketGroupUpdate);
3893
4532
  window.addEventListener("group:created", this.handleSocketGroupUpdate);
3894
4533
  this.socketListenerRegistered = true;
3895
4534
  }
4535
+ unsubscribeFromSocketEvents() {
4536
+ if (typeof window === "undefined" || !this.socketListenerRegistered) {
4537
+ return;
4538
+ }
4539
+ window.removeEventListener("group:fulfilled", this.handleSocketGroupUpdate);
4540
+ window.removeEventListener("group:member:joined", this.handleSocketGroupUpdate);
4541
+ window.removeEventListener("group:member:left", this.handleSocketGroupUpdate);
4542
+ window.removeEventListener("group:created", this.handleSocketGroupUpdate);
4543
+ this.socketListenerRegistered = false;
4544
+ }
4545
+ /**
4546
+ * Fetch current user's offline redemption codes when group becomes fulfilled
4547
+ * This ensures we show the current user's codes, not someone else's
4548
+ */
4549
+ async fetchCurrentUserOfflineCodesIfNeeded() {
4550
+ var _a;
4551
+ if (!this.apiClient || !this.currentGroupId) {
4552
+ return;
4553
+ }
4554
+ try {
4555
+ this.logger.info("[Offline] Fetching current user's offline codes");
4556
+ const response = await this.apiClient.getGroupOfflineRedemption(this.currentGroupId);
4557
+ if (response.success && ((_a = response.data) === null || _a === void 0 ? void 0 : _a.offline_redemption)) {
4558
+ const offlineRedemption = response.data.offline_redemption;
4559
+ if (isValidOfflineRedemption(offlineRedemption)) {
4560
+ this.logger.info("[Offline] Successfully fetched current user's offline codes");
4561
+ this.updateData({ offlineRedemption });
4562
+ }
4563
+ }
4564
+ else {
4565
+ this.logger.warn("[Offline] Failed to fetch offline codes", response.error);
4566
+ }
4567
+ }
4568
+ catch (error) {
4569
+ this.logger.error("[Offline] Error fetching offline codes", error);
4570
+ }
4571
+ }
3896
4572
  /**
3897
4573
  * Create activity item from socket event data
3898
4574
  */
@@ -3923,6 +4599,10 @@ var CoBuySDK = (function (exports) {
3923
4599
  emoji = "🛒";
3924
4600
  action = "started a new group";
3925
4601
  break;
4602
+ case "group:member:left":
4603
+ emoji = "🚪";
4604
+ action = "left the group";
4605
+ break;
3926
4606
  default:
3927
4607
  emoji = "⚡";
3928
4608
  action = "activity in group";
@@ -4395,6 +5075,9 @@ var CoBuySDK = (function (exports) {
4395
5075
  WidgetState["LOADED"] = "loaded";
4396
5076
  WidgetState["ERROR"] = "error";
4397
5077
  })(WidgetState || (WidgetState = {}));
5078
+ const RECOVERY_TOAST_REJOINED = "Rejoined your previous group.";
5079
+ const RECOVERY_TOAST_NEXT_AVAILABLE = "Your previous group was full, so we joined you to the next available group.";
5080
+ const RECOVERY_TOAST_CREATED_NEW = "Your previous group was no longer available, so we created a fresh group for you.";
4398
5081
  /**
4399
5082
  * WidgetRoot handles rendering of the CoBuy widget into a DOM container
4400
5083
  */
@@ -4426,6 +5109,7 @@ var CoBuySDK = (function (exports) {
4426
5109
  this.groupExpiryRefreshTriggered = false; // Track if expiry refresh already triggered for current group
4427
5110
  this.offlineRedemption = null;
4428
5111
  this.offlineRedemptionModal = null;
5112
+ this.campaignRedemptionMethod = "online";
4429
5113
  this.isRendering = false;
4430
5114
  this.renderPromise = null;
4431
5115
  this.liveRegionAnnouncer = null;
@@ -4435,6 +5119,7 @@ var CoBuySDK = (function (exports) {
4435
5119
  this.renderDebounceTimer = null;
4436
5120
  this.pendingRenderOptions = null;
4437
5121
  this.RENDER_DEBOUNCE_MS = 300; // 300ms debounce for rapid re-renders
5122
+ this.LAST_LEFT_GROUP_PREFIX = "cobuy_last_left_group";
4438
5123
  /** Handle backend fulfillment notifications */
4439
5124
  this.handleGroupFulfilledEvent = (event) => {
4440
5125
  const detail = event.detail;
@@ -4478,6 +5163,80 @@ var CoBuySDK = (function (exports) {
4478
5163
  this.handleCTAClick(productId);
4479
5164
  }, 300); // 300ms debounce to prevent double-clicks
4480
5165
  }
5166
+ getRecoveryStorageKey(productId) {
5167
+ var _a;
5168
+ const sessionId = (_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId();
5169
+ if (!sessionId || !productId) {
5170
+ return null;
5171
+ }
5172
+ return `${this.LAST_LEFT_GROUP_PREFIX}:${sessionId}:${productId}`;
5173
+ }
5174
+ getStoredRecoveryGroupId(productId) {
5175
+ if (typeof window === "undefined" || !window.localStorage) {
5176
+ return null;
5177
+ }
5178
+ const key = this.getRecoveryStorageKey(productId);
5179
+ if (!key) {
5180
+ return null;
5181
+ }
5182
+ try {
5183
+ const raw = window.localStorage.getItem(key);
5184
+ if (!raw) {
5185
+ return null;
5186
+ }
5187
+ const parsed = JSON.parse(raw);
5188
+ if (!(parsed === null || parsed === void 0 ? void 0 : parsed.groupId) || parsed.productId !== productId) {
5189
+ return null;
5190
+ }
5191
+ return parsed.groupId;
5192
+ }
5193
+ catch (_a) {
5194
+ return null;
5195
+ }
5196
+ }
5197
+ clearStoredRecoveryGroupId(productId) {
5198
+ if (typeof window === "undefined" || !window.localStorage) {
5199
+ return;
5200
+ }
5201
+ const key = this.getRecoveryStorageKey(productId);
5202
+ if (!key) {
5203
+ return;
5204
+ }
5205
+ try {
5206
+ window.localStorage.removeItem(key);
5207
+ }
5208
+ catch (_a) {
5209
+ // Ignore storage issues
5210
+ }
5211
+ }
5212
+ showRecoveryToast(message) {
5213
+ if (typeof document === "undefined") {
5214
+ return;
5215
+ }
5216
+ const existing = document.getElementById("cobuy-recovery-toast");
5217
+ if (existing) {
5218
+ existing.remove();
5219
+ }
5220
+ const toast = document.createElement("div");
5221
+ toast.id = "cobuy-recovery-toast";
5222
+ toast.style.position = "fixed";
5223
+ toast.style.left = "50%";
5224
+ toast.style.bottom = "24px";
5225
+ toast.style.transform = "translateX(-50%)";
5226
+ toast.style.background = "#0f172a";
5227
+ toast.style.color = "#fff";
5228
+ toast.style.padding = "10px 14px";
5229
+ toast.style.borderRadius = "10px";
5230
+ toast.style.fontSize = "13px";
5231
+ toast.style.fontWeight = "600";
5232
+ toast.style.zIndex = "10040";
5233
+ toast.style.boxShadow = "0 8px 20px rgba(0,0,0,0.24)";
5234
+ toast.textContent = message;
5235
+ document.body.appendChild(toast);
5236
+ window.setTimeout(() => {
5237
+ toast.remove();
5238
+ }, 2800);
5239
+ }
4481
5240
  /** Subscribe once to backend socket events routed through the host page */
4482
5241
  subscribeToSocketEvents() {
4483
5242
  if (typeof window === "undefined" || this.socketListenerRegistered) {
@@ -4485,11 +5244,13 @@ var CoBuySDK = (function (exports) {
4485
5244
  }
4486
5245
  window.addEventListener("group:fulfilled", this.handleGroupFulfilledEvent);
4487
5246
  window.addEventListener("group:member:joined", this.handleGroupUpdatedEvent);
5247
+ window.addEventListener("group:member:left", this.handleGroupUpdatedEvent);
4488
5248
  window.addEventListener("group:created", this.handleGroupUpdatedEvent);
4489
5249
  this.socketListenerRegistered = true;
4490
5250
  }
4491
5251
  /** Fetch latest group data and re-render containers */
4492
5252
  async refreshGroupDataFromRealtime() {
5253
+ var _a;
4493
5254
  // Debounce rapid refreshes to prevent loops and reduce API load
4494
5255
  const now = Date.now();
4495
5256
  if (now - this.lastGroupDataRefreshTime < this.GROUP_REFRESH_DEBOUNCE) {
@@ -4506,21 +5267,29 @@ var CoBuySDK = (function (exports) {
4506
5267
  const groupData = await this.fetchPrimaryGroup(this.currentProductId);
4507
5268
  this.currentGroupData = groupData;
4508
5269
  this.currentGroupId = (groupData === null || groupData === void 0 ? void 0 : groupData.id) || this.currentGroupId;
5270
+ // If backend returned member-scoped offline redemption, this session is a member.
5271
+ const hasMemberOfflineRedemption = !!(groupData === null || groupData === void 0 ? void 0 : groupData.offline_redemption) && isValidOfflineRedemption(groupData.offline_redemption);
5272
+ if (hasMemberOfflineRedemption && !this.currentSessionId) {
5273
+ this.currentSessionId = ((_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId()) || null;
5274
+ }
4509
5275
  // If backend signals completion via counts, reflect it — but only for actual members.
4510
5276
  // Observers may see a full group returned by fetchPrimaryGroup; they should NOT enter
4511
5277
  // the checkout flow. currentSessionId is the membership signal (non-null = joined).
4512
5278
  if (groupData) {
4513
5279
  const participants = Number(groupData.participants_count || 0);
4514
5280
  const max = Number(groupData.max_participants || 0);
4515
- if (max > 0 && participants >= max && this.currentSessionId) {
5281
+ const isMember = !!this.currentSessionId || hasMemberOfflineRedemption;
5282
+ if (max > 0 && participants >= max && isMember) {
4516
5283
  this.groupFulfilled = true;
4517
5284
  // The primary group API returns offline_redemption for members of fulfilled groups.
4518
5285
  // Extract it here so renderFulfilledSummary shows the "Redeem In-store" link
4519
5286
  // (matching the UI users see on a full page reload).
4520
- if (groupData.offline_redemption &&
4521
- isValidOfflineRedemption(groupData.offline_redemption)) {
5287
+ if (hasMemberOfflineRedemption) {
4522
5288
  this.offlineRedemption = groupData.offline_redemption;
4523
5289
  }
5290
+ if (groupData.campaign_redemption_method) {
5291
+ this.campaignRedemptionMethod = groupData.campaign_redemption_method;
5292
+ }
4524
5293
  }
4525
5294
  }
4526
5295
  else {
@@ -4616,7 +5385,7 @@ var CoBuySDK = (function (exports) {
4616
5385
  }
4617
5386
  /** Persist fulfilled state, emit callback, and refresh UI */
4618
5387
  processGroupFulfilled(eventData) {
4619
- var _a, _b, _c, _d;
5388
+ var _a, _b, _c, _d, _e;
4620
5389
  this.groupFulfilled = true;
4621
5390
  // Extract reward from new event structure or fallback to legacy
4622
5391
  const reward = ((_a = eventData.frozen_reward) === null || _a === void 0 ? void 0 : _a.reward) || eventData.reward || null;
@@ -4631,7 +5400,10 @@ var CoBuySDK = (function (exports) {
4631
5400
  code: this.offlineRedemption.redemption_code,
4632
5401
  });
4633
5402
  }
4634
- (_d = (_c = this.events) === null || _c === void 0 ? void 0 : _c.onGroupFulfilled) === null || _d === void 0 ? void 0 : _d.call(_c, eventData);
5403
+ if ((_c = eventData.group) === null || _c === void 0 ? void 0 : _c.campaign_redemption_method) {
5404
+ this.campaignRedemptionMethod = eventData.group.campaign_redemption_method;
5405
+ }
5406
+ (_e = (_d = this.events) === null || _d === void 0 ? void 0 : _d.onGroupFulfilled) === null || _e === void 0 ? void 0 : _e.call(_d, eventData);
4635
5407
  this.renderFulfilledState();
4636
5408
  }
4637
5409
  /** Re-render widget and external containers to reflect fulfillment */
@@ -4771,8 +5543,10 @@ var CoBuySDK = (function (exports) {
4771
5543
  footer.style.justifyContent = "flex-end";
4772
5544
  footer.style.alignItems = "center";
4773
5545
  footer.style.gap = "12px";
4774
- // Add "Redeem In-store" CTA if offline redemption is available
4775
- if (this.offlineRedemption && isValidOfflineRedemption(this.offlineRedemption)) {
5546
+ // Add "Redeem In-store" CTA if offline redemption is available and campaign allows it
5547
+ if (this.offlineRedemption &&
5548
+ isValidOfflineRedemption(this.offlineRedemption) &&
5549
+ this.campaignRedemptionMethod !== "online") {
4776
5550
  const redeemLink = document.createElement("button");
4777
5551
  redeemLink.className = "cobuy-redeem-instore-link";
4778
5552
  redeemLink.style.background = "none";
@@ -4870,7 +5644,7 @@ var CoBuySDK = (function (exports) {
4870
5644
  }
4871
5645
  // Set callback to open lobby when a group is successfully joined
4872
5646
  this.groupListModal.setOnGroupJoined((joinData) => {
4873
- var _a;
5647
+ var _a, _b;
4874
5648
  // Update current group ID and session ID so the modal knows which group the user has joined
4875
5649
  this.currentGroupId = joinData.group.id;
4876
5650
  this.currentSessionId = ((_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId()) || null;
@@ -4884,8 +5658,9 @@ var CoBuySDK = (function (exports) {
4884
5658
  currentMembers: joinData.group.participants_count,
4885
5659
  totalMembers: joinData.group.max_participants,
4886
5660
  timeLeft: joinData.group.timeLeftSeconds,
5661
+ redemptionMethod: (_b = joinData.group.campaign_redemption_method) !== null && _b !== void 0 ? _b : this.campaignRedemptionMethod,
4887
5662
  };
4888
- const lobbyModal = new LobbyModal(lobbyData, {}, null, null, this.config.debug);
5663
+ const lobbyModal = new LobbyModal(lobbyData, {}, this.apiClient, null, this.analyticsClient, this.config.debug);
4889
5664
  lobbyModal.open(joinData.group.id);
4890
5665
  });
4891
5666
  // Set callback for viewing progress on already joined group
@@ -4901,8 +5676,9 @@ var CoBuySDK = (function (exports) {
4901
5676
  progress: Math.round((groupData.joined / groupData.total) * 100),
4902
5677
  currentMembers: groupData.joined,
4903
5678
  totalMembers: groupData.total,
5679
+ redemptionMethod: this.campaignRedemptionMethod,
4904
5680
  };
4905
- const lobbyModal = new LobbyModal(lobbyData, {}, null, null, this.config.debug);
5681
+ const lobbyModal = new LobbyModal(lobbyData, {}, this.apiClient, null, this.analyticsClient, this.config.debug);
4906
5682
  lobbyModal.open(groupId);
4907
5683
  });
4908
5684
  }
@@ -5086,6 +5862,9 @@ var CoBuySDK = (function (exports) {
5086
5862
  isValidOfflineRedemption(groupData.offline_redemption)) {
5087
5863
  this.offlineRedemption = groupData.offline_redemption;
5088
5864
  }
5865
+ if (groupData === null || groupData === void 0 ? void 0 : groupData.campaign_redemption_method) {
5866
+ this.campaignRedemptionMethod = groupData.campaign_redemption_method;
5867
+ }
5089
5868
  // Check if group is already fulfilled (full) on initial load
5090
5869
  if (groupData) {
5091
5870
  const participants = Number(groupData.participants_count || 0);
@@ -5807,10 +6586,99 @@ var CoBuySDK = (function (exports) {
5807
6586
  to { transform: rotate(360deg); }
5808
6587
  }
5809
6588
 
6589
+ .cobuy-prejoin-contact-overlay {
6590
+ position: fixed;
6591
+ inset: 0;
6592
+ z-index: 10045;
6593
+ background: rgba(2, 6, 23, 0.5);
6594
+ display: flex;
6595
+ align-items: center;
6596
+ justify-content: center;
6597
+ padding: 16px;
6598
+ }
6599
+
6600
+ .cobuy-prejoin-contact-card {
6601
+ width: min(440px, 100%);
6602
+ background: #fff;
6603
+ border-radius: 14px;
6604
+ padding: 18px;
6605
+ box-shadow: 0 14px 36px rgba(15, 23, 42, 0.22);
6606
+ display: flex;
6607
+ flex-direction: column;
6608
+ gap: 10px;
6609
+ }
6610
+
6611
+ .cobuy-prejoin-contact-title {
6612
+ font-size: 20px;
6613
+ font-weight: 800;
6614
+ color: #0f172a;
6615
+ margin: 0;
6616
+ }
6617
+
6618
+ .cobuy-prejoin-contact-subtitle {
6619
+ font-size: 14px;
6620
+ line-height: 1.5;
6621
+ color: #334155;
6622
+ margin: 0;
6623
+ }
6624
+
6625
+ .cobuy-prejoin-contact-input {
6626
+ width: 100%;
6627
+ border: 1px solid #cbd5e1;
6628
+ border-radius: 10px;
6629
+ padding: 11px 12px;
6630
+ font-size: 14px;
6631
+ outline: none;
6632
+ }
6633
+
6634
+ .cobuy-prejoin-contact-input:focus {
6635
+ border-color: #1d4ed8;
6636
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
6637
+ }
6638
+
6639
+ .cobuy-prejoin-contact-actions {
6640
+ display: flex;
6641
+ gap: 8px;
6642
+ flex-wrap: wrap;
6643
+ }
6644
+
6645
+ .cobuy-prejoin-contact-primary,
6646
+ .cobuy-prejoin-contact-secondary {
6647
+ border: none;
6648
+ border-radius: 10px;
6649
+ padding: 10px 12px;
6650
+ font-weight: 700;
6651
+ cursor: pointer;
6652
+ font-size: 13px;
6653
+ }
6654
+
6655
+ .cobuy-prejoin-contact-primary {
6656
+ background: #1d4ed8;
6657
+ color: #fff;
6658
+ }
6659
+
6660
+ .cobuy-prejoin-contact-primary:hover {
6661
+ background: #1e40af;
6662
+ }
6663
+
6664
+ .cobuy-prejoin-contact-secondary {
6665
+ background: #e2e8f0;
6666
+ color: #1e293b;
6667
+ }
6668
+
6669
+ .cobuy-prejoin-contact-secondary:hover {
6670
+ background: #cbd5e1;
6671
+ }
6672
+
5810
6673
  @media (max-width: 640px) {
5811
6674
  .cobuy-widget {
5812
6675
  grid-template-columns: 1fr;
5813
6676
  }
6677
+
6678
+ .cobuy-prejoin-contact-primary,
6679
+ .cobuy-prejoin-contact-secondary {
6680
+ width: 100%;
6681
+ }
5814
6682
  }
5815
6683
  `;
5816
6684
  document.head.appendChild(style);
@@ -5819,7 +6687,7 @@ var CoBuySDK = (function (exports) {
5819
6687
  * Handle CTA button click with analytics and modal opening
5820
6688
  */
5821
6689
  async handleCTAClick(productId) {
5822
- var _a, _b, _c, _d, _e, _f, _g;
6690
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
5823
6691
  this.logger.info(`CTA clicked for product: ${productId}`);
5824
6692
  // Track analytics event asynchronously (fire-and-forget)
5825
6693
  if (this.analyticsClient) {
@@ -5834,29 +6702,113 @@ var CoBuySDK = (function (exports) {
5834
6702
  // Join group before opening modal
5835
6703
  let groupJoinData = null;
5836
6704
  let inviteData = null;
6705
+ let recoveryGroupId = null;
5837
6706
  if (this.apiClient && this.currentGroupId) {
5838
6707
  try {
5839
- this.logger.info(`Joining group: ${this.currentGroupId}`);
5840
- if (this.analyticsClient) {
6708
+ recoveryGroupId = this.getStoredRecoveryGroupId(productId);
6709
+ if (this.analyticsClient && recoveryGroupId) {
5841
6710
  this.analyticsClient
5842
- .trackJoinAttempt(productId, this.currentGroupId)
6711
+ .trackEvent("RECOVERY_ATTEMPT", productId, {
6712
+ previousGroupId: recoveryGroupId,
6713
+ protectionState: "protected",
6714
+ })
5843
6715
  .catch((e) => this.logger.warn("Analytics tracking failed", e));
5844
6716
  }
5845
- const joinResponse = await this.apiClient.joinGroup(this.currentGroupId);
5846
- if (!joinResponse.success) {
5847
- this.logger.error("Failed to join group, modal will not open", joinResponse.error);
6717
+ const recoverResponse = await this.apiClient.recoverOrJoinGroup(productId);
6718
+ if (recoverResponse.success && recoverResponse.data) {
6719
+ groupJoinData = recoverResponse.data;
6720
+ this.currentGroupId = groupJoinData.group.id;
6721
+ this.clearStoredRecoveryGroupId(productId);
6722
+ if (this.analyticsClient && groupJoinData.recovery.recovered) {
6723
+ this.analyticsClient
6724
+ .trackEvent("RECOVERY_SUCCESS", productId, {
6725
+ previousGroupId: groupJoinData.recovery.previous_group_id || recoveryGroupId,
6726
+ joinedGroupId: groupJoinData.recovery.joined_group_id,
6727
+ recoveryOutcome: groupJoinData.recovery.outcome,
6728
+ protectionState: groupJoinData.recovery.matched_by === "none" ? "unprotected" : "protected",
6729
+ })
6730
+ .catch((e) => this.logger.warn("Analytics tracking failed", e));
6731
+ }
6732
+ else if (this.analyticsClient && recoveryGroupId) {
6733
+ this.analyticsClient
6734
+ .trackEvent("RECOVERY_FAILED", productId, {
6735
+ previousGroupId: recoveryGroupId,
6736
+ errorCode: "NOT_RECOVERED",
6737
+ errorMessage: "Recovery did not restore the previous membership.",
6738
+ protectionState: "protected",
6739
+ })
6740
+ .catch((e) => this.logger.warn("Analytics tracking failed", e));
6741
+ }
6742
+ switch (groupJoinData.recovery.outcome) {
6743
+ case "rejoined_previous_group":
6744
+ this.showRecoveryToast(RECOVERY_TOAST_REJOINED);
6745
+ break;
6746
+ case "joined_next_available_group":
6747
+ if (groupJoinData.recovery.recovered) {
6748
+ this.showRecoveryToast(RECOVERY_TOAST_NEXT_AVAILABLE);
6749
+ }
6750
+ break;
6751
+ case "created_new_group":
6752
+ if (groupJoinData.recovery.recovered) {
6753
+ this.showRecoveryToast(RECOVERY_TOAST_CREATED_NEW);
6754
+ }
6755
+ break;
6756
+ }
6757
+ }
6758
+ else {
6759
+ if (this.analyticsClient && recoveryGroupId) {
6760
+ const errCode = (_c = recoverResponse.error) === null || _c === void 0 ? void 0 : _c.code;
6761
+ const errMsg = (_d = recoverResponse.error) === null || _d === void 0 ? void 0 : _d.message;
6762
+ this.analyticsClient
6763
+ .trackEvent("RECOVERY_FAILED", productId, {
6764
+ previousGroupId: recoveryGroupId,
6765
+ errorCode: errCode || "RECOVERY_UNAVAILABLE",
6766
+ errorMessage: errMsg || "Recovery response was not successful.",
6767
+ protectionState: "protected",
6768
+ })
6769
+ .catch((e) => this.logger.warn("Analytics tracking failed", e));
6770
+ }
6771
+ const joinTargetGroupId = recoveryGroupId || this.currentGroupId;
6772
+ this.logger.info(`Joining group: ${joinTargetGroupId}`);
5848
6773
  if (this.analyticsClient) {
5849
- const errCode = (_c = joinResponse.error) === null || _c === void 0 ? void 0 : _c.code;
5850
- const errMsg = (_d = joinResponse.error) === null || _d === void 0 ? void 0 : _d.message;
5851
6774
  this.analyticsClient
5852
- .trackJoinFailure(productId, this.currentGroupId, errCode, errMsg)
6775
+ .trackJoinAttempt(productId, joinTargetGroupId)
5853
6776
  .catch((e) => this.logger.warn("Analytics tracking failed", e));
5854
6777
  }
5855
- this.setButtonLoadingState(false);
5856
- return; // Don't open modal on error
6778
+ let joinResponse = await this.apiClient.joinGroup(joinTargetGroupId);
6779
+ if (!joinResponse.success && recoveryGroupId && this.currentGroupId !== recoveryGroupId) {
6780
+ this.clearStoredRecoveryGroupId(productId);
6781
+ joinResponse = await this.apiClient.joinGroup(this.currentGroupId);
6782
+ }
6783
+ if (!joinResponse.success) {
6784
+ this.logger.error("Failed to join group, modal will not open", joinResponse.error);
6785
+ if (this.analyticsClient) {
6786
+ const errCode = (_e = joinResponse.error) === null || _e === void 0 ? void 0 : _e.code;
6787
+ const errMsg = (_f = joinResponse.error) === null || _f === void 0 ? void 0 : _f.message;
6788
+ this.analyticsClient
6789
+ .trackJoinFailure(productId, joinTargetGroupId, errCode, errMsg)
6790
+ .catch((e) => this.logger.warn("Analytics tracking failed", e));
6791
+ }
6792
+ this.setButtonLoadingState(false);
6793
+ return; // Don't open modal on error
6794
+ }
6795
+ groupJoinData = joinResponse.data;
6796
+ if ((_g = groupJoinData === null || groupJoinData === void 0 ? void 0 : groupJoinData.group) === null || _g === void 0 ? void 0 : _g.id) {
6797
+ this.currentGroupId = groupJoinData.group.id;
6798
+ }
6799
+ if (recoveryGroupId && ((_h = groupJoinData === null || groupJoinData === void 0 ? void 0 : groupJoinData.group) === null || _h === void 0 ? void 0 : _h.id)) {
6800
+ this.clearStoredRecoveryGroupId(productId);
6801
+ if (groupJoinData.group.id !== recoveryGroupId) {
6802
+ this.showRecoveryToast(RECOVERY_TOAST_NEXT_AVAILABLE);
6803
+ }
6804
+ else {
6805
+ this.showRecoveryToast(RECOVERY_TOAST_REJOINED);
6806
+ }
6807
+ }
5857
6808
  }
5858
- groupJoinData = joinResponse.data;
5859
6809
  this.logger.info("Successfully joined group", groupJoinData);
6810
+ // Mark this widget session as an active member for subsequent socket fulfillment handling.
6811
+ this.currentSessionId = ((_j = this.apiClient) === null || _j === void 0 ? void 0 : _j.getSessionId()) || this.currentSessionId;
5860
6812
  if (this.analyticsClient && groupJoinData) {
5861
6813
  this.analyticsClient
5862
6814
  .trackJoinSuccess(productId, groupJoinData.group.id)
@@ -5868,26 +6820,39 @@ var CoBuySDK = (function (exports) {
5868
6820
  isValidOfflineRedemption(groupJoinData.offline_redemption)
5869
6821
  ? groupJoinData.offline_redemption
5870
6822
  : null;
6823
+ const joinedGroupId = (_k = groupJoinData === null || groupJoinData === void 0 ? void 0 : groupJoinData.group) === null || _k === void 0 ? void 0 : _k.id;
5871
6824
  // Trigger invite tracking before opening lobby (global for product)
5872
- try {
5873
- const inviteResponse = await this.apiClient.inviteToGroup(this.currentGroupId, "copy_link");
5874
- if (inviteResponse.success && inviteResponse.data) {
5875
- inviteData = inviteResponse.data;
5876
- this.logger.info("Invite link generated", inviteData);
6825
+ if (joinedGroupId) {
6826
+ try {
6827
+ const inviteResponse = await this.apiClient.inviteToGroup(joinedGroupId, "copy_link");
6828
+ if (inviteResponse.success && inviteResponse.data) {
6829
+ inviteData = inviteResponse.data;
6830
+ this.logger.info("Invite link generated", inviteData);
6831
+ }
6832
+ else {
6833
+ this.logger.warn("Invite link generation failed", inviteResponse.error);
6834
+ }
5877
6835
  }
5878
- else {
5879
- this.logger.warn("Invite link generation failed", inviteResponse.error);
6836
+ catch (inviteError) {
6837
+ this.logger.warn("Invite request failed", inviteError);
5880
6838
  }
5881
6839
  }
5882
- catch (inviteError) {
5883
- this.logger.warn("Invite request failed", inviteError);
5884
- }
5885
6840
  }
5886
6841
  catch (error) {
5887
6842
  this.logger.error("Group join failed, modal will not open", error);
6843
+ if (this.analyticsClient && recoveryGroupId) {
6844
+ this.analyticsClient
6845
+ .trackEvent("RECOVERY_FAILED", productId, {
6846
+ previousGroupId: recoveryGroupId,
6847
+ errorCode: "EXCEPTION",
6848
+ errorMessage: error instanceof Error ? error.message : String(error),
6849
+ protectionState: "protected",
6850
+ })
6851
+ .catch((e) => this.logger.warn("Analytics tracking failed", e));
6852
+ }
5888
6853
  if (this.analyticsClient) {
5889
6854
  this.analyticsClient
5890
- .trackJoinFailure(productId, (_e = this.currentGroupId) !== null && _e !== void 0 ? _e : undefined, "EXCEPTION", error instanceof Error ? error.message : String(error))
6855
+ .trackJoinFailure(productId, (_l = this.currentGroupId) !== null && _l !== void 0 ? _l : undefined, "EXCEPTION", error instanceof Error ? error.message : String(error))
5891
6856
  .catch((e) => this.logger.warn("Analytics tracking failed", e));
5892
6857
  }
5893
6858
  this.setButtonLoadingState(false);
@@ -5904,7 +6869,7 @@ var CoBuySDK = (function (exports) {
5904
6869
  const progress = Math.round((groupJoinData.group.participants_count / groupJoinData.group.max_participants) * 100);
5905
6870
  // Format discount based on reward type
5906
6871
  let discountText = "";
5907
- if ((_g = (_f = this.currentRewardData) === null || _f === void 0 ? void 0 : _f.reward) === null || _g === void 0 ? void 0 : _g.value) {
6872
+ if ((_o = (_m = this.currentRewardData) === null || _m === void 0 ? void 0 : _m.reward) === null || _o === void 0 ? void 0 : _o.value) {
5908
6873
  const rewardType = this.currentRewardData.reward.type;
5909
6874
  const rewardValue = this.currentRewardData.reward.value;
5910
6875
  if (rewardType === "percentage" || rewardType === "cashback") {
@@ -5944,6 +6909,7 @@ var CoBuySDK = (function (exports) {
5944
6909
  shareMessage: shareMessageFromInvite,
5945
6910
  isLocked: !isGroupFulfilled,
5946
6911
  offlineRedemption: offlineRedemptionFromJoin,
6912
+ redemptionMethod: (_p = groupJoinData.group.campaign_redemption_method) !== null && _p !== void 0 ? _p : this.campaignRedemptionMethod,
5947
6913
  onShare: this.analyticsClient
5948
6914
  ? () => {
5949
6915
  this.analyticsClient.trackShareClick(productId, groupJoinData.group.id, "other").catch((e) => this.logger.warn("Analytics tracking failed", e));
@@ -6114,11 +7080,14 @@ var CoBuySDK = (function (exports) {
6114
7080
  PRODUCT_CONTEXT: "/v1/sdk/products/:productId/context",
6115
7081
  PRODUCT_REWARD: "/v1/sdk/products/:productId/reward",
6116
7082
  PRODUCT_PRIMARY_GROUP: "/v1/sdk/products/:productId/group/primary",
7083
+ PRODUCT_RECOVER_OR_JOIN_GROUP: "/v1/sdk/products/:productId/group/recover-or-join",
6117
7084
  // Group endpoints
6118
7085
  GROUP_JOIN: "/v1/sdk/groups/:groupId/join",
7086
+ GROUP_LEAVE: "/v1/sdk/groups/:groupId/leave",
6119
7087
  GROUP_CREATE_AND_JOIN: "/v1/sdk/groups/new/join",
6120
7088
  PRODUCT_ACTIVE_GROUPS: "/v1/sdk/products/:productId/groups/active",
6121
7089
  GROUP_INVITE: "/v1/sdk/groups/:groupId/invite",
7090
+ GROUP_OFFLINE_REDEMPTION: "/v1/sdk/groups/:groupId/offline-redemption",
6122
7091
  GROUP_CHECKOUT_PREPARE: "/v1/sdk/groups/:groupId/checkout/prepare",
6123
7092
  GROUP_CHECKOUT_VALIDATE: "/v1/sdk/groups/:groupId/checkout/validate",
6124
7093
  GROUP_CHECKOUT_CONFIRM: "/v1/sdk/groups/:groupId/checkout/confirm",
@@ -7206,6 +8175,98 @@ var CoBuySDK = (function (exports) {
7206
8175
  },
7207
8176
  };
7208
8177
  }
8178
+ /**
8179
+ * Leave an active group for the current SDK session.
8180
+ */
8181
+ async leaveGroup(groupId, params) {
8182
+ const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_LEAVE, { groupId });
8183
+ this.logger.info(`Leaving group: ${groupId}`);
8184
+ const response = await this.post(endpoint, {
8185
+ leave_source: (params === null || params === void 0 ? void 0 : params.leave_source) || "unknown",
8186
+ leave_reason: (params === null || params === void 0 ? void 0 : params.leave_reason) || "unknown",
8187
+ });
8188
+ if (response.success) {
8189
+ const payload = response.data;
8190
+ return {
8191
+ success: true,
8192
+ data: ((payload === null || payload === void 0 ? void 0 : payload.data) || response.data),
8193
+ };
8194
+ }
8195
+ return {
8196
+ success: false,
8197
+ error: response.error || {
8198
+ message: "Failed to leave group",
8199
+ code: "LEAVE_GROUP_ERROR",
8200
+ },
8201
+ };
8202
+ }
8203
+ /**
8204
+ * Fetch current session member's offline redemption codes for a group.
8205
+ */
8206
+ async getGroupOfflineRedemption(groupId) {
8207
+ const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_OFFLINE_REDEMPTION, {
8208
+ groupId,
8209
+ });
8210
+ const response = await this.get(endpoint);
8211
+ if (response.success) {
8212
+ const payload = response.data;
8213
+ return {
8214
+ success: true,
8215
+ data: (payload === null || payload === void 0 ? void 0 : payload.data) || {},
8216
+ };
8217
+ }
8218
+ return {
8219
+ success: false,
8220
+ error: response.error || {
8221
+ message: "Failed to fetch offline redemption",
8222
+ code: "OFFLINE_REDEMPTION_FETCH_ERROR",
8223
+ },
8224
+ };
8225
+ }
8226
+ async recoverOrJoinGroup(productId, contact) {
8227
+ var _a;
8228
+ const endpoint = buildApiUrl("", API_ENDPOINTS.PRODUCT_RECOVER_OR_JOIN_GROUP, {
8229
+ productId,
8230
+ });
8231
+ this.logger.info(`Recovering or joining group for product: ${productId}`);
8232
+ const response = await this.post(endpoint, contact ? { contact } : {});
8233
+ if (response.success && ((_a = response.data) === null || _a === void 0 ? void 0 : _a.data)) {
8234
+ return {
8235
+ success: true,
8236
+ data: response.data.data,
8237
+ };
8238
+ }
8239
+ return {
8240
+ success: false,
8241
+ error: response.error || {
8242
+ message: "Failed to recover or join group",
8243
+ code: "RECOVER_OR_JOIN_GROUP_ERROR",
8244
+ },
8245
+ };
8246
+ }
8247
+ /**
8248
+ * Best-effort leave signal for unload/pagehide scenarios.
8249
+ * Uses fetch keepalive so it can run while page is closing.
8250
+ */
8251
+ leaveGroupBestEffort(groupId, params) {
8252
+ if (!groupId)
8253
+ return;
8254
+ const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_LEAVE, { groupId });
8255
+ const url = `${this.baseUrl}${endpoint}`;
8256
+ const payload = JSON.stringify({
8257
+ leave_source: (params === null || params === void 0 ? void 0 : params.leave_source) || "browser_close",
8258
+ leave_reason: (params === null || params === void 0 ? void 0 : params.leave_reason) || "voluntary_no_contact",
8259
+ });
8260
+ const authHeaders = this.authStrategy.getHeaders();
8261
+ void fetch(url, {
8262
+ method: "POST",
8263
+ headers: Object.assign({ "Content-Type": "application/json", "X-CoBuy-SDK-Version": "1.0.0", "X-CoBuy-Session": this.sessionId }, authHeaders),
8264
+ body: payload,
8265
+ keepalive: true,
8266
+ }).catch((error) => {
8267
+ this.logger.debug("Best-effort leave request failed", error);
8268
+ });
8269
+ }
7209
8270
  /**
7210
8271
  * Prepare checkout for a group
7211
8272
  *
@@ -12035,15 +13096,16 @@ var CoBuySDK = (function (exports) {
12035
13096
  return;
12036
13097
  const bind = (eventName, handler) => {
12037
13098
  var _a;
12038
- if (!handler)
12039
- return;
12040
13099
  (_a = this.socket) === null || _a === void 0 ? void 0 : _a.on(eventName, (payload) => {
12041
13100
  this.logger.info(`[Socket] ${eventName} payload: ${this.formatPayload(payload)}`);
12042
13101
  this.dispatchWindowEvent(eventName, payload);
12043
- handler(payload);
13102
+ if (handler) {
13103
+ handler(payload);
13104
+ }
12044
13105
  });
12045
13106
  };
12046
13107
  bind("group:member:joined", handlers.onGroupMemberJoined);
13108
+ bind("group:member:left", handlers.onGroupMemberLeft);
12047
13109
  bind("group:created", handlers.onGroupCreated);
12048
13110
  bind("group:fulfilled", handlers.onGroupFulfilled);
12049
13111
  }
@@ -12054,6 +13116,7 @@ var CoBuySDK = (function (exports) {
12054
13116
  if (!this.socket)
12055
13117
  return;
12056
13118
  this.socket.off("group:member:joined");
13119
+ this.socket.off("group:member:left");
12057
13120
  this.socket.off("group:created");
12058
13121
  this.socket.off("group:fulfilled");
12059
13122
  }
@@ -13427,6 +14490,7 @@ var CoBuySDK = (function (exports) {
13427
14490
  activities: options.activities,
13428
14491
  isLocked: options.isLocked,
13429
14492
  offlineRedemption: options.offlineRedemption,
14493
+ redemptionMethod: options.redemptionMethod,
13430
14494
  };
13431
14495
  // Create modal instance
13432
14496
  const modal = new LobbyModal(modalData, {
@@ -13438,10 +14502,22 @@ var CoBuySDK = (function (exports) {
13438
14502
  if ((_a = config.events) === null || _a === void 0 ? void 0 : _a.onModalClose) {
13439
14503
  config.events.onModalClose(options.productId);
13440
14504
  }
14505
+ // Sync widget state after lobby closes so fulfilled CTAs/group view
14506
+ // reflect socket-driven changes without requiring a page reload.
14507
+ const refreshPromises = [];
14508
+ this.widgets.forEach((w) => {
14509
+ const pid = typeof w.getProductId === "function"
14510
+ ? w.getProductId()
14511
+ : null;
14512
+ if (pid === options.productId && typeof w.requestRefresh === "function") {
14513
+ refreshPromises.push(w.requestRefresh());
14514
+ }
14515
+ });
14516
+ void Promise.all(refreshPromises.map((p) => p.then(() => undefined).catch(() => undefined)));
13441
14517
  },
13442
14518
  onCopyLink: options.onCopyLink,
13443
14519
  onShare: options.onShare,
13444
- }, this.apiClient, this.socketManager, config.debug);
14520
+ }, this.apiClient, this.socketManager, this.analyticsClient, config.debug);
13445
14521
  // Store in map for persistence
13446
14522
  this.modals.set(modalKey, modal);
13447
14523
  // Maintain backward compatibility