@active-reach/web-sdk 1.29.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -311,12 +311,12 @@ const ICONS = {
311
311
  function escapeAttr(value) {
312
312
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
313
313
  }
314
- const SVG_NS = "http://www.w3.org/2000/svg";
314
+ const SVG_NS$1 = "http://www.w3.org/2000/svg";
315
315
  function lucideSvgEl(name, opts = {}) {
316
316
  const node = ICONS[name];
317
317
  if (!node || typeof document === "undefined") return null;
318
318
  const size = opts.size ?? 24;
319
- const svg = document.createElementNS(SVG_NS, "svg");
319
+ const svg = document.createElementNS(SVG_NS$1, "svg");
320
320
  svg.setAttribute("width", String(size));
321
321
  svg.setAttribute("height", String(size));
322
322
  svg.setAttribute("viewBox", "0 0 24 24");
@@ -333,7 +333,7 @@ function lucideSvgEl(name, opts = {}) {
333
333
  svg.setAttribute("aria-hidden", "true");
334
334
  }
335
335
  for (const [tag, attrs] of node) {
336
- const child = document.createElementNS(SVG_NS, tag);
336
+ const child = document.createElementNS(SVG_NS$1, tag);
337
337
  for (const [k, v] of Object.entries(attrs)) child.setAttribute(k, String(v));
338
338
  svg.appendChild(child);
339
339
  }
@@ -525,6 +525,16 @@ function applyPerCardOverride(tpl, look) {
525
525
  if (look.hide_cta) drop("cta");
526
526
  if (look.hide_rating) drop("rating");
527
527
  if (look.cta_text) elements = elements.map((e) => e.type === "cta" ? { ...e, text: look.cta_text } : e);
528
+ if (look.cta_action || look.cta_url || look.cta_custom_name) {
529
+ elements = elements.map(
530
+ (e) => e.type === "cta" ? {
531
+ ...e,
532
+ ...look.cta_action ? { cta_action: look.cta_action } : {},
533
+ ...look.cta_url ? { cta_url: look.cta_url } : {},
534
+ ...look.cta_custom_name ? { cta_custom_name: look.cta_custom_name } : {}
535
+ } : e
536
+ );
537
+ }
528
538
  if (look.badge) {
529
539
  const badgeEl = { type: "badge", text: look.badge, color: look.badge_color };
530
540
  elements = elements.some((e) => e.type === "badge") ? elements.map((e) => e.type === "badge" ? badgeEl : e) : [elements[0], badgeEl, ...elements.slice(1)].filter(Boolean);
@@ -538,7 +548,7 @@ function priceText(card) {
538
548
  if (card.body && /\d/.test(card.body)) return card.body;
539
549
  return void 0;
540
550
  }
541
- function paintPrice(card, el, accent, ctx) {
551
+ function paintPrice(card, el2, accent, ctx) {
542
552
  const price = priceText(card);
543
553
  if (price === void 0) return null;
544
554
  const meta = card.metadata || {};
@@ -547,10 +557,10 @@ function paintPrice(card, el, accent, ctx) {
547
557
  row.style.cssText = "display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;";
548
558
  const p = document.createElement("span");
549
559
  p.textContent = price;
550
- if (el.variant === "plain") {
560
+ if (el2.variant === "plain") {
551
561
  p.style.cssText = `color:${accent};font-weight:700;font-size:13px;`;
552
562
  } else {
553
- const pillBg = el.color ? ctx.sanitizeColor(el.color) : "#059669";
563
+ const pillBg = el2.color ? ctx.sanitizeColor(el2.color) : "#059669";
554
564
  p.style.cssText = `background:${pillBg};color:#fff;font-weight:800;font-size:12px;padding:2px 7px;border-radius:6px;box-shadow:0 2px 6px rgba(0,0,0,0.30);`;
555
565
  }
556
566
  row.appendChild(p);
@@ -602,14 +612,14 @@ function contrastFg(bg) {
602
612
  const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
603
613
  return lum < 0.5 ? "#ffffff" : "#1f2937";
604
614
  }
605
- function buildCountdownElement(el, fg, muted) {
606
- const targetStr = typeof el.countdown_target === "string" ? el.countdown_target : "";
615
+ function buildCountdownElement(el2, fg, muted) {
616
+ const targetStr = typeof el2.countdown_target === "string" ? el2.countdown_target : "";
607
617
  const target = targetStr ? Date.parse(targetStr) : NaN;
608
618
  if (!targetStr || isNaN(target)) return null;
609
619
  const wrap = document.createElement("div");
610
620
  wrap.style.cssText = "display:flex;align-items:baseline;gap:8px;font-variant-numeric:tabular-nums;margin:2px 0;";
611
621
  const label = document.createElement("span");
612
- label.textContent = typeof el.countdown_label === "string" && el.countdown_label || "Ends in";
622
+ label.textContent = typeof el2.countdown_label === "string" && el2.countdown_label || "Ends in";
613
623
  label.style.cssText = `font-size:12px;color:${muted};`;
614
624
  const time = document.createElement("span");
615
625
  time.style.cssText = `font-size:15px;font-weight:700;color:${fg};letter-spacing:0.2px;`;
@@ -631,13 +641,13 @@ function buildCountdownElement(el, fg, muted) {
631
641
  timer = setInterval(tick, 1e3);
632
642
  return wrap;
633
643
  }
634
- function appendRatingThanks(wrap, el, ctx, fg, accent, isHigh) {
635
- appendThanks(wrap, el, fg, "Thanks for rating us!");
636
- const url = isHigh && typeof el.rating_high_url === "string" ? ctx.sanitizeUrl(el.rating_high_url) : "";
644
+ function appendRatingThanks(wrap, el2, ctx, fg, accent, isHigh) {
645
+ appendThanks(wrap, el2, fg, "Thanks for rating us!");
646
+ const url = isHigh && typeof el2.rating_high_url === "string" ? ctx.sanitizeUrl(el2.rating_high_url) : "";
637
647
  if (url) {
638
648
  const cta = document.createElement("button");
639
649
  cta.type = "button";
640
- cta.textContent = el.rating_high_cta || "Leave a review";
650
+ cta.textContent = el2.rating_high_cta || "Leave a review";
641
651
  cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;
642
652
  cta.addEventListener("click", () => {
643
653
  ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "rating_input", cta: "high_review" } });
@@ -647,10 +657,10 @@ function appendRatingThanks(wrap, el, ctx, fg, accent, isHigh) {
647
657
  wrap.appendChild(cta);
648
658
  }
649
659
  }
650
- function renderRatingFollowupStep(wrap, el, ctx, stars, fg, accent) {
660
+ function renderRatingFollowupStep(wrap, el2, ctx, stars2, fg, accent) {
651
661
  while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
652
662
  const heading = document.createElement("div");
653
- heading.textContent = el.rating_followup_prompt || "Sorry to hear that — what went wrong?";
663
+ heading.textContent = el2.rating_followup_prompt || "Sorry to hear that — what went wrong?";
654
664
  heading.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin-bottom:8px;`;
655
665
  wrap.appendChild(heading);
656
666
  const ta = document.createElement("textarea");
@@ -664,9 +674,9 @@ function renderRatingFollowupStep(wrap, el, ctx, stars, fg, accent) {
664
674
  if (done) return;
665
675
  done = true;
666
676
  const comment = withComment ? ta.value.trim() : "";
667
- void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "rating", comment ? { stars, comment } : { stars }));
677
+ void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "rating", comment ? { stars: stars2, comment } : { stars: stars2 }));
668
678
  (_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, "acknowledge");
669
- appendThanks(wrap, el, fg, "Thanks — we’ll do better.");
679
+ appendThanks(wrap, el2, fg, "Thanks — we’ll do better.");
670
680
  };
671
681
  const btnRow = document.createElement("div");
672
682
  btnRow.style.cssText = "display:flex;gap:8px;justify-content:center;";
@@ -685,23 +695,23 @@ function renderRatingFollowupStep(wrap, el, ctx, stars, fg, accent) {
685
695
  wrap.appendChild(btnRow);
686
696
  ta.focus();
687
697
  }
688
- function buildRatingInputElement(el, ctx, fg) {
698
+ function buildRatingInputElement(el2, ctx, fg) {
689
699
  const accent = ctx.sanitizeColor(ctx.campaign.background_color || "#4169e1");
690
700
  const wrap = document.createElement("div");
691
701
  wrap.style.cssText = "display:flex;flex-direction:column;gap:6px;margin:2px 0;";
692
- if (el.content) {
702
+ if (el2.content) {
693
703
  const q = document.createElement("div");
694
- q.textContent = el.content;
704
+ q.textContent = el2.content;
695
705
  q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;
696
706
  wrap.appendChild(q);
697
707
  }
698
- const scale = typeof el.rating_scale === "number" && el.rating_scale >= 2 && el.rating_scale <= 10 ? Math.floor(el.rating_scale) : 5;
708
+ const scale = typeof el2.rating_scale === "number" && el2.rating_scale >= 2 && el2.rating_scale <= 10 ? Math.floor(el2.rating_scale) : 5;
699
709
  const row = document.createElement("div");
700
710
  row.style.cssText = "display:flex;gap:6px;";
701
- const stars = [];
711
+ const stars2 = [];
702
712
  let submitted = false;
703
713
  const paint = (upto) => {
704
- stars.forEach((s, idx) => {
714
+ stars2.forEach((s, idx) => {
705
715
  const on = idx < upto;
706
716
  s.setAttribute("stroke", on ? "#f59e0b" : "#d1d5db");
707
717
  s.setAttribute("fill", on ? "#f59e0b" : "none");
@@ -715,7 +725,7 @@ function buildRatingInputElement(el, ctx, fg) {
715
725
  const svg = lucideSvgEl("star", { size: 26, color: "#d1d5db" });
716
726
  if (svg) {
717
727
  btn.appendChild(svg);
718
- stars.push(svg);
728
+ stars2.push(svg);
719
729
  }
720
730
  btn.addEventListener("mouseenter", () => {
721
731
  if (!submitted) paint(i);
@@ -729,13 +739,13 @@ function buildRatingInputElement(el, ctx, fg) {
729
739
  submitted = true;
730
740
  paint(i);
731
741
  ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "rating_input", stars: i } });
732
- const lowThreshold = typeof el.rating_followup_threshold === "number" ? el.rating_followup_threshold : Math.ceil(scale * 0.8);
733
- if (el.rating_followup_enabled === true && i <= lowThreshold) {
734
- renderRatingFollowupStep(wrap, el, ctx, i, fg, accent);
742
+ const lowThreshold = typeof el2.rating_followup_threshold === "number" ? el2.rating_followup_threshold : Math.ceil(scale * 0.8);
743
+ if (el2.rating_followup_enabled === true && i <= lowThreshold) {
744
+ renderRatingFollowupStep(wrap, el2, ctx, i, fg, accent);
735
745
  } else {
736
746
  void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "rating", { stars: i }));
737
747
  (_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, i >= Math.ceil(scale * 0.6) ? "affirm" : "acknowledge");
738
- appendRatingThanks(wrap, el, ctx, fg, accent, i >= Math.ceil(scale * 0.6));
748
+ appendRatingThanks(wrap, el2, ctx, fg, accent, i >= Math.ceil(scale * 0.6));
739
749
  }
740
750
  });
741
751
  row.appendChild(btn);
@@ -743,8 +753,8 @@ function buildRatingInputElement(el, ctx, fg) {
743
753
  wrap.appendChild(row);
744
754
  return wrap;
745
755
  }
746
- function appendThanks(wrap, el, fg, fallback) {
747
- const msg = typeof el.thanks_text === "string" && el.thanks_text.trim() ? el.thanks_text : fallback;
756
+ function appendThanks(wrap, el2, fg, fallback) {
757
+ const msg = typeof el2.thanks_text === "string" && el2.thanks_text.trim() ? el2.thanks_text : fallback;
748
758
  if (!msg) return;
749
759
  const note = document.createElement("div");
750
760
  note.textContent = msg;
@@ -752,14 +762,14 @@ function appendThanks(wrap, el, fg, fallback) {
752
762
  note.style.cssText = `font-size:12px;font-weight:600;color:${fg};opacity:0.85;margin-top:4px;`;
753
763
  wrap.appendChild(note);
754
764
  }
755
- function buildPollElement(el, ctx, fg, accent) {
756
- const opts = Array.isArray(el.poll_options) ? el.poll_options.filter((o) => typeof o === "string" && o.trim().length > 0) : [];
765
+ function buildPollElement(el2, ctx, fg, accent) {
766
+ const opts = Array.isArray(el2.poll_options) ? el2.poll_options.filter((o) => typeof o === "string" && o.trim().length > 0) : [];
757
767
  if (opts.length < 2) return null;
758
768
  const wrap = document.createElement("div");
759
769
  wrap.style.cssText = "display:flex;flex-direction:column;gap:6px;margin:2px 0;";
760
- if (el.content) {
770
+ if (el2.content) {
761
771
  const q = document.createElement("div");
762
- q.textContent = el.content;
772
+ q.textContent = el2.content;
763
773
  q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;
764
774
  wrap.appendChild(q);
765
775
  }
@@ -789,24 +799,24 @@ function buildPollElement(el, ctx, fg, accent) {
789
799
  ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "poll", option_index: idx, option_label: label } });
790
800
  void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "poll", { option_index: idx, option_label: label }));
791
801
  (_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, "affirm");
792
- appendThanks(wrap, el, fg, "Thanks for your answer!");
802
+ appendThanks(wrap, el2, fg, "Thanks for your answer!");
793
803
  });
794
804
  wrap.appendChild(btn);
795
805
  });
796
806
  return wrap;
797
807
  }
798
- function appendNpsThanks(wrap, el, ctx, fg, accent, isPromoter) {
808
+ function appendNpsThanks(wrap, el2, ctx, fg, accent, isPromoter) {
799
809
  while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
800
810
  const note = document.createElement("div");
801
811
  note.setAttribute("role", "status");
802
- note.textContent = (typeof el.thanks_text === "string" && el.thanks_text.trim() ? el.thanks_text : "") || (isPromoter ? "Thank you! We’d love a public review." : "Thanks — your feedback helps us improve.");
812
+ note.textContent = (typeof el2.thanks_text === "string" && el2.thanks_text.trim() ? el2.thanks_text : "") || (isPromoter ? "Thank you! We’d love a public review." : "Thanks — your feedback helps us improve.");
803
813
  note.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin:6px 0;`;
804
814
  wrap.appendChild(note);
805
- const url = isPromoter && typeof el.nps_promoter_url === "string" ? ctx.sanitizeUrl(el.nps_promoter_url) : "";
815
+ const url = isPromoter && typeof el2.nps_promoter_url === "string" ? ctx.sanitizeUrl(el2.nps_promoter_url) : "";
806
816
  if (url) {
807
817
  const cta = document.createElement("button");
808
818
  cta.type = "button";
809
- cta.textContent = el.nps_promoter_cta || "Leave a review";
819
+ cta.textContent = el2.nps_promoter_cta || "Leave a review";
810
820
  cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;
811
821
  cta.addEventListener("click", () => {
812
822
  ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "nps", cta: "promoter_review" } });
@@ -816,30 +826,30 @@ function appendNpsThanks(wrap, el, ctx, fg, accent, isPromoter) {
816
826
  wrap.appendChild(cta);
817
827
  }
818
828
  }
819
- function renderNpsReasonStep(wrap, el, ctx, score, fg, accent) {
820
- const promoterThreshold = typeof el.nps_promoter_threshold === "number" ? el.nps_promoter_threshold : 9;
829
+ function renderNpsReasonStep(wrap, el2, ctx, score, fg, accent) {
830
+ const promoterThreshold = typeof el2.nps_promoter_threshold === "number" ? el2.nps_promoter_threshold : 9;
821
831
  const band = npsBandFromScore(score);
822
- const prompt = score >= promoterThreshold ? el.nps_follow_up_promoter || "What do you love most about us?" : score <= 6 ? el.nps_follow_up_detractor || "How can we improve?" : el.nps_follow_up_passive || "What would make us a 10?";
832
+ const prompt = score >= promoterThreshold ? el2.nps_follow_up_promoter || "What do you love most about us?" : score <= 6 ? el2.nps_follow_up_detractor || "How can we improve?" : el2.nps_follow_up_passive || "What would make us a 10?";
823
833
  while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
824
834
  const heading = document.createElement("div");
825
835
  heading.textContent = prompt;
826
836
  heading.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin-bottom:8px;`;
827
837
  wrap.appendChild(heading);
828
838
  const ta = document.createElement("textarea");
829
- const chips = Array.isArray(el.nps_reason_chips) ? el.nps_reason_chips : [];
839
+ const chips = Array.isArray(el2.nps_reason_chips) ? el2.nps_reason_chips : [];
830
840
  if (chips.length) {
831
841
  const chipRow = document.createElement("div");
832
842
  chipRow.style.cssText = "display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-bottom:8px;";
833
843
  chips.slice(0, 8).forEach((c) => {
834
- const chip = document.createElement("button");
835
- chip.type = "button";
836
- chip.textContent = c;
837
- chip.style.cssText = `padding:5px 11px;border-radius:999px;border:1px solid color-mix(in srgb, ${accent} 35%, transparent);background:transparent;color:${fg};font-size:12px;cursor:pointer;`;
838
- chip.addEventListener("click", () => {
844
+ const chip2 = document.createElement("button");
845
+ chip2.type = "button";
846
+ chip2.textContent = c;
847
+ chip2.style.cssText = `padding:5px 11px;border-radius:999px;border:1px solid color-mix(in srgb, ${accent} 35%, transparent);background:transparent;color:${fg};font-size:12px;cursor:pointer;`;
848
+ chip2.addEventListener("click", () => {
839
849
  ta.value = ta.value ? `${ta.value}, ${c}` : c;
840
850
  ta.focus();
841
851
  });
842
- chipRow.appendChild(chip);
852
+ chipRow.appendChild(chip2);
843
853
  });
844
854
  wrap.appendChild(chipRow);
845
855
  }
@@ -855,7 +865,7 @@ function renderNpsReasonStep(wrap, el, ctx, score, fg, accent) {
855
865
  const comment = withComment ? ta.value.trim() : "";
856
866
  void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "nps", comment ? { score, band, comment } : { score, band }));
857
867
  (_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, score >= promoterThreshold ? "affirm" : score <= 6 ? "empathize" : "acknowledge");
858
- appendNpsThanks(wrap, el, ctx, fg, accent, score >= promoterThreshold);
868
+ appendNpsThanks(wrap, el2, ctx, fg, accent, score >= promoterThreshold);
859
869
  };
860
870
  const btnRow = document.createElement("div");
861
871
  btnRow.style.cssText = "display:flex;gap:8px;justify-content:center;";
@@ -874,12 +884,12 @@ function renderNpsReasonStep(wrap, el, ctx, score, fg, accent) {
874
884
  wrap.appendChild(btnRow);
875
885
  ta.focus();
876
886
  }
877
- function buildNpsElement(el, ctx, fg, accent, muted) {
887
+ function buildNpsElement(el2, ctx, fg, accent, muted) {
878
888
  const wrap = document.createElement("div");
879
889
  wrap.style.cssText = "display:flex;flex-direction:column;gap:6px;margin:2px 0;";
880
- if (el.content) {
890
+ if (el2.content) {
881
891
  const q = document.createElement("div");
882
- q.textContent = el.content;
892
+ q.textContent = el2.content;
883
893
  q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;
884
894
  wrap.appendChild(q);
885
895
  }
@@ -911,12 +921,12 @@ function buildNpsElement(el, ctx, fg, accent, muted) {
911
921
  btn.style.opacity = "1";
912
922
  const npsPayload = { score: n, band: npsBandFromScore(n) };
913
923
  ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "nps", score: n, band: npsPayload.band } });
914
- if (el.nps_followup_enabled === true) {
915
- renderNpsReasonStep(wrap, el, ctx, n, fg, accent);
924
+ if (el2.nps_followup_enabled === true) {
925
+ renderNpsReasonStep(wrap, el2, ctx, n, fg, accent);
916
926
  } else {
917
927
  void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "nps", { ...npsPayload }));
918
928
  (_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, n >= 9 ? "affirm" : n >= 7 ? "acknowledge" : "empathize");
919
- appendThanks(wrap, el, fg, "Thanks for your feedback!");
929
+ appendThanks(wrap, el2, fg, "Thanks for your feedback!");
920
930
  }
921
931
  });
922
932
  row.appendChild(btn);
@@ -933,22 +943,22 @@ function buildNpsElement(el, ctx, fg, accent, muted) {
933
943
  wrap.appendChild(scale);
934
944
  return wrap;
935
945
  }
936
- function buildProgressElement(el, ctx, accent, muted) {
946
+ function buildProgressElement(el2, ctx, accent, muted) {
937
947
  const { trackEvent, campaign, sanitizeColor } = ctx;
938
- const goal = el.goal_type || "cart_total";
948
+ const goal = el2.goal_type || "cart_total";
939
949
  const moneyGoal = goal === "cart_total";
940
- const fill = el.fill ? sanitizeColor(el.fill) : accent;
950
+ const fill = el2.fill ? sanitizeColor(el2.fill) : accent;
941
951
  const props = () => ctx.properties && ctx.properties() || {};
942
952
  const sym = () => {
943
953
  var _a;
944
- if (el.currency_symbol) return el.currency_symbol;
954
+ if (el2.currency_symbol) return el2.currency_symbol;
945
955
  const code = ctx.getCartState && ((_a = ctx.getCartState()) == null ? void 0 : _a.currency);
946
956
  const M = { INR: "₹", USD: "$", EUR: "€", GBP: "£" };
947
957
  return code && M[code] || "";
948
958
  };
949
959
  const threshold = () => {
950
- if (typeof el.threshold === "number" && el.threshold > 0) return el.threshold;
951
- if (el.threshold_source === "free_delivery_setting") {
960
+ if (typeof el2.threshold === "number" && el2.threshold > 0) return el2.threshold;
961
+ if (el2.threshold_source === "free_delivery_setting") {
952
962
  const v = Number(props()["cart.free_delivery_above"]);
953
963
  if (v > 0) return v;
954
964
  }
@@ -989,9 +999,9 @@ function buildProgressElement(el, ctx, accent, muted) {
989
999
  goalType: goal,
990
1000
  threshold: th,
991
1001
  current: cur,
992
- rewardText: el.reward_text,
1002
+ rewardText: el2.reward_text,
993
1003
  currencySymbol: moneyGoal ? sym() : "",
994
- showPct: el.show_pct
1004
+ showPct: el2.show_pct
995
1005
  });
996
1006
  sub.textContent = labeled.text;
997
1007
  if (complete) {
@@ -1000,8 +1010,8 @@ function buildProgressElement(el, ctx, accent, muted) {
1000
1010
  if (!firedUnlock) {
1001
1011
  firedUnlock = true;
1002
1012
  if (seenIncomplete) {
1003
- trackEvent(campaign.id, "clicked", { stepId: el.id || "unlocked" });
1004
- if (el.celebrate_on_complete !== false) (_a = ctx.playReaction) == null ? void 0 : _a.call(ctx, wrap, "celebrate");
1013
+ trackEvent(campaign.id, "clicked", { stepId: el2.id || "unlocked" });
1014
+ if (el2.celebrate_on_complete !== false) (_a = ctx.playReaction) == null ? void 0 : _a.call(ctx, wrap, "celebrate");
1005
1015
  }
1006
1016
  }
1007
1017
  } else {
@@ -1019,6 +1029,40 @@ function buildProgressElement(el, ctx, accent, muted) {
1019
1029
  }
1020
1030
  return wrap;
1021
1031
  }
1032
+ function buildCtaAction(el2, card, meta, inline, sanitizeUrl) {
1033
+ const kind = el2.cta_action ?? (inline ? "add_to_cart" : "navigate");
1034
+ const safe = (u) => u ? sanitizeUrl(u) || void 0 : void 0;
1035
+ const url = safe(el2.cta_url || card.cta_url);
1036
+ const productId = meta.product_id;
1037
+ const sku = meta.sku;
1038
+ const platformProductId = meta.platform_product_id;
1039
+ const platformUrl = safe(meta.platform_url);
1040
+ switch (kind) {
1041
+ case "open_cart":
1042
+ return { type: "open_cart" };
1043
+ case "open_login":
1044
+ return { type: "open_login" };
1045
+ case "open_product":
1046
+ return { type: "open_product", productId, sku, url: platformUrl || url, platformProductId, platformUrl };
1047
+ case "navigate":
1048
+ return { type: "navigate", url: url || "" };
1049
+ case "custom":
1050
+ return { type: "custom", name: el2.cta_custom_name || "cta", payload: { ...meta, url } };
1051
+ case "add_to_cart":
1052
+ default:
1053
+ return {
1054
+ type: "add_to_cart",
1055
+ productId,
1056
+ sku,
1057
+ platformProductId,
1058
+ title: card.title,
1059
+ price: meta.price ?? card.body,
1060
+ imageUrl: card.image_url,
1061
+ url: platformUrl || url,
1062
+ qty: 1
1063
+ };
1064
+ }
1065
+ }
1022
1066
  function renderCardFromTemplate(card, opts) {
1023
1067
  const { ctx, action, layout, layoutAccent } = opts;
1024
1068
  const inline = action === "cart";
@@ -1038,7 +1082,7 @@ function renderCardFromTemplate(card, opts) {
1038
1082
  const fLayout = frame.layout;
1039
1083
  const dir = fLayout === "row" ? "row" : "column";
1040
1084
  const tileGap = frame.gap != null ? frame.gap : 6;
1041
- const sizing = fLayout ? frame.width != null ? `width:${typeof frame.width === "number" ? `${frame.width}px` : frame.width};` : "flex:1 1 auto; width:100%;" : layout === "grid" ? "" : "flex:0 0 150px; scroll-snap-align:start;";
1085
+ const sizing = fLayout ? frame.width != null ? `width:${typeof frame.width === "number" ? `${frame.width}px` : frame.width};` : "flex:1 1 auto; width:100%;" : layout === "grid" || layout === "list" ? "" : "flex:0 0 150px; scroll-snap-align:start;";
1042
1086
  tile.style.cssText = `
1043
1087
  box-sizing:border-box; display:flex; flex-direction:${dir}; ${fLayout === "row" ? "align-items:center;" : ""} gap:${tileGap}px; position:relative;
1044
1088
  color:${fg};
@@ -1055,11 +1099,11 @@ function renderCardFromTemplate(card, opts) {
1055
1099
  const elements = tpl.elements && tpl.elements.length ? tpl.elements : DEFAULT_CARD_TEMPLATE.elements;
1056
1100
  let imageWrap = null;
1057
1101
  const paintInto = (container, els) => {
1058
- for (const el of els) {
1059
- switch (el.type) {
1102
+ for (const el2 of els) {
1103
+ switch (el2.type) {
1060
1104
  case "image": {
1061
1105
  const wrap = document.createElement("div");
1062
- wrap.style.cssText = `position:relative;width:100%;aspect-ratio:${ASPECT$2[el.aspect || "1/1"] || "1 / 1"};border-radius:${el.radius ?? 10}px;overflow:hidden;background:#f7f8fa;`;
1106
+ wrap.style.cssText = `position:relative;width:100%;aspect-ratio:${ASPECT$2[el2.aspect || "1/1"] || "1 / 1"};border-radius:${el2.radius ?? 10}px;overflow:hidden;background:#f7f8fa;`;
1063
1107
  const safeVideo = card.video_url ? sanitizeUrl(card.video_url) : "";
1064
1108
  if (safeVideo) {
1065
1109
  const v = document.createElement("video");
@@ -1087,17 +1131,17 @@ function renderCardFromTemplate(card, opts) {
1087
1131
  break;
1088
1132
  }
1089
1133
  case "badge": {
1090
- const text = el.text;
1134
+ const text = el2.text;
1091
1135
  if (!text) break;
1092
1136
  const badge = document.createElement("span");
1093
1137
  badge.textContent = text;
1094
- const bg = el.color ? sanitizeColor(el.color) : brand;
1138
+ const bg = el2.color ? sanitizeColor(el2.color) : brand;
1095
1139
  badge.style.cssText = `position:absolute;top:6px;left:6px;z-index:1;background:${bg};color:#fff;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:0.03em;padding:3px 6px;border-radius:5px;`;
1096
1140
  (imageWrap || container).appendChild(badge);
1097
1141
  break;
1098
1142
  }
1099
1143
  case "price": {
1100
- const node = paintPrice(card, el, accent, ctx);
1144
+ const node = paintPrice(card, el2, accent, ctx);
1101
1145
  if (node) container.appendChild(node);
1102
1146
  break;
1103
1147
  }
@@ -1105,8 +1149,8 @@ function renderCardFromTemplate(card, opts) {
1105
1149
  if (!card.title) break;
1106
1150
  const t = document.createElement("div");
1107
1151
  t.textContent = card.title;
1108
- const lines = el.lines && el.lines > 0 ? el.lines : 2;
1109
- t.style.cssText = `font-size:12.5px;font-weight:600;line-height:1.3;text-align:${el.align ?? "left"};color:${el.color ? sanitizeColor(el.color) : fg};display:-webkit-box;-webkit-line-clamp:${lines};-webkit-box-orient:vertical;overflow:hidden;`;
1152
+ const lines = el2.lines && el2.lines > 0 ? el2.lines : 2;
1153
+ t.style.cssText = `font-size:12.5px;font-weight:600;line-height:1.3;text-align:${el2.align ?? "left"};color:${el2.color ? sanitizeColor(el2.color) : fg};display:-webkit-box;-webkit-line-clamp:${lines};-webkit-box-orient:vertical;overflow:hidden;`;
1110
1154
  container.appendChild(t);
1111
1155
  break;
1112
1156
  }
@@ -1114,8 +1158,8 @@ function renderCardFromTemplate(card, opts) {
1114
1158
  if (!card.body) break;
1115
1159
  const s = document.createElement("div");
1116
1160
  s.textContent = card.body;
1117
- const lines = el.lines && el.lines > 0 ? el.lines : 2;
1118
- s.style.cssText = `font-size:11px;color:${el.color ? sanitizeColor(el.color) : muted};line-height:1.3;text-align:${el.align ?? "left"};display:-webkit-box;-webkit-line-clamp:${lines};-webkit-box-orient:vertical;overflow:hidden;`;
1161
+ const lines = el2.lines && el2.lines > 0 ? el2.lines : 2;
1162
+ s.style.cssText = `font-size:11px;color:${el2.color ? sanitizeColor(el2.color) : muted};line-height:1.3;text-align:${el2.align ?? "left"};display:-webkit-box;-webkit-line-clamp:${lines};-webkit-box-orient:vertical;overflow:hidden;`;
1119
1163
  container.appendChild(s);
1120
1164
  break;
1121
1165
  }
@@ -1127,25 +1171,25 @@ function renderCardFromTemplate(card, opts) {
1127
1171
  case "category": {
1128
1172
  const cat = meta.category;
1129
1173
  if (typeof cat !== "string" || !cat) break;
1130
- const chip = document.createElement("span");
1131
- chip.textContent = cat;
1132
- chip.style.cssText = "align-self:flex-start;background:#f3f4f6;color:#6b7280;font-size:10px;font-weight:500;padding:2px 7px;border-radius:6px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";
1133
- container.appendChild(chip);
1174
+ const chip2 = document.createElement("span");
1175
+ chip2.textContent = cat;
1176
+ chip2.style.cssText = "align-self:flex-start;background:#f3f4f6;color:#6b7280;font-size:10px;font-weight:500;padding:2px 7px;border-radius:6px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";
1177
+ container.appendChild(chip2);
1134
1178
  break;
1135
1179
  }
1136
1180
  // ── rich-card-editor elements (P0) ──────────────────────────────────
1137
1181
  case "icon": {
1138
- const px = ICON_PX[el.size || "md"] || 30;
1182
+ const px = ICON_PX[el2.size || "md"] || 30;
1139
1183
  const node = document.createElement("div");
1140
- const iconColor = el.color ? sanitizeColor(el.color) : fg;
1141
- if (el.bubble) {
1142
- const bubbleTint = el.color ? sanitizeColor(el.color) : brand;
1184
+ const iconColor = el2.color ? sanitizeColor(el2.color) : fg;
1185
+ if (el2.bubble) {
1186
+ const bubbleTint = el2.color ? sanitizeColor(el2.color) : brand;
1143
1187
  node.style.cssText = `flex:0 0 auto;width:${px}px;height:${px}px;border-radius:999px;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb, ${bubbleTint} 16%, transparent);`;
1144
1188
  } else {
1145
1189
  node.style.cssText = "flex:0 0 auto;display:flex;align-items:center;";
1146
1190
  }
1147
- const svg = lucideSvgEl(el.icon_name || "circle-check", {
1148
- size: el.bubble ? Math.round(px * 0.55) : Math.round(px * 0.7),
1191
+ const svg = lucideSvgEl(el2.icon_name || "circle-check", {
1192
+ size: el2.bubble ? Math.round(px * 0.55) : Math.round(px * 0.7),
1149
1193
  color: iconColor,
1150
1194
  strokeWidth: 2
1151
1195
  });
@@ -1155,99 +1199,64 @@ function renderCardFromTemplate(card, opts) {
1155
1199
  }
1156
1200
  case "text": {
1157
1201
  const t = document.createElement("div");
1158
- const fs = TEXT_PX[el.size || "md"] || 13;
1159
- t.style.cssText = `font-size:${fs}px;font-weight:${WEIGHT[el.weight ?? "medium"]};text-align:${el.align ?? "left"};color:${el.color ? sanitizeColor(el.color) : fg};line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;`;
1160
- t.textContent = interpolateTokens(el.content ?? "", ctx);
1202
+ const fs = TEXT_PX[el2.size || "md"] || 13;
1203
+ t.style.cssText = `font-size:${fs}px;font-weight:${WEIGHT[el2.weight ?? "medium"]};text-align:${el2.align ?? "left"};color:${el2.color ? sanitizeColor(el2.color) : fg};line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;`;
1204
+ t.textContent = interpolateTokens(el2.content ?? "", ctx);
1161
1205
  container.appendChild(t);
1162
1206
  break;
1163
1207
  }
1164
1208
  case "spacer": {
1165
1209
  const sp = document.createElement("div");
1166
- sp.style.height = `${el.height ?? 8}px`;
1210
+ sp.style.height = `${el2.height ?? 8}px`;
1167
1211
  container.appendChild(sp);
1168
1212
  break;
1169
1213
  }
1170
1214
  case "progress": {
1171
- container.appendChild(buildProgressElement(el, ctx, brand, muted));
1215
+ container.appendChild(buildProgressElement(el2, ctx, brand, muted));
1172
1216
  break;
1173
1217
  }
1174
1218
  case "countdown": {
1175
- const cd = buildCountdownElement(el, fg, muted);
1219
+ const cd = buildCountdownElement(el2, fg, muted);
1176
1220
  if (cd) container.appendChild(cd);
1177
1221
  break;
1178
1222
  }
1179
1223
  case "rating_input": {
1180
- container.appendChild(buildRatingInputElement(el, ctx, fg));
1224
+ container.appendChild(buildRatingInputElement(el2, ctx, fg));
1181
1225
  break;
1182
1226
  }
1183
1227
  case "poll": {
1184
- const p = buildPollElement(el, ctx, fg, brand);
1228
+ const p = buildPollElement(el2, ctx, fg, brand);
1185
1229
  if (p) container.appendChild(p);
1186
1230
  break;
1187
1231
  }
1188
1232
  case "nps": {
1189
- container.appendChild(buildNpsElement(el, ctx, fg, brand, muted));
1233
+ container.appendChild(buildNpsElement(el2, ctx, fg, brand, muted));
1190
1234
  break;
1191
1235
  }
1192
1236
  case "group": {
1193
1237
  const g = document.createElement("div");
1194
- const gdir = el.layout === "row" ? "row" : "column";
1195
- g.style.cssText = `display:flex;flex-direction:${gdir};${el.layout === "row" ? "align-items:center;" : ""}gap:${el.gap ?? 4}px;min-width:0;${el.layout === "row" ? "" : "flex:1 1 auto;"}`;
1196
- paintInto(g, el.elements || []);
1238
+ const gdir = el2.layout === "row" ? "row" : "column";
1239
+ g.style.cssText = `display:flex;flex-direction:${gdir};${el2.layout === "row" ? "align-items:center;" : ""}gap:${el2.gap ?? 4}px;min-width:0;${el2.layout === "row" ? "" : "flex:1 1 auto;"}`;
1240
+ paintInto(g, el2.elements || []);
1197
1241
  container.appendChild(g);
1198
1242
  break;
1199
1243
  }
1200
1244
  case "cta": {
1201
- const label = el.text || card.cta_text || (inline ? "Add" : "Shop");
1245
+ const label = el2.text || card.cta_text || (inline ? "Add" : "Shop");
1202
1246
  const cta = document.createElement("button");
1203
1247
  cta.className = "aegis-cta";
1204
1248
  cta.textContent = label;
1205
- const overlayOnImage = !!el.overlay && !!imageWrap;
1249
+ const overlayOnImage = !!el2.overlay && !!imageWrap;
1206
1250
  if (overlayOnImage) {
1207
- cta.style.cssText = el.variant === "outline" ? `position:absolute;right:6px;bottom:6px;background:#fff;color:${brand};border:1px solid ${brand};border-radius:9px;padding:5px 18px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:0.04em;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.14);` : `position:absolute;right:6px;bottom:6px;background:${brand};color:#fff;border:none;border-radius:9px;padding:5px 18px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:0.04em;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.14);`;
1251
+ cta.style.cssText = el2.variant === "outline" ? `position:absolute;right:6px;bottom:6px;background:#fff;color:${brand};border:1px solid ${brand};border-radius:9px;padding:5px 18px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:0.04em;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.14);` : `position:absolute;right:6px;bottom:6px;background:${brand};color:#fff;border:none;border-radius:9px;padding:5px 18px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:0.04em;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.14);`;
1208
1252
  } else {
1209
- cta.style.cssText = el.variant === "outline" ? `margin-top:${fLayout === "row" ? "0" : "auto"};background:transparent;color:${brand};border:1px solid ${brand};border-radius:999px;padding:6px 10px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;` : `margin-top:${fLayout === "row" ? "0" : "auto"};background:${brand};color:#fff;border:none;border-radius:999px;padding:7px 10px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;`;
1253
+ cta.style.cssText = el2.variant === "outline" ? `margin-top:${fLayout === "row" ? "0" : "auto"};background:transparent;color:${brand};border:1px solid ${brand};border-radius:999px;padding:6px 10px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;` : `margin-top:${fLayout === "row" ? "0" : "auto"};background:${brand};color:#fff;border:none;border-radius:999px;padding:7px 10px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;`;
1210
1254
  }
1211
1255
  cta.addEventListener("click", (e) => {
1256
+ var _a;
1212
1257
  e.stopPropagation();
1213
- trackEvent(campaign.id, "clicked", { stepId: el.id || "add" });
1214
- if (inline) {
1215
- const m2 = meta;
1216
- if (ctx.dispatchAction) {
1217
- ctx.dispatchAction({
1218
- type: "add_to_cart",
1219
- productId: m2.product_id,
1220
- sku: m2.sku,
1221
- // Platform-native id (catalog-sync C.2) — plugins use this for a
1222
- // native add; undefined today → graceful product-search fallback.
1223
- platformProductId: m2.platform_product_id,
1224
- title: card.title,
1225
- price: m2.price ?? card.body,
1226
- imageUrl: card.image_url,
1227
- url: m2.platform_url || card.cta_url,
1228
- qty: 1
1229
- });
1230
- } else if (typeof window !== "undefined") {
1231
- window.dispatchEvent(
1232
- new CustomEvent("aegis:add-to-cart", {
1233
- detail: {
1234
- campaign_id: campaign.id,
1235
- product_id: m2.product_id,
1236
- sku: m2.sku,
1237
- title: card.title,
1238
- price: m2.price ?? card.body,
1239
- image_url: card.image_url,
1240
- cta_url: card.cta_url
1241
- }
1242
- })
1243
- );
1244
- }
1245
- return;
1246
- }
1247
- if (card.cta_url) {
1248
- const safe = sanitizeUrl(card.cta_url);
1249
- if (safe) ctx.navigate ? ctx.navigate(safe) : window.location.href = safe;
1250
- }
1258
+ trackEvent(campaign.id, "clicked", { stepId: el2.id || "add" });
1259
+ (_a = ctx.dispatchAction) == null ? void 0 : _a.call(ctx, buildCtaAction(el2, card, meta, inline, sanitizeUrl));
1251
1260
  });
1252
1261
  (overlayOnImage ? imageWrap : container).appendChild(cta);
1253
1262
  break;
@@ -1257,37 +1266,20 @@ function renderCardFromTemplate(card, opts) {
1257
1266
  };
1258
1267
  paintInto(tile, elements);
1259
1268
  const open = (e) => {
1269
+ var _a;
1260
1270
  e.stopPropagation();
1261
1271
  trackEvent(campaign.id, "clicked");
1262
- if (inline) {
1263
- const bm = meta;
1264
- if (ctx.dispatchAction) {
1265
- ctx.dispatchAction({
1266
- type: "open_product",
1267
- productId: bm.product_id,
1268
- sku: bm.sku,
1269
- url: bm.platform_url || card.cta_url,
1270
- platformProductId: bm.platform_product_id,
1271
- platformUrl: bm.platform_url
1272
- });
1273
- } else if (typeof window !== "undefined") {
1274
- window.dispatchEvent(
1275
- new CustomEvent("aegis:open-product", {
1276
- detail: {
1277
- campaign_id: campaign.id,
1278
- product_id: bm.product_id,
1279
- sku: bm.sku,
1280
- cta_url: card.cta_url
1281
- }
1282
- })
1283
- );
1284
- }
1285
- return;
1286
- }
1287
- if (card.cta_url) {
1288
- const safe = sanitizeUrl(card.cta_url);
1289
- if (safe) ctx.navigate ? ctx.navigate(safe) : window.location.href = safe;
1290
- }
1272
+ const bm = meta;
1273
+ const safe = (u) => u ? sanitizeUrl(u) || void 0 : void 0;
1274
+ const act = inline ? {
1275
+ type: "open_product",
1276
+ productId: bm.product_id,
1277
+ sku: bm.sku,
1278
+ url: safe(bm.platform_url) || safe(card.cta_url),
1279
+ platformProductId: bm.platform_product_id,
1280
+ platformUrl: safe(bm.platform_url)
1281
+ } : { type: "navigate", url: safe(card.cta_url) || "" };
1282
+ (_a = ctx.dispatchAction) == null ? void 0 : _a.call(ctx, act);
1291
1283
  };
1292
1284
  if (inline || card.cta_url) tile.addEventListener("click", open);
1293
1285
  if (frame.celebrate_on_show === true && ctx.playReaction) {
@@ -1320,6 +1312,7 @@ function renderCarouselCards(ctx) {
1320
1312
  }
1321
1313
  const autoplay = Number(ic.autoplay_ms) || 0;
1322
1314
  const loop = ic.loop !== false;
1315
+ const layout = ic.rec_layout || "row";
1323
1316
  addAnimationStyles();
1324
1317
  ensureInAppFont();
1325
1318
  const bg = "#ffffff";
@@ -1367,20 +1360,26 @@ function renderCarouselCards(ctx) {
1367
1360
  header.appendChild(closeBtn);
1368
1361
  card.appendChild(header);
1369
1362
  const track = document.createElement("div");
1370
- track.style.cssText = `
1371
- display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x proximity;
1372
- scroll-padding-left: 0; scrollbar-width: none; -ms-overflow-style: none;
1373
- /* room so overflow-x: auto doesn't clip the card drop-shadow */
1374
- padding: 8px 6px 16px; margin: -8px -6px -6px; -webkit-overflow-scrolling: touch;
1375
- `;
1376
- track.style.msOverflowStyle = "none";
1363
+ if (layout === "grid") {
1364
+ track.style.cssText = "display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; padding: 4px 2px;";
1365
+ } else if (layout === "list") {
1366
+ track.style.cssText = "display: flex; flex-direction: column; gap: 10px; padding: 4px 2px;";
1367
+ } else {
1368
+ track.style.cssText = `
1369
+ display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x proximity;
1370
+ scroll-padding-left: 0; scrollbar-width: none; -ms-overflow-style: none;
1371
+ /* room so overflow-x: auto doesn't clip the card drop-shadow */
1372
+ padding: 8px 6px 16px; margin: -8px -6px -6px; -webkit-overflow-scrolling: touch;
1373
+ `;
1374
+ track.style.msOverflowStyle = "none";
1375
+ }
1377
1376
  const template = resolveGlobalTemplate(ic);
1378
1377
  cards.forEach((c, i) => {
1379
1378
  const tile = renderCardFromTemplate(c, {
1380
1379
  template,
1381
1380
  ctx,
1382
1381
  action: "navigate",
1383
- layout: "row",
1382
+ layout,
1384
1383
  layoutAccent: accent
1385
1384
  });
1386
1385
  tile.setAttribute("data-card-index", String(i));
@@ -1390,6 +1389,12 @@ function renderCarouselCards(ctx) {
1390
1389
  track.appendChild(tile);
1391
1390
  });
1392
1391
  card.appendChild(track);
1392
+ if (layout !== "row") {
1393
+ cards.forEach((_, i) => trackEvent(campaign.id, "impression", { stepId: `card_${i}` }));
1394
+ overlay.appendChild(card);
1395
+ document.body.appendChild(overlay);
1396
+ return;
1397
+ }
1393
1398
  const dots = document.createElement("div");
1394
1399
  dots.style.cssText = "display: flex; justify-content: center; gap: 6px; margin-top: 10px;";
1395
1400
  const dotEls = [];
@@ -1419,9 +1424,9 @@ function renderCarouselCards(ctx) {
1419
1424
  const tiles = track.querySelectorAll("[data-card-index]");
1420
1425
  const goto = (idx) => {
1421
1426
  activeIdx = (idx % cards.length + cards.length) % cards.length;
1422
- const el = tiles[activeIdx];
1423
- if (el) {
1424
- const delta = el.getBoundingClientRect().left - track.getBoundingClientRect().left;
1427
+ const el2 = tiles[activeIdx];
1428
+ if (el2) {
1429
+ const delta = el2.getBoundingClientRect().left - track.getBoundingClientRect().left;
1425
1430
  track.scrollTo({ left: track.scrollLeft + delta, behavior: "smooth" });
1426
1431
  setActive(activeIdx);
1427
1432
  }
@@ -2038,6 +2043,10 @@ function renderProductRecommendation(ctx) {
2038
2043
  display: flex; gap: 10px; overflow-x: auto;
2039
2044
  scroll-snap-type: x mandatory; padding: 14px 2px 18px;
2040
2045
  `;
2046
+ } else if (layout === "list") {
2047
+ grid.style.cssText = `
2048
+ display: flex; flex-direction: column; gap: 10px; padding: 14px 2px 6px;
2049
+ `;
2041
2050
  } else {
2042
2051
  grid.style.cssText = `
2043
2052
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;
@@ -2094,9 +2103,11 @@ function renderProductRecommendationInline(ctx, target) {
2094
2103
  section.appendChild(body);
2095
2104
  }
2096
2105
  const grid = document.createElement("div");
2097
- const isRow = layout !== "grid";
2106
+ const isRow = layout === "row" || layout !== "grid" && layout !== "list";
2098
2107
  if (layout === "grid") {
2099
2108
  grid.style.cssText = "display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;";
2109
+ } else if (layout === "list") {
2110
+ grid.style.cssText = "display: flex; flex-direction: column; gap: 10px;";
2100
2111
  } else {
2101
2112
  grid.style.cssText = `
2102
2113
  display: flex; gap: 10px; overflow-x: auto;
@@ -2639,10 +2650,10 @@ function brandAccent(ctx) {
2639
2650
  function injectGameStyles() {
2640
2651
  if (typeof document === "undefined" || document.getElementById("aegis-game-styles")) return;
2641
2652
  if (typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
2642
- const el = document.createElement("style");
2643
- el.id = "aegis-game-styles";
2644
- el.textContent = "@keyframes aegisBoxRattle{0%,100%{transform:scale(1) rotate(0)}20%{transform:scale(1.05) rotate(-4deg)}40%{transform:scale(1.05) rotate(4deg)}60%{transform:scale(1.06) rotate(-3deg)}80%{transform:scale(1.06) rotate(3deg)}}@keyframes aegisStampIn{0%{transform:scale(2.4) rotate(-20deg);opacity:0}55%{transform:scale(0.82) rotate(7deg);opacity:1}100%{transform:scale(1) rotate(-6deg);opacity:1}}@keyframes aegisBadgePop{0%{transform:scale(0) rotate(-12deg);opacity:0}70%{transform:scale(1.18) rotate(4deg);opacity:1}100%{transform:scale(1) rotate(0);opacity:1}}";
2645
- document.head.appendChild(el);
2653
+ const el2 = document.createElement("style");
2654
+ el2.id = "aegis-game-styles";
2655
+ el2.textContent = "@keyframes aegisBoxRattle{0%,100%{transform:scale(1) rotate(0)}20%{transform:scale(1.05) rotate(-4deg)}40%{transform:scale(1.05) rotate(4deg)}60%{transform:scale(1.06) rotate(-3deg)}80%{transform:scale(1.06) rotate(3deg)}}@keyframes aegisStampIn{0%{transform:scale(2.4) rotate(-20deg);opacity:0}55%{transform:scale(0.82) rotate(7deg);opacity:1}100%{transform:scale(1) rotate(-6deg);opacity:1}}@keyframes aegisBadgePop{0%{transform:scale(0) rotate(-12deg);opacity:0}70%{transform:scale(1.18) rotate(4deg);opacity:1}100%{transform:scale(1) rotate(0);opacity:1}}";
2656
+ document.head.appendChild(el2);
2646
2657
  }
2647
2658
  function openGameModal(ctx, className, maxWidth = 360) {
2648
2659
  const { campaign, trackEvent, addAnimationStyles } = ctx;
@@ -2737,10 +2748,10 @@ function renderStreak(ctx) {
2737
2748
  s.content.appendChild(gEl("div", "font-size:22px;font-weight:800;margin:4px 0 2px", `${count}-day streak!`));
2738
2749
  const chips = gEl("div", "display:flex;justify-content:center;gap:8px;margin:12px 0");
2739
2750
  for (let i = 0; i < 7; i++) {
2740
- const chip = gEl("div", "display:inline-flex;flex-direction:column;align-items:center;gap:3px;font-size:11px;opacity:0.8");
2741
- chip.appendChild(gEl("span", "", days[i]));
2742
- chip.appendChild(gEl("span", "font-size:16px", i < count % 7 || count >= 7 && i < 7 ? "✅" : i === count % 7 ? "🔥" : "⚪"));
2743
- chips.appendChild(chip);
2751
+ const chip2 = gEl("div", "display:inline-flex;flex-direction:column;align-items:center;gap:3px;font-size:11px;opacity:0.8");
2752
+ chip2.appendChild(gEl("span", "", days[i]));
2753
+ chip2.appendChild(gEl("span", "font-size:16px", i < count % 7 || count >= 7 && i < 7 ? "✅" : i === count % 7 ? "🔥" : "⚪"));
2754
+ chips.appendChild(chip2);
2744
2755
  }
2745
2756
  s.content.appendChild(chips);
2746
2757
  const note = gEl("div", "font-size:13px;opacity:0.8");
@@ -4105,15 +4116,15 @@ class AegisChat {
4105
4116
  row.className = "aegis-chat-quickreplies";
4106
4117
  for (const label of this.quickReplies) {
4107
4118
  if (typeof label !== "string" || !label.trim()) continue;
4108
- const chip = document.createElement("button");
4109
- chip.type = "button";
4110
- chip.className = "aegis-chat-chip";
4111
- chip.textContent = label;
4112
- chip.addEventListener("click", () => {
4119
+ const chip2 = document.createElement("button");
4120
+ chip2.type = "button";
4121
+ chip2.className = "aegis-chat-chip";
4122
+ chip2.textContent = label;
4123
+ chip2.addEventListener("click", () => {
4113
4124
  row.remove();
4114
4125
  void this.send(label);
4115
4126
  });
4116
- row.appendChild(chip);
4127
+ row.appendChild(chip2);
4117
4128
  }
4118
4129
  if (row.childElementCount > 0) this.thread.appendChild(row);
4119
4130
  }
@@ -4150,8 +4161,8 @@ class AegisChat {
4150
4161
  }
4151
4162
  if (msg.interactive && Array.isArray(msg.interactive)) {
4152
4163
  for (const inter of msg.interactive) {
4153
- const el = this.renderInteractive(inter);
4154
- if (el) row.appendChild(el);
4164
+ const el2 = this.renderInteractive(inter);
4165
+ if (el2) row.appendChild(el2);
4155
4166
  }
4156
4167
  }
4157
4168
  this.thread.appendChild(row);
@@ -5015,6 +5026,408 @@ function disposeActiveWebChat() {
5015
5026
  activeInstance == null ? void 0 : activeInstance.destroy();
5016
5027
  activeInstance = null;
5017
5028
  }
5029
+ const SVG_NS = "http://www.w3.org/2000/svg";
5030
+ function el(tag, css, text) {
5031
+ const e = document.createElement(tag);
5032
+ if (css) e.style.cssText = css;
5033
+ if (text != null) e.textContent = text;
5034
+ return e;
5035
+ }
5036
+ function stars(rating, size = 14) {
5037
+ const wrap = el("span", "display:inline-flex;gap:1px;vertical-align:middle;");
5038
+ wrap.setAttribute("aria-label", `${rating} out of 5`);
5039
+ for (let i = 1; i <= 5; i++) {
5040
+ const svg = document.createElementNS(SVG_NS, "svg");
5041
+ svg.setAttribute("viewBox", "0 0 24 24");
5042
+ svg.setAttribute("width", String(size));
5043
+ svg.setAttribute("height", String(size));
5044
+ const path = document.createElementNS(SVG_NS, "path");
5045
+ path.setAttribute(
5046
+ "d",
5047
+ "M12 2l2.9 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.1-1.01L12 2z"
5048
+ );
5049
+ path.setAttribute("fill", i <= rating ? "#f59e0b" : "#e2e8f0");
5050
+ svg.appendChild(path);
5051
+ wrap.appendChild(svg);
5052
+ }
5053
+ return wrap;
5054
+ }
5055
+ function chip(text, kind) {
5056
+ const palette = kind === "verified" ? "background:#ecfdf5;color:#047857;" : "background:#fffbeb;color:#b45309;";
5057
+ return el(
5058
+ "span",
5059
+ `display:inline-flex;align-items:center;font-size:11px;font-weight:600;padding:2px 8px;border-radius:999px;${palette}`,
5060
+ text
5061
+ );
5062
+ }
5063
+ function sparkles() {
5064
+ const svg = document.createElementNS(SVG_NS, "svg");
5065
+ svg.setAttribute("viewBox", "0 0 24 24");
5066
+ svg.setAttribute("width", "15");
5067
+ svg.setAttribute("height", "15");
5068
+ const p = document.createElementNS(SVG_NS, "path");
5069
+ p.setAttribute("d", "M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6L12 3z");
5070
+ p.setAttribute("fill", "#7c3aed");
5071
+ svg.appendChild(p);
5072
+ return svg;
5073
+ }
5074
+ function miniStar() {
5075
+ const svg = document.createElementNS(SVG_NS, "svg");
5076
+ svg.setAttribute("viewBox", "0 0 24 24");
5077
+ svg.setAttribute("width", "11");
5078
+ svg.setAttribute("height", "11");
5079
+ const p = document.createElementNS(SVG_NS, "path");
5080
+ p.setAttribute(
5081
+ "d",
5082
+ "M12 2l2.9 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.1-1.01L12 2z"
5083
+ );
5084
+ p.setAttribute("fill", "#f59e0b");
5085
+ svg.appendChild(p);
5086
+ return svg;
5087
+ }
5088
+ function distributionRows(dist, total) {
5089
+ const box = el("div", "display:flex;flex-direction:column;gap:3px;margin:10px 0 4px;");
5090
+ for (let r = 5; r >= 1; r--) {
5091
+ const n = dist[String(r)] ?? 0;
5092
+ const pct = total > 0 ? Math.round(n / total * 100) : 0;
5093
+ const row = el("div", "display:flex;align-items:center;gap:8px;font-size:12px;color:#64748b;");
5094
+ const label = el("span", "width:26px;display:inline-flex;align-items:center;gap:2px;color:#334155;", String(r));
5095
+ label.appendChild(miniStar());
5096
+ row.appendChild(label);
5097
+ const track = el("div", "flex:1;height:6px;background:#f1f5f9;border-radius:999px;overflow:hidden;");
5098
+ track.appendChild(el("div", `width:${pct}%;height:100%;background:#f59e0b;`));
5099
+ row.appendChild(track);
5100
+ row.appendChild(el("span", "width:28px;text-align:right;", String(n)));
5101
+ box.appendChild(row);
5102
+ }
5103
+ return box;
5104
+ }
5105
+ function injectJsonLd(target, data, productId) {
5106
+ if (!data.aggregate.count || data.aggregate.avg_rating == null) return;
5107
+ const ld = {
5108
+ "@context": "https://schema.org",
5109
+ "@type": "Product",
5110
+ ...productId ? { sku: productId } : {},
5111
+ aggregateRating: {
5112
+ "@type": "AggregateRating",
5113
+ ratingValue: data.aggregate.avg_rating,
5114
+ reviewCount: data.aggregate.count
5115
+ },
5116
+ review: data.items.slice(0, 10).map((r) => {
5117
+ var _a;
5118
+ return {
5119
+ "@type": "Review",
5120
+ reviewRating: { "@type": "Rating", ratingValue: r.rating ?? 0 },
5121
+ author: { "@type": "Person", name: ((_a = r.reviewer_snapshot) == null ? void 0 : _a.name) ?? "Customer" },
5122
+ ...r.review_text ? { reviewBody: r.review_text } : {}
5123
+ };
5124
+ })
5125
+ };
5126
+ const script = document.createElement("script");
5127
+ script.type = "application/ld+json";
5128
+ script.textContent = JSON.stringify(ld);
5129
+ target.appendChild(script);
5130
+ }
5131
+ function thumbSvg(up) {
5132
+ const svg = document.createElementNS(SVG_NS, "svg");
5133
+ svg.setAttribute("viewBox", "0 0 24 24");
5134
+ svg.setAttribute("width", "14");
5135
+ svg.setAttribute("height", "14");
5136
+ if (!up) svg.style.cssText = "transform:rotate(180deg);";
5137
+ const p = document.createElementNS(SVG_NS, "path");
5138
+ p.setAttribute(
5139
+ "d",
5140
+ "M2 21h4V9H2v12zM23 10a2 2 0 0 0-2-2h-6.31l.95-4.57.03-.32a1.5 1.5 0 0 0-.44-1.06L14.17 1 7.59 7.59A2 2 0 0 0 7 9v10a2 2 0 0 0 2 2h9a2 2 0 0 0 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-1z"
5141
+ );
5142
+ p.setAttribute("fill", "currentColor");
5143
+ svg.appendChild(p);
5144
+ return svg;
5145
+ }
5146
+ function helpfulButton(reviewId, up, count, creds) {
5147
+ const btn = el(
5148
+ "button",
5149
+ "display:inline-flex;align-items:center;gap:4px;font-size:12px;color:#64748b;background:none;border:none;cursor:pointer;padding:2px 4px;"
5150
+ );
5151
+ btn.appendChild(thumbSvg(up));
5152
+ const countEl = el("span", "", String(count));
5153
+ btn.appendChild(countEl);
5154
+ const voteKey = `aegis_rv_voted_${reviewId}`;
5155
+ const already = (() => {
5156
+ try {
5157
+ return typeof localStorage !== "undefined" && localStorage.getItem(voteKey) != null;
5158
+ } catch {
5159
+ return false;
5160
+ }
5161
+ })();
5162
+ if (already) btn.style.opacity = "0.5";
5163
+ btn.addEventListener("click", () => {
5164
+ try {
5165
+ if (localStorage.getItem(voteKey) != null) return;
5166
+ localStorage.setItem(voteKey, up ? "up" : "down");
5167
+ } catch {
5168
+ }
5169
+ countEl.textContent = String(count + 1);
5170
+ btn.style.opacity = "0.5";
5171
+ const url = new URL(`/v1/reviews/${reviewId}/helpful`, creds.apiHost);
5172
+ void fetch(url.toString(), {
5173
+ method: "POST",
5174
+ headers: { "X-Aegis-Write-Key": creds.writeKey, "Content-Type": "application/json" },
5175
+ body: JSON.stringify({ direction: up ? "up" : "down" })
5176
+ }).catch(() => {
5177
+ });
5178
+ });
5179
+ return btn;
5180
+ }
5181
+ function reviewCard(ctx, r, creds) {
5182
+ var _a, _b;
5183
+ const li = el("li", "padding:14px 0;border-top:1px solid #e2e8f0;");
5184
+ const meta = el("div", "display:flex;flex-wrap:wrap;align-items:center;gap:8px;");
5185
+ meta.appendChild(stars(r.rating ?? 0));
5186
+ meta.appendChild(el("span", "font-size:14px;font-weight:600;", ((_a = r.reviewer_snapshot) == null ? void 0 : _a.name) ?? "Customer"));
5187
+ if (r.verified_purchase) meta.appendChild(chip("Verified purchase", "verified"));
5188
+ if (r.is_incentivized) meta.appendChild(chip("Reward included", "reward"));
5189
+ if (r.created_at) {
5190
+ const d = new Date(r.created_at);
5191
+ if (!Number.isNaN(d.getTime())) {
5192
+ meta.appendChild(el("span", "margin-left:auto;font-size:12px;color:#94a3b8;", d.toLocaleDateString()));
5193
+ }
5194
+ }
5195
+ li.appendChild(meta);
5196
+ if (r.review_text) {
5197
+ li.appendChild(el("p", "margin:6px 0 0;font-size:14px;line-height:1.5;color:#334155;", r.review_text));
5198
+ }
5199
+ if (r.media && r.media.length > 0) {
5200
+ const gallery = el("div", "display:flex;gap:6px;margin-top:8px;");
5201
+ for (const m2 of r.media.slice(0, 6)) {
5202
+ const safe = ctx.sanitizeUrl(m2.url);
5203
+ if (!safe) continue;
5204
+ const img = document.createElement("img");
5205
+ img.src = safe;
5206
+ img.loading = "lazy";
5207
+ img.alt = "";
5208
+ img.style.cssText = "width:56px;height:56px;object-fit:cover;border-radius:8px;";
5209
+ gallery.appendChild(img);
5210
+ }
5211
+ if (gallery.childElementCount) li.appendChild(gallery);
5212
+ }
5213
+ if ((_b = r.operator_reply) == null ? void 0 : _b.text) {
5214
+ const reply = el(
5215
+ "div",
5216
+ "margin-top:8px;padding:8px 10px;background:#f8fafc;border-radius:8px;font-size:13px;color:#475569;"
5217
+ );
5218
+ reply.appendChild(el("span", "font-weight:600;margin-right:6px;", "Reply"));
5219
+ reply.appendChild(el("span", "", r.operator_reply.text));
5220
+ li.appendChild(reply);
5221
+ }
5222
+ const votes = el("div", "display:flex;align-items:center;gap:10px;margin-top:8px;");
5223
+ votes.appendChild(el("span", "font-size:12px;color:#94a3b8;", "Helpful?"));
5224
+ votes.appendChild(helpfulButton(r.id, true, r.helpful_count ?? 0, creds));
5225
+ votes.appendChild(helpfulButton(r.id, false, r.not_helpful_count ?? 0, creds));
5226
+ li.appendChild(votes);
5227
+ return li;
5228
+ }
5229
+ const SORTS = [
5230
+ { key: "recent", label: "Most recent" },
5231
+ { key: "helpful", label: "Most helpful" },
5232
+ { key: "rating", label: "Highest rated" }
5233
+ ];
5234
+ function renderReviewsInline(ctx, target, creds) {
5235
+ var _a;
5236
+ if (typeof document === "undefined" || !(creds == null ? void 0 : creds.apiHost) || !(creds == null ? void 0 : creds.writeKey)) return false;
5237
+ const productId = target.getAttribute("data-product-id") || ctx.campaign.product_id || null;
5238
+ const state = { sort: "recent", withPhotos: false };
5239
+ target.innerHTML = "";
5240
+ target.appendChild(el("div", "font-size:13px;color:#94a3b8;padding:8px 0;", "Loading reviews…"));
5241
+ function buildUrl() {
5242
+ const url = new URL("/v1/reviews/public", creds.apiHost);
5243
+ if (productId) url.searchParams.set("product_id", productId);
5244
+ url.searchParams.set("sort", state.sort);
5245
+ if (state.withPhotos) url.searchParams.set("with_photos", "true");
5246
+ url.searchParams.set("limit", "20");
5247
+ return url.toString();
5248
+ }
5249
+ function fetchReviews() {
5250
+ return fetch(buildUrl(), { method: "GET", headers: { "X-Aegis-Write-Key": creds.writeKey } }).then((r) => r.ok ? r.json() : null).catch(() => null);
5251
+ }
5252
+ let listContainer = null;
5253
+ function renderList(items) {
5254
+ if (!listContainer) return;
5255
+ listContainer.innerHTML = "";
5256
+ if (items.length === 0) {
5257
+ listContainer.appendChild(el("p", "padding:14px 0;font-size:13px;color:#94a3b8;", "No reviews match this filter."));
5258
+ return;
5259
+ }
5260
+ const ul = el("ul", "list-style:none;margin:0;padding:0;");
5261
+ for (const r of items) ul.appendChild(reviewCard(ctx, r, creds));
5262
+ listContainer.appendChild(ul);
5263
+ }
5264
+ function controls(onChange) {
5265
+ const bar = el("div", "display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:10px 0;");
5266
+ const sel = document.createElement("select");
5267
+ sel.style.cssText = "font-size:13px;padding:4px 8px;border:1px solid #e2e8f0;border-radius:999px;background:#fff;color:#334155;";
5268
+ for (const s of SORTS) {
5269
+ const opt = document.createElement("option");
5270
+ opt.value = s.key;
5271
+ opt.textContent = s.label;
5272
+ sel.appendChild(opt);
5273
+ }
5274
+ sel.value = state.sort;
5275
+ sel.addEventListener("change", () => {
5276
+ state.sort = sel.value;
5277
+ onChange();
5278
+ });
5279
+ bar.appendChild(sel);
5280
+ const photos = el(
5281
+ "button",
5282
+ "font-size:13px;padding:4px 12px;border:1px solid #e2e8f0;border-radius:999px;background:#fff;color:#334155;cursor:pointer;",
5283
+ "With photos"
5284
+ );
5285
+ photos.addEventListener("click", () => {
5286
+ state.withPhotos = !state.withPhotos;
5287
+ photos.style.cssText = "font-size:13px;padding:4px 12px;border-radius:999px;cursor:pointer;" + (state.withPhotos ? "background:#4169e1;color:#fff;border:1px solid #4169e1;" : "background:#fff;color:#334155;border:1px solid #e2e8f0;");
5288
+ onChange();
5289
+ });
5290
+ bar.appendChild(photos);
5291
+ return bar;
5292
+ }
5293
+ function renderWall(data) {
5294
+ target.innerHTML = "";
5295
+ const root = el(
5296
+ "section",
5297
+ "font-family:Inter,system-ui,-apple-system,sans-serif;color:#0f172a;max-width:760px;"
5298
+ );
5299
+ root.setAttribute("data-aegis-reviews", "true");
5300
+ const summary = data.aggregate.ai_summary;
5301
+ if (summary && summary.text) {
5302
+ const card = el(
5303
+ "div",
5304
+ "border:1px solid #ede9fe;background:#faf5ff;border-radius:12px;padding:12px 14px;margin-bottom:14px;"
5305
+ );
5306
+ const h = el("div", "display:flex;align-items:center;gap:6px;margin-bottom:6px;");
5307
+ h.appendChild(sparkles());
5308
+ h.appendChild(el("span", "font-size:12px;font-weight:700;color:#7c3aed;text-transform:uppercase;letter-spacing:.04em;", "AI summary"));
5309
+ card.appendChild(h);
5310
+ card.appendChild(el("p", "margin:0;font-size:14px;line-height:1.5;color:#334155;", summary.text));
5311
+ card.appendChild(
5312
+ el("p", "margin:6px 0 0;font-size:11px;color:#a78bda;text-align:right;", `summarized from ${summary.review_count} reviews`)
5313
+ );
5314
+ root.appendChild(card);
5315
+ }
5316
+ const head = el("div", "display:flex;align-items:center;gap:8px;");
5317
+ const avg = data.aggregate.avg_rating ?? 0;
5318
+ head.appendChild(stars(Math.round(avg), 18));
5319
+ head.appendChild(el("span", "font-size:20px;font-weight:700;", avg ? avg.toFixed(1) : "—"));
5320
+ head.appendChild(
5321
+ el("span", "font-size:13px;color:#64748b;", `${data.aggregate.count} review${data.aggregate.count === 1 ? "" : "s"}`)
5322
+ );
5323
+ root.appendChild(head);
5324
+ if (data.aggregate.count === 0) {
5325
+ root.appendChild(el("p", "margin-top:8px;font-size:14px;color:#94a3b8;", "No reviews yet."));
5326
+ target.appendChild(root);
5327
+ return;
5328
+ }
5329
+ root.appendChild(distributionRows(data.aggregate.distribution, data.aggregate.count));
5330
+ root.appendChild(
5331
+ controls(() => {
5332
+ void fetchReviews().then((d) => renderList((d == null ? void 0 : d.items) ?? []));
5333
+ })
5334
+ );
5335
+ listContainer = el("div");
5336
+ root.appendChild(listContainer);
5337
+ renderList(data.items);
5338
+ target.appendChild(root);
5339
+ injectJsonLd(target, data, productId);
5340
+ ctx.trackEvent(ctx.campaign.id, "impression");
5341
+ }
5342
+ const displayStyle = (_a = ctx.campaign.interactive_config) == null ? void 0 : _a["display_style"];
5343
+ const compact = displayStyle === "compact";
5344
+ function renderCompact(data) {
5345
+ var _a2;
5346
+ target.innerHTML = "";
5347
+ if (data.aggregate.count === 0) return;
5348
+ const root = el(
5349
+ "section",
5350
+ "font-family:Inter,system-ui,-apple-system,sans-serif;color:#0f172a;max-width:760px;border:1px solid #e2e8f0;border-radius:12px;padding:12px 14px;"
5351
+ );
5352
+ const line = el("div", "display:flex;align-items:center;gap:8px;");
5353
+ const avg = data.aggregate.avg_rating ?? 0;
5354
+ line.appendChild(stars(Math.round(avg), 16));
5355
+ line.appendChild(el("span", "font-size:15px;font-weight:700;", avg ? avg.toFixed(1) : "—"));
5356
+ line.appendChild(el("span", "font-size:13px;color:#64748b;", `(${data.aggregate.count})`));
5357
+ line.appendChild(chip("Verified reviews", "verified"));
5358
+ root.appendChild(line);
5359
+ const top = data.items.find((i) => i.review_text);
5360
+ if (top && top.review_text) {
5361
+ const q = el("p", "margin:8px 0 0;font-size:14px;color:#334155;", `“${top.review_text}”`);
5362
+ root.appendChild(q);
5363
+ root.appendChild(
5364
+ el("span", "font-size:12px;color:#94a3b8;", `— ${((_a2 = top.reviewer_snapshot) == null ? void 0 : _a2.name) ?? "Customer"}`)
5365
+ );
5366
+ }
5367
+ const seeAll = el(
5368
+ "button",
5369
+ "margin-top:10px;font-size:13px;font-weight:600;color:#4169e1;background:none;border:none;cursor:pointer;padding:0;",
5370
+ "See all reviews"
5371
+ );
5372
+ seeAll.addEventListener("click", () => renderWall(data));
5373
+ root.appendChild(seeAll);
5374
+ target.appendChild(root);
5375
+ ctx.trackEvent(ctx.campaign.id, "impression");
5376
+ }
5377
+ void fetchReviews().then((data) => {
5378
+ if (!data || !Array.isArray(data.items)) {
5379
+ target.innerHTML = "";
5380
+ return;
5381
+ }
5382
+ if (compact) renderCompact(data);
5383
+ else renderWall(data);
5384
+ });
5385
+ return true;
5386
+ }
5387
+ function renderReviewsOverlay(ctx, creds) {
5388
+ if (typeof document === "undefined" || !(creds == null ? void 0 : creds.apiHost) || !(creds == null ? void 0 : creds.writeKey)) return;
5389
+ const overlay = document.createElement("div");
5390
+ overlay.setAttribute("data-campaign-id", ctx.campaign.id);
5391
+ overlay.style.cssText = "position:fixed;inset:0;background:rgba(15,23,42,0.55);display:flex;align-items:flex-end;justify-content:center;z-index:99999;";
5392
+ const sheet = document.createElement("div");
5393
+ sheet.style.cssText = "background:#fff;color:#0f172a;max-width:620px;width:100%;max-height:82vh;overflow-y:auto;border-radius:20px 20px 0 0;padding:16px 16px 24px;font-family:Inter,system-ui,-apple-system,sans-serif;box-shadow:0 -12px 30px rgba(0,0,0,0.12);";
5394
+ const handle = el("div", "width:36px;height:4px;border-radius:999px;background:#e2e8f0;margin:0 auto 12px;");
5395
+ sheet.appendChild(handle);
5396
+ const bar = el("div", "display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;");
5397
+ bar.appendChild(el("div", "font-weight:700;font-size:16px;", ctx.campaign.title || "Customer reviews"));
5398
+ const close = el("button", "background:none;border:none;cursor:pointer;padding:4px 8px;color:#64748b;");
5399
+ const xsvg = document.createElementNS(SVG_NS, "svg");
5400
+ xsvg.setAttribute("viewBox", "0 0 24 24");
5401
+ xsvg.setAttribute("width", "18");
5402
+ xsvg.setAttribute("height", "18");
5403
+ const xp = document.createElementNS(SVG_NS, "path");
5404
+ xp.setAttribute("d", "M18 6L6 18M6 6l12 12");
5405
+ xp.setAttribute("stroke", "currentColor");
5406
+ xp.setAttribute("stroke-width", "2");
5407
+ xp.setAttribute("stroke-linecap", "round");
5408
+ xsvg.appendChild(xp);
5409
+ close.appendChild(xsvg);
5410
+ close.setAttribute("aria-label", "Close");
5411
+ close.addEventListener("click", () => {
5412
+ ctx.trackEvent(ctx.campaign.id, "dismissed");
5413
+ overlay.remove();
5414
+ });
5415
+ bar.appendChild(close);
5416
+ sheet.appendChild(bar);
5417
+ const body = el("div");
5418
+ const pid = ctx.campaign.product_id;
5419
+ if (pid) body.setAttribute("data-product-id", pid);
5420
+ sheet.appendChild(body);
5421
+ overlay.appendChild(sheet);
5422
+ overlay.addEventListener("click", (e) => {
5423
+ if (e.target === overlay) {
5424
+ ctx.trackEvent(ctx.campaign.id, "dismissed");
5425
+ overlay.remove();
5426
+ }
5427
+ });
5428
+ document.body.appendChild(overlay);
5429
+ renderReviewsInline(ctx, body, creds);
5430
+ }
5018
5431
  function readAnonIdFromStorage$1() {
5019
5432
  if (typeof document === "undefined") return void 0;
5020
5433
  try {
@@ -5642,8 +6055,8 @@ const _AegisInAppManager = class _AegisInAppManager {
5642
6055
  m2.addedNodes.forEach((n) => {
5643
6056
  var _a, _b;
5644
6057
  if (sawSlot || n.nodeType !== 1) return;
5645
- const el = n;
5646
- if (((_a = el.matches) == null ? void 0 : _a.call(el, "[data-aegis-slot]")) || ((_b = el.querySelector) == null ? void 0 : _b.call(el, "[data-aegis-slot]"))) {
6058
+ const el2 = n;
6059
+ if (((_a = el2.matches) == null ? void 0 : _a.call(el2, "[data-aegis-slot]")) || ((_b = el2.querySelector) == null ? void 0 : _b.call(el2, "[data-aegis-slot]"))) {
5647
6060
  sawSlot = true;
5648
6061
  }
5649
6062
  });
@@ -5850,13 +6263,13 @@ const _AegisInAppManager = class _AegisInAppManager {
5850
6263
  const anchor = ic == null ? void 0 : ic.anchor;
5851
6264
  const selector = anchor == null ? void 0 : anchor.element_selector;
5852
6265
  if (!selector) continue;
5853
- let el = null;
6266
+ let el2 = null;
5854
6267
  try {
5855
- el = document.querySelector(selector);
6268
+ el2 = document.querySelector(selector);
5856
6269
  } catch {
5857
- el = null;
6270
+ el2 = null;
5858
6271
  }
5859
- if (!el) {
6272
+ if (!el2) {
5860
6273
  void this.trackEvent(c.id, "anchor_unresolved", {
5861
6274
  meta: { anchor_resolved: false, anchor_selector: selector, widget_type: c.sub_type }
5862
6275
  });
@@ -5867,9 +6280,9 @@ const _AegisInAppManager = class _AegisInAppManager {
5867
6280
  if (position === "before" || position === "after") {
5868
6281
  target = document.createElement("div");
5869
6282
  target.setAttribute("data-aegis-anchor-wrapper", c.id);
5870
- (_a = el.parentNode) == null ? void 0 : _a.insertBefore(target, position === "before" ? el : el.nextSibling);
6283
+ (_a = el2.parentNode) == null ? void 0 : _a.insertBefore(target, position === "before" ? el2 : el2.nextSibling);
5871
6284
  } else {
5872
- target = el;
6285
+ target = el2;
5873
6286
  if (position === "replace") {
5874
6287
  target.querySelectorAll(":scope > [data-aegis-slot-default]").forEach((d) => d.remove());
5875
6288
  }
@@ -6026,6 +6439,12 @@ const _AegisInAppManager = class _AegisInAppManager {
6026
6439
  case "product_recommendation":
6027
6440
  rendered = renderProductRecommendationInline(this.buildRenderContext(campaign), target);
6028
6441
  break;
6442
+ case "reviews":
6443
+ rendered = renderReviewsInline(this.buildRenderContext(campaign), target, {
6444
+ apiHost: this.apiHost,
6445
+ writeKey: this.writeKey
6446
+ });
6447
+ break;
6029
6448
  case "quiz":
6030
6449
  rendered = this.renderQuizSlot(campaign, ic, bg, text, target);
6031
6450
  if (!rendered) {
@@ -6258,10 +6677,10 @@ const _AegisInAppManager = class _AegisInAppManager {
6258
6677
  cbody.appendChild(b);
6259
6678
  }
6260
6679
  if (couponCode) {
6261
- const chip = document.createElement("span");
6262
- chip.style.cssText = `align-self: flex-start; border: 1px dashed ${text}66; border-radius: 6px; padding: 2px 8px; font-family: monospace; font-size: 11px; letter-spacing: 0.04em;`;
6263
- chip.textContent = couponCode;
6264
- cbody.appendChild(chip);
6680
+ const chip2 = document.createElement("span");
6681
+ chip2.style.cssText = `align-self: flex-start; border: 1px dashed ${text}66; border-radius: 6px; padding: 2px 8px; font-family: monospace; font-size: 11px; letter-spacing: 0.04em;`;
6682
+ chip2.textContent = couponCode;
6683
+ cbody.appendChild(chip2);
6265
6684
  }
6266
6685
  if (buttonText) {
6267
6686
  const btn = document.createElement("button");
@@ -6844,19 +7263,19 @@ const _AegisInAppManager = class _AegisInAppManager {
6844
7263
  ov.appendChild(eyebrow);
6845
7264
  ov.appendChild(big);
6846
7265
  if (r.code) {
6847
- const chip = document.createElement("button");
6848
- chip.type = "button";
6849
- chip.textContent = `${r.code} ⧉`;
6850
- chip.style.cssText = `margin-top: 4px; padding: 9px 16px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 14px; letter-spacing: 1px; cursor: pointer;`;
6851
- chip.addEventListener("click", () => {
7266
+ const chip2 = document.createElement("button");
7267
+ chip2.type = "button";
7268
+ chip2.textContent = `${r.code} ⧉`;
7269
+ chip2.style.cssText = `margin-top: 4px; padding: 9px 16px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 14px; letter-spacing: 1px; cursor: pointer;`;
7270
+ chip2.addEventListener("click", () => {
6852
7271
  var _a;
6853
7272
  try {
6854
7273
  void ((_a = navigator.clipboard) == null ? void 0 : _a.writeText(r.code));
6855
- chip.textContent = "Copied";
7274
+ chip2.textContent = "Copied";
6856
7275
  } catch {
6857
7276
  }
6858
7277
  });
6859
- ov.appendChild(chip);
7278
+ ov.appendChild(chip2);
6860
7279
  } else if (r.kind === "freebie") {
6861
7280
  const hint = document.createElement("div");
6862
7281
  hint.textContent = "🎟️ Show this screen to claim";
@@ -6890,20 +7309,20 @@ const _AegisInAppManager = class _AegisInAppManager {
6890
7309
  result.textContent = "";
6891
7310
  const tag = document.createElement("span");
6892
7311
  tag.textContent = `${r.label} — `;
6893
- const chip = document.createElement("button");
6894
- chip.type = "button";
6895
- chip.textContent = `${r.code} ⧉`;
6896
- chip.style.cssText = `padding: 2px 8px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 12px; letter-spacing: 0.5px; cursor: pointer;`;
6897
- chip.addEventListener("click", () => {
7312
+ const chip2 = document.createElement("button");
7313
+ chip2.type = "button";
7314
+ chip2.textContent = `${r.code} ⧉`;
7315
+ chip2.style.cssText = `padding: 2px 8px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 12px; letter-spacing: 0.5px; cursor: pointer;`;
7316
+ chip2.addEventListener("click", () => {
6898
7317
  var _a;
6899
7318
  try {
6900
7319
  void ((_a = navigator.clipboard) == null ? void 0 : _a.writeText(r.code));
6901
- chip.textContent = "Copied";
7320
+ chip2.textContent = "Copied";
6902
7321
  } catch {
6903
7322
  }
6904
7323
  });
6905
7324
  result.appendChild(tag);
6906
- result.appendChild(chip);
7325
+ result.appendChild(chip2);
6907
7326
  } else result.textContent = r.label || "Thanks for playing!";
6908
7327
  retireCta();
6909
7328
  };
@@ -7078,17 +7497,27 @@ const _AegisInAppManager = class _AegisInAppManager {
7078
7497
  h.textContent = campaign.title;
7079
7498
  card.appendChild(h);
7080
7499
  }
7500
+ const layout = ic.rec_layout || "row";
7081
7501
  const strip = document.createElement("div");
7082
- strip.style.cssText = `display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x mandatory; padding: ${st.padY ?? 12}px ${st.padX ?? 12}px;`;
7502
+ const pad = `${st.padY ?? 12}px ${st.padX ?? 12}px`;
7503
+ if (layout === "grid") {
7504
+ strip.style.cssText = `display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; padding: ${pad};`;
7505
+ } else if (layout === "list") {
7506
+ strip.style.cssText = `display: flex; flex-direction: column; gap: 8px; padding: ${pad};`;
7507
+ } else {
7508
+ strip.style.cssText = `display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; scrollbar-width: none; padding: ${pad};`;
7509
+ }
7083
7510
  const template = resolveGlobalTemplate(ic);
7084
7511
  const ctx = this.buildRenderContext(campaign);
7085
7512
  const accent = this.sanitizeColor(campaign.background_color || text);
7086
7513
  cards.forEach((c, i) => {
7514
+ const meta = c.metadata || {};
7515
+ const linked = !!(meta.product_id || meta.sku);
7087
7516
  const tile = renderCardFromTemplate(c, {
7088
7517
  template,
7089
7518
  ctx,
7090
- action: "navigate",
7091
- layout: "row",
7519
+ action: linked ? "cart" : "navigate",
7520
+ layout,
7092
7521
  layoutAccent: accent
7093
7522
  });
7094
7523
  tile.setAttribute("data-card-index", String(i));
@@ -7096,6 +7525,43 @@ const _AegisInAppManager = class _AegisInAppManager {
7096
7525
  });
7097
7526
  card.appendChild(strip);
7098
7527
  target.appendChild(card);
7528
+ const autoplayMs = Number(ic.autoplay_ms) || 0;
7529
+ if (layout === "row" && autoplayMs > 0 && cards.length > 1) {
7530
+ const loop = ic.loop !== false;
7531
+ const interval = Math.max(autoplayMs, 600);
7532
+ let idx = 0;
7533
+ let timer = null;
7534
+ const stop = () => {
7535
+ if (timer) {
7536
+ clearInterval(timer);
7537
+ timer = null;
7538
+ }
7539
+ };
7540
+ timer = setInterval(() => {
7541
+ if (!document.body.contains(strip)) {
7542
+ stop();
7543
+ return;
7544
+ }
7545
+ if (strip.scrollWidth <= strip.clientWidth + 4) return;
7546
+ const tiles = Array.from(strip.children);
7547
+ idx += 1;
7548
+ if (idx >= tiles.length) {
7549
+ if (!loop) {
7550
+ stop();
7551
+ return;
7552
+ }
7553
+ idx = 0;
7554
+ }
7555
+ const el2 = tiles[idx];
7556
+ if (el2) {
7557
+ const delta = el2.getBoundingClientRect().left - strip.getBoundingClientRect().left;
7558
+ strip.scrollTo({ left: strip.scrollLeft + delta, behavior: "smooth" });
7559
+ }
7560
+ }, interval);
7561
+ ["pointerdown", "wheel", "touchstart"].forEach(
7562
+ (evt) => strip.addEventListener(evt, stop, { passive: true })
7563
+ );
7564
+ }
7099
7565
  return true;
7100
7566
  }
7101
7567
  /** Built-in embedded COUNTDOWN — live H:M:S digits that pulse red in the
@@ -7118,10 +7584,10 @@ const _AegisInAppManager = class _AegisInAppManager {
7118
7584
  const digits = document.createElement("div");
7119
7585
  digits.style.cssText = "display: flex; gap: 6px; justify-content: center;";
7120
7586
  const mkDigit = () => {
7121
- const el = document.createElement("span");
7122
- el.style.cssText = `padding: 6px 9px; border-radius: 8px; font-size: 18px; font-weight: 700; font-family: ui-monospace, monospace; background: ${text}1a; transition: background 0.3s ease, color 0.3s ease;`;
7123
- el.textContent = "00";
7124
- return el;
7587
+ const el2 = document.createElement("span");
7588
+ el2.style.cssText = `padding: 6px 9px; border-radius: 8px; font-size: 18px; font-weight: 700; font-family: ui-monospace, monospace; background: ${text}1a; transition: background 0.3s ease, color 0.3s ease;`;
7589
+ el2.textContent = "00";
7590
+ return el2;
7125
7591
  };
7126
7592
  const hEl = mkDigit();
7127
7593
  const mEl = mkDigit();
@@ -7151,9 +7617,9 @@ const _AegisInAppManager = class _AegisInAppManager {
7151
7617
  sEl.textContent = String(s).padStart(2, "0");
7152
7618
  if (diff > 0 && diff <= 6e4 && !urgent) {
7153
7619
  urgent = true;
7154
- [hEl, mEl, sEl].forEach((el) => {
7155
- el.style.boxShadow = "0 0 0 2px #ef4444";
7156
- el.style.animation = "aegisUrgencyPulse 1s ease-in-out infinite";
7620
+ [hEl, mEl, sEl].forEach((el2) => {
7621
+ el2.style.boxShadow = "0 0 0 2px #ef4444";
7622
+ el2.style.animation = "aegisUrgencyPulse 1s ease-in-out infinite";
7157
7623
  });
7158
7624
  }
7159
7625
  if (diff > 0) requestAnimationFrame(tick);
@@ -7508,19 +7974,19 @@ const _AegisInAppManager = class _AegisInAppManager {
7508
7974
  ov.appendChild(eb);
7509
7975
  ov.appendChild(big);
7510
7976
  if (code) {
7511
- const chip = document.createElement("button");
7512
- chip.type = "button";
7513
- chip.textContent = `${code} ⧉`;
7514
- chip.style.cssText = `margin-top: 4px; padding: 8px 14px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 13px; letter-spacing: 1px; cursor: pointer;`;
7515
- chip.addEventListener("click", () => {
7977
+ const chip2 = document.createElement("button");
7978
+ chip2.type = "button";
7979
+ chip2.textContent = `${code} ⧉`;
7980
+ chip2.style.cssText = `margin-top: 4px; padding: 8px 14px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 13px; letter-spacing: 1px; cursor: pointer;`;
7981
+ chip2.addEventListener("click", () => {
7516
7982
  var _a;
7517
7983
  try {
7518
7984
  void ((_a = navigator.clipboard) == null ? void 0 : _a.writeText(code));
7519
- chip.textContent = "Copied";
7985
+ chip2.textContent = "Copied";
7520
7986
  } catch {
7521
7987
  }
7522
7988
  });
7523
- ov.appendChild(chip);
7989
+ ov.appendChild(chip2);
7524
7990
  }
7525
7991
  card.appendChild(ov);
7526
7992
  try {
@@ -7530,10 +7996,10 @@ const _AegisInAppManager = class _AegisInAppManager {
7530
7996
  } else {
7531
7997
  prizeText.textContent = icoVal && !isUrl(icoVal) ? `${icoVal} ${label}` : label;
7532
7998
  if (code) {
7533
- const chip = document.createElement("div");
7534
- chip.style.cssText = `margin-top: 6px; font-family: ui-monospace, monospace; font-size: 12px; font-weight: 700; padding: 4px 8px; border-radius: 6px; background: ${text}14; color: ${text};`;
7535
- chip.textContent = code;
7536
- body.appendChild(chip);
7999
+ const chip2 = document.createElement("div");
8000
+ chip2.style.cssText = `margin-top: 6px; font-family: ui-monospace, monospace; font-size: 12px; font-weight: 700; padding: 4px 8px; border-radius: 6px; background: ${text}14; color: ${text};`;
8001
+ chip2.textContent = code;
8002
+ body.appendChild(chip2);
7537
8003
  }
7538
8004
  this._playReaction(body, "celebrate", "gift.json", ic);
7539
8005
  }
@@ -7695,9 +8161,9 @@ const _AegisInAppManager = class _AegisInAppManager {
7695
8161
  */
7696
8162
  _flattenSlotCard(container) {
7697
8163
  const cards = container.matches("[data-campaign-id]") ? [container] : Array.from(container.querySelectorAll("[data-campaign-id]"));
7698
- for (const el of cards) {
7699
- el.style.borderRadius = "0";
7700
- el.style.boxShadow = "none";
8164
+ for (const el2 of cards) {
8165
+ el2.style.borderRadius = "0";
8166
+ el2.style.boxShadow = "none";
7701
8167
  }
7702
8168
  }
7703
8169
  /**
@@ -7783,6 +8249,15 @@ const _AegisInAppManager = class _AegisInAppManager {
7783
8249
  this.emit("campaign-shown", campaign);
7784
8250
  return;
7785
8251
  }
8252
+ if (campaign.sub_type === "reviews") {
8253
+ renderReviewsOverlay(this.buildRenderContext(campaign), {
8254
+ apiHost: this.apiHost,
8255
+ writeKey: this.writeKey
8256
+ });
8257
+ this.trackEvent(campaign.id, "impression");
8258
+ this.emit("campaign-shown", campaign);
8259
+ return;
8260
+ }
7786
8261
  {
7787
8262
  const ic2 = campaign.interactive_config || {};
7788
8263
  const multiCard2 = campaign.sub_type === "product_recommendation" || campaign.sub_type === "carousel_cards";
@@ -7910,7 +8385,7 @@ const _AegisInAppManager = class _AegisInAppManager {
7910
8385
  navigate: (url, buttonId) => this.navigateToCampaignAction(campaign, url, buttonId),
7911
8386
  dispatchAction: (action) => this.dispatchAction(action, campaign.id),
7912
8387
  submitResponse: (responseType, payload) => this.submitWidgetResponse(campaign, responseType, payload),
7913
- playReaction: (el, intent, lottieFile, scope) => this._playReaction(el, intent, lottieFile, campaign.interactive_config, scope),
8388
+ playReaction: (el2, intent, lottieFile, scope) => this._playReaction(el2, intent, lottieFile, campaign.interactive_config, scope),
7914
8389
  haptic: (pattern) => {
7915
8390
  try {
7916
8391
  if (typeof navigator !== "undefined" && typeof navigator.vibrate === "function") {
@@ -8167,9 +8642,9 @@ const _AegisInAppManager = class _AegisInAppManager {
8167
8642
  if (cells.s) cells.s.textContent = pad(s);
8168
8643
  if (!urgent && diff > 0 && diff <= 6e4) {
8169
8644
  urgent = true;
8170
- Object.values(cells).forEach((el) => {
8171
- if (el) {
8172
- el.style.boxShadow = "0 0 0 2px #ef4444";
8645
+ Object.values(cells).forEach((el2) => {
8646
+ if (el2) {
8647
+ el2.style.boxShadow = "0 0 0 2px #ef4444";
8173
8648
  }
8174
8649
  });
8175
8650
  digits.style.animation = "aegisCountdownPulse 1.1s ease-in-out infinite";
@@ -8698,58 +9173,21 @@ const _AegisInAppManager = class _AegisInAppManager {
8698
9173
  * 3. if STILL unhandled → canonical fallback (navigate to the product/cart
8699
9174
  * page) so a CTA is never a dead no-op (the rail-"Add" 3rd-party gap).
8700
9175
  * Both add-to-cart paths (card "Add" + the `aegis://cart-add` verb) funnel here,
8701
- * so they're one behaviour with one legacy event (`aegis:add-to-cart`).
9176
+ * so they're one behaviour delivered to the host's single `onAction` handler.
9177
+ * (Phase E.2 hard cutover — the legacy `aegis:*` DOM events were removed.)
8702
9178
  */
8703
- dispatchAction(action, campaignId) {
9179
+ dispatchAction(action, _campaignId) {
8704
9180
  var _a;
8705
9181
  let handled = false;
8706
9182
  try {
8707
9183
  handled = ((_a = this.onActionHandler) == null ? void 0 : _a.call(this, action)) === true;
8708
9184
  } catch {
8709
9185
  }
8710
- if (typeof window !== "undefined") {
8711
- const legacy = this._legacyEventFor(action, campaignId);
8712
- if (legacy) {
8713
- const evt = new CustomEvent(legacy.name, { detail: legacy.detail, cancelable: true });
8714
- const proceed = window.dispatchEvent(evt);
8715
- if (!proceed || evt.defaultPrevented) handled = true;
8716
- }
8717
- }
8718
9186
  if (!handled) {
8719
9187
  const url = fallbackUrlForAction(action, this.productUrl);
8720
9188
  if (url && typeof window !== "undefined") window.location.href = url;
8721
9189
  }
8722
9190
  }
8723
- /** Back-compat mapping: canonical action → the legacy DOM event hosts listen
8724
- * for today. `add_to_cart` always emits `aegis:add-to-cart` (the one ACS
8725
- * handles), collapsing the old `aegis:cart-add` divergence. */
8726
- _legacyEventFor(a, campaignId) {
8727
- const base = { campaign_id: campaignId };
8728
- switch (a.type) {
8729
- case "add_to_cart":
8730
- return {
8731
- name: "aegis:add-to-cart",
8732
- detail: {
8733
- ...base,
8734
- product_id: a.productId,
8735
- sku: a.sku,
8736
- title: a.title,
8737
- price: a.price,
8738
- image_url: a.imageUrl,
8739
- cta_url: a.url,
8740
- qty: a.qty ?? 1
8741
- }
8742
- };
8743
- case "open_product":
8744
- return { name: "aegis:open-product", detail: { ...base, sku: a.sku, product_id: a.productId, url: a.url } };
8745
- case "open_cart":
8746
- return { name: "aegis:open-cart", detail: base };
8747
- case "open_login":
8748
- return { name: "aegis:open-login", detail: base };
8749
- default:
8750
- return null;
8751
- }
8752
- }
8753
9191
  /**
8754
9192
  * Phase-2 CTA action verbs (`aegis://<verb>?<params>`). Performs an in-app
8755
9193
  * behaviour instead of navigating. Verbs:
@@ -8789,9 +9227,9 @@ const _AegisInAppManager = class _AegisInAppManager {
8789
9227
  /** Remove a rendered campaign's element(s) from the DOM by campaign id. */
8790
9228
  removeCampaignEl(campaignId) {
8791
9229
  if (typeof document === "undefined") return;
8792
- document.querySelectorAll(`[data-campaign-id="${campaignId}"]`).forEach((el) => {
9230
+ document.querySelectorAll(`[data-campaign-id="${campaignId}"]`).forEach((el2) => {
8793
9231
  var _a;
8794
- return (_a = el.parentNode) == null ? void 0 : _a.removeChild(el);
9232
+ return (_a = el2.parentNode) == null ? void 0 : _a.removeChild(el2);
8795
9233
  });
8796
9234
  }
8797
9235
  /**
@@ -10518,9 +10956,9 @@ const _AegisInAppManager = class _AegisInAppManager {
10518
10956
  if (typeof document !== "undefined") {
10519
10957
  document.querySelectorAll(
10520
10958
  ".aegis-in-app-banner, .aegis-in-app-modal-overlay, .aegis-in-app-fullscreen-overlay, .aegis-in-app-half-interstitial-overlay, .aegis-in-app-alert-overlay, .aegis-in-app-pip, .aegis-in-app-nps-overlay, .aegis-in-app-countdown-overlay, .aegis-in-app-rating-overlay, .aegis-in-app-poll-overlay, .aegis-in-app-quiz-overlay, .aegis-in-app-rating-card, .aegis-in-app-nps-card"
10521
- ).forEach((el) => {
10522
- if (el.parentNode) {
10523
- el.parentNode.removeChild(el);
10959
+ ).forEach((el2) => {
10960
+ if (el2.parentNode) {
10961
+ el2.parentNode.removeChild(el2);
10524
10962
  }
10525
10963
  });
10526
10964
  const styles = document.getElementById("aegis-in-app-styles");
@@ -10552,9 +10990,9 @@ let AegisInAppManager = _AegisInAppManager;
10552
10990
  function renderPreview(config, opts) {
10553
10991
  document.querySelectorAll(
10554
10992
  '[class^="aegis-in-app-"]'
10555
- ).forEach((el) => {
10556
- if (el.parentNode) {
10557
- el.parentNode.removeChild(el);
10993
+ ).forEach((el2) => {
10994
+ if (el2.parentNode) {
10995
+ el2.parentNode.removeChild(el2);
10558
10996
  }
10559
10997
  });
10560
10998
  try {
@@ -10590,7 +11028,7 @@ function renderPreview(config, opts) {
10590
11028
  if (anchors.length > 0) {
10591
11029
  try {
10592
11030
  anchors.forEach((slot) => {
10593
- slot.querySelectorAll(":scope > :not([data-aegis-slot-default])").forEach((el) => el.remove());
11031
+ slot.querySelectorAll(":scope > :not([data-aegis-slot-default])").forEach((el2) => el2.remove());
10594
11032
  });
10595
11033
  const mm = manager;
10596
11034
  mm.campaigns = (inline ? [config, ...coGroup] : coGroup).map((c) => ({ ...c, surface: void 0, target_screens: void 0 })).sort((a, b) => (b.priority || 0) - (a.priority || 0));
@@ -10611,17 +11049,17 @@ function renderPreview(config, opts) {
10611
11049
  m2.displayCampaign(config);
10612
11050
  }
10613
11051
  const MAX_NODES = 400;
10614
- function selectorFor(el) {
10615
- if (el.id) return `#${CSS.escape(el.id)}`;
11052
+ function selectorFor(el2) {
11053
+ if (el2.id) return `#${CSS.escape(el2.id)}`;
10616
11054
  for (const attr of ["data-testid", "data-test", "data-aegis-anchor", "data-qa", "name"]) {
10617
- const v = el.getAttribute(attr);
11055
+ const v = el2.getAttribute(attr);
10618
11056
  if (v) {
10619
- const sel = `${el.tagName.toLowerCase()}[${attr}="${CSS.escape(v)}"]`;
11057
+ const sel = `${el2.tagName.toLowerCase()}[${attr}="${CSS.escape(v)}"]`;
10620
11058
  if (document.querySelectorAll(sel).length === 1) return sel;
10621
11059
  }
10622
11060
  }
10623
11061
  const parts = [];
10624
- let node = el;
11062
+ let node = el2;
10625
11063
  while (node && node !== document.body && parts.length < 6) {
10626
11064
  if (node.id) {
10627
11065
  parts.unshift(`#${CSS.escape(node.id)}`);
@@ -10645,15 +11083,15 @@ function captureWebScreen(screenName) {
10645
11083
  const candidates = document.querySelectorAll(
10646
11084
  "a,button,input,select,textarea,[role],[data-aegis-anchor],[data-testid],h1,h2,h3,img,[data-aegis-slot]"
10647
11085
  );
10648
- for (const el of Array.from(candidates)) {
11086
+ for (const el2 of Array.from(candidates)) {
10649
11087
  if (nodes.length >= MAX_NODES) break;
10650
- const rect = el.getBoundingClientRect();
11088
+ const rect = el2.getBoundingClientRect();
10651
11089
  if (rect.width <= 0 || rect.height <= 0) continue;
10652
- const slotKey = el.getAttribute("data-aegis-slot");
10653
- const role = slotKey ? `slot:${slotKey}` : el.getAttribute("role") || el.tagName.toLowerCase();
10654
- const text = (el.textContent || el.getAttribute("aria-label") || "").trim().slice(0, 80);
11090
+ const slotKey = el2.getAttribute("data-aegis-slot");
11091
+ const role = slotKey ? `slot:${slotKey}` : el2.getAttribute("role") || el2.tagName.toLowerCase();
11092
+ const text = (el2.textContent || el2.getAttribute("aria-label") || "").trim().slice(0, 80);
10655
11093
  nodes.push({
10656
- selector: selectorFor(el),
11094
+ selector: selectorFor(el2),
10657
11095
  role,
10658
11096
  ...text ? { text } : {},
10659
11097
  bounds: {