@syntrologie/adapt-product 2.34.0 → 2.36.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/cdn.js +2 -2
- package/dist/{chunk-NH3CFDTZ.js → chunk-SLHBNATX.js} +1130 -483
- package/dist/chunk-SLHBNATX.js.map +7 -0
- package/dist/{chunk-FAKCB3BS.js → chunk-Y2QAYCSK.js} +72 -30
- package/dist/chunk-Y2QAYCSK.js.map +7 -0
- package/dist/native/nativeSlotWatcher.d.ts.map +1 -1
- package/dist/runtime.d.ts +12 -10
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +6 -2
- package/dist/schema-primitives.d.ts +4 -4
- package/dist/schema.d.ts +148 -58
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +9 -1
- package/dist/takeover/engine.d.ts +45 -0
- package/dist/takeover/engine.d.ts.map +1 -0
- package/dist/takeover/fetchPlan.d.ts +61 -0
- package/dist/takeover/fetchPlan.d.ts.map +1 -0
- package/dist/takeover/ports.d.ts +29 -0
- package/dist/takeover/ports.d.ts.map +1 -0
- package/dist/takeover/regionCurtain.d.ts +17 -0
- package/dist/takeover/regionCurtain.d.ts.map +1 -0
- package/dist/takeover/renderModules.d.ts +48 -0
- package/dist/takeover/renderModules.d.ts.map +1 -0
- package/dist/takeover/resurrectionGuard.d.ts +28 -0
- package/dist/takeover/resurrectionGuard.d.ts.map +1 -0
- package/dist/takeover/schema.d.ts +171 -0
- package/dist/takeover/schema.d.ts.map +1 -0
- package/dist/takeover/sectionDetach.d.ts +13 -0
- package/dist/takeover/sectionDetach.d.ts.map +1 -0
- package/dist/widgets/SyntroPdpLit.d.ts +69 -0
- package/dist/widgets/SyntroPdpLit.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FAKCB3BS.js.map +0 -7
- package/dist/chunk-NH3CFDTZ.js.map +0 -7
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
getProductPlatformAdapter,
|
|
10
10
|
gridSchema,
|
|
11
11
|
heroSchema,
|
|
12
|
-
recoSchema
|
|
13
|
-
|
|
12
|
+
recoSchema,
|
|
13
|
+
takeoverPlanSchema
|
|
14
|
+
} from "./chunk-Y2QAYCSK.js";
|
|
14
15
|
|
|
15
16
|
// ../../../node_modules/@lit/context/lib/create-context.js
|
|
16
17
|
function n(n2) {
|
|
@@ -279,7 +280,7 @@ function selectPresetData(kind, data, decision) {
|
|
|
279
280
|
}
|
|
280
281
|
|
|
281
282
|
// src/widgets/SyntroPdpLit.ts
|
|
282
|
-
import { html as
|
|
283
|
+
import { html as html6, LitElement as LitElement6, render } from "lit";
|
|
283
284
|
|
|
284
285
|
// src/decision/pdp.ts
|
|
285
286
|
function createInitialPdpState(generation = 0) {
|
|
@@ -734,6 +735,334 @@ async function resolveScoredTemplate(opts) {
|
|
|
734
735
|
return { value, decisionValue, setSlots, unsetSlots };
|
|
735
736
|
}
|
|
736
737
|
|
|
738
|
+
// src/takeover/resurrectionGuard.ts
|
|
739
|
+
function startResurrectionGuard(opts) {
|
|
740
|
+
const maxRetries = opts.maxRetries ?? 3;
|
|
741
|
+
const debounceMs = opts.debounceMs ?? 50;
|
|
742
|
+
const watched = new Map(opts.anchors);
|
|
743
|
+
const counts = /* @__PURE__ */ new Map();
|
|
744
|
+
let timer = null;
|
|
745
|
+
let stopped = false;
|
|
746
|
+
const sweep = () => {
|
|
747
|
+
if (stopped) return;
|
|
748
|
+
const present = [];
|
|
749
|
+
for (const [sectionId, selector] of watched) {
|
|
750
|
+
let matches = [];
|
|
751
|
+
try {
|
|
752
|
+
matches = Array.from(opts.region.querySelectorAll(selector));
|
|
753
|
+
} catch {
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
const el = matches.find((m) => !(opts.ignore?.(m) ?? false));
|
|
757
|
+
if (!el) continue;
|
|
758
|
+
present.push({ sectionId, el, n: (counts.get(sectionId) ?? 0) + 1 });
|
|
759
|
+
}
|
|
760
|
+
const exhausted = present.find((p) => p.n > maxRetries);
|
|
761
|
+
if (exhausted) {
|
|
762
|
+
for (const p of present) p.el.remove();
|
|
763
|
+
opts.onBudgetExhausted(exhausted.sectionId, exhausted.el);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
for (const { sectionId, el, n: n2 } of present) {
|
|
767
|
+
counts.set(sectionId, n2);
|
|
768
|
+
opts.onResurrected(sectionId, el);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
const observer2 = new MutationObserver(() => {
|
|
772
|
+
if (timer) clearTimeout(timer);
|
|
773
|
+
timer = setTimeout(sweep, debounceMs);
|
|
774
|
+
});
|
|
775
|
+
observer2.observe(opts.region, { childList: true, subtree: true });
|
|
776
|
+
return {
|
|
777
|
+
stop() {
|
|
778
|
+
stopped = true;
|
|
779
|
+
if (timer) clearTimeout(timer);
|
|
780
|
+
observer2.disconnect();
|
|
781
|
+
},
|
|
782
|
+
unwatch(sectionId) {
|
|
783
|
+
watched.delete(sectionId);
|
|
784
|
+
counts.delete(sectionId);
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// src/takeover/sectionDetach.ts
|
|
790
|
+
var activeDetaches = /* @__PURE__ */ new WeakSet();
|
|
791
|
+
function detachSection(section) {
|
|
792
|
+
if (activeDetaches.has(section)) return null;
|
|
793
|
+
if (!section.parentNode) return null;
|
|
794
|
+
activeDetaches.add(section);
|
|
795
|
+
const anchor = document.createComment("syntro-takeover-section");
|
|
796
|
+
section.before(anchor);
|
|
797
|
+
section.remove();
|
|
798
|
+
let restored = false;
|
|
799
|
+
return {
|
|
800
|
+
section,
|
|
801
|
+
get restored() {
|
|
802
|
+
return restored;
|
|
803
|
+
},
|
|
804
|
+
restore() {
|
|
805
|
+
if (restored) return;
|
|
806
|
+
restored = true;
|
|
807
|
+
if (anchor.parentNode) anchor.after(section);
|
|
808
|
+
anchor.remove();
|
|
809
|
+
activeDetaches.delete(section);
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// src/takeover/engine.ts
|
|
815
|
+
var KIND_SUPERSEDES_ROLE = {
|
|
816
|
+
qa: "faq",
|
|
817
|
+
"peer-feed": "social-proof"
|
|
818
|
+
};
|
|
819
|
+
var DEFAULT_COMMIT_TIMEOUT_MS = 500;
|
|
820
|
+
var NOOP_DISPOSE = () => {
|
|
821
|
+
};
|
|
822
|
+
async function runTakeover(config, ports, opts = {}) {
|
|
823
|
+
if (!ports.hasSignals()) {
|
|
824
|
+
const declaredRoles = new Set(config.hostSections.map((s4) => s4.role));
|
|
825
|
+
void Promise.resolve().then(() => ports.fetchPlan()).then(
|
|
826
|
+
(response2) => ports.renderModules(
|
|
827
|
+
{
|
|
828
|
+
// Dropped on this path (nothing may detach, so the host's own
|
|
829
|
+
// sections all stay attached — rendering an overlap duplicates
|
|
830
|
+
// content, the exact class the spec forbids):
|
|
831
|
+
// 1. host-derived entries (kind:sectionId) — review F2;
|
|
832
|
+
// 2. native kinds superseding a DECLARED host role (two-Q&As
|
|
833
|
+
// bug: our qa next to their attached FAQ).
|
|
834
|
+
order: response2.plan.order.filter((entry) => {
|
|
835
|
+
if (entry.includes(":")) return false;
|
|
836
|
+
const superseded = KIND_SUPERSEDES_ROLE[entry];
|
|
837
|
+
return !(superseded && declaredRoles.has(superseded));
|
|
838
|
+
}),
|
|
839
|
+
suppress: []
|
|
840
|
+
},
|
|
841
|
+
() => {
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
).catch(() => ports.telemetry("takeover.render_failed", { path: "progressive" }));
|
|
845
|
+
return { outcome: "progressive", dispose: NOOP_DISPOSE };
|
|
846
|
+
}
|
|
847
|
+
const commitTimeoutMs = opts.commitTimeoutMs ?? DEFAULT_COMMIT_TIMEOUT_MS;
|
|
848
|
+
const region = ports.region();
|
|
849
|
+
const lift = ports.curtain();
|
|
850
|
+
let timeoutId;
|
|
851
|
+
const timedOut = /* @__PURE__ */ Symbol("timeout");
|
|
852
|
+
const fetchFailed = /* @__PURE__ */ Symbol("fetch-failed");
|
|
853
|
+
const response = await Promise.race([
|
|
854
|
+
ports.fetchPlan().catch(() => fetchFailed),
|
|
855
|
+
new Promise((r) => {
|
|
856
|
+
timeoutId = setTimeout(() => r(timedOut), commitTimeoutMs);
|
|
857
|
+
})
|
|
858
|
+
]).finally(() => clearTimeout(timeoutId));
|
|
859
|
+
if (response === timedOut || response === fetchFailed) {
|
|
860
|
+
ports.deactivateRoot();
|
|
861
|
+
lift();
|
|
862
|
+
if (response === timedOut) {
|
|
863
|
+
ports.telemetry("takeover.precommit_timeout", { budgetMs: commitTimeoutMs });
|
|
864
|
+
} else {
|
|
865
|
+
ports.telemetry("takeover.plan_fetch_failed", { budgetMs: commitTimeoutMs });
|
|
866
|
+
}
|
|
867
|
+
return { outcome: "aborted-precommit", dispose: NOOP_DISPOSE };
|
|
868
|
+
}
|
|
869
|
+
const plan = response.plan;
|
|
870
|
+
const sectionsById = new Map(config.hostSections.map((s4) => [s4.id, s4]));
|
|
871
|
+
const toSuppress = [];
|
|
872
|
+
let reservedPx = 0;
|
|
873
|
+
for (const id of plan.suppress) {
|
|
874
|
+
const section = sectionsById.get(id);
|
|
875
|
+
if (!section) {
|
|
876
|
+
ports.telemetry("takeover.suppress_undeclared", { sectionId: id });
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
let el = null;
|
|
880
|
+
try {
|
|
881
|
+
el = ports.findSection(section.anchor);
|
|
882
|
+
} catch {
|
|
883
|
+
}
|
|
884
|
+
if (!el) {
|
|
885
|
+
ports.telemetry("takeover.section_anchor_missing", { sectionId: id });
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
reservedPx += ports.measureSection(el);
|
|
889
|
+
toSuppress.push({ id, el });
|
|
890
|
+
}
|
|
891
|
+
ports.reserve(reservedPx);
|
|
892
|
+
const handles = /* @__PURE__ */ new Map();
|
|
893
|
+
for (const { id, el } of toSuppress) {
|
|
894
|
+
const handle = detachSection(el);
|
|
895
|
+
if (handle) handles.set(id, handle);
|
|
896
|
+
}
|
|
897
|
+
let disposed = false;
|
|
898
|
+
let guard;
|
|
899
|
+
const dispose = () => {
|
|
900
|
+
if (disposed) return;
|
|
901
|
+
disposed = true;
|
|
902
|
+
guard?.stop();
|
|
903
|
+
for (const handle of handles.values()) handle.restore();
|
|
904
|
+
handles.clear();
|
|
905
|
+
ports.deactivateRoot();
|
|
906
|
+
lift();
|
|
907
|
+
};
|
|
908
|
+
guard = startResurrectionGuard({
|
|
909
|
+
region,
|
|
910
|
+
ignore: (el) => ports.ownsNode(el),
|
|
911
|
+
anchors: new Map(toSuppress.map(({ id }) => [id, sectionsById.get(id).anchor])),
|
|
912
|
+
onResurrected: (sectionId, el) => {
|
|
913
|
+
ports.telemetry("takeover.section_resurrected", { sectionId });
|
|
914
|
+
el.remove();
|
|
915
|
+
},
|
|
916
|
+
onBudgetExhausted: (sectionId, el) => {
|
|
917
|
+
ports.telemetry("takeover.resurrection_budget_exhausted", { sectionId });
|
|
918
|
+
el.remove();
|
|
919
|
+
dispose();
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
const onModuleFailed = (suppressedSectionId) => {
|
|
923
|
+
if (!suppressedSectionId) return;
|
|
924
|
+
const handle = handles.get(suppressedSectionId);
|
|
925
|
+
if (!handle) return;
|
|
926
|
+
guard?.unwatch(suppressedSectionId);
|
|
927
|
+
handle.restore();
|
|
928
|
+
handles.delete(suppressedSectionId);
|
|
929
|
+
ports.telemetry("takeover.module_failed_section_restored", {
|
|
930
|
+
sectionId: suppressedSectionId
|
|
931
|
+
});
|
|
932
|
+
};
|
|
933
|
+
void Promise.resolve().then(() => ports.renderModules(plan, onModuleFailed)).catch(() => {
|
|
934
|
+
ports.telemetry("takeover.render_failed", { path: "committed" });
|
|
935
|
+
dispose();
|
|
936
|
+
}).finally(() => ports.releaseReservation());
|
|
937
|
+
lift();
|
|
938
|
+
return { outcome: "committed", dispose };
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// src/takeover/fetchPlan.ts
|
|
942
|
+
import { z as z2 } from "zod";
|
|
943
|
+
var takeoverPlanResponseSchema = z2.object({
|
|
944
|
+
decisionArchetype: z2.string().min(1),
|
|
945
|
+
plan: takeoverPlanSchema
|
|
946
|
+
}).strict();
|
|
947
|
+
function createTakeoverPlanFetcher(deps) {
|
|
948
|
+
return async () => {
|
|
949
|
+
const resp = await deps.authedFetch("/api/pdp/plan", {
|
|
950
|
+
method: "POST",
|
|
951
|
+
// FROZEN SEAM SHAPE — field set AND order are pinned byte-for-byte by
|
|
952
|
+
// `src/__fixtures__/takeover-plan-request.json`; the runtime-backend PR
|
|
953
|
+
// commits the same fixture. Change both sides together or not at all.
|
|
954
|
+
// Deliberately NO client-scored archetype/decision slugs: the server
|
|
955
|
+
// scores (spec rev 3) — the client sends vocabulary + signals only.
|
|
956
|
+
body: JSON.stringify({
|
|
957
|
+
product_id: deps.productId,
|
|
958
|
+
product_name: deps.productName,
|
|
959
|
+
// Page anatomy: ids + roles only. Anchors are host DOM selectors —
|
|
960
|
+
// client-side facts the server has no business receiving.
|
|
961
|
+
host_sections: deps.config.hostSections.map(({ id, role }) => ({ id, role })),
|
|
962
|
+
decision_archetypes: deps.decisionArchetypes,
|
|
963
|
+
behavior_summary: deps.behaviorSummary,
|
|
964
|
+
signals: deps.signals,
|
|
965
|
+
ingest_snapshot: deps.config.ingestSnapshot,
|
|
966
|
+
// The client's configurable wait (commitTimeoutMs, default 500):
|
|
967
|
+
// the server may classify inline within it (bounded inline classify).
|
|
968
|
+
budget_ms: deps.config.commitTimeoutMs ?? 500
|
|
969
|
+
})
|
|
970
|
+
});
|
|
971
|
+
if (!resp.ok) {
|
|
972
|
+
throw new Error(`takeover plan fetch failed: HTTP ${resp.status}`);
|
|
973
|
+
}
|
|
974
|
+
const data = await resp.json();
|
|
975
|
+
return takeoverPlanResponseSchema.parse(data);
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/takeover/regionCurtain.ts
|
|
980
|
+
var CLASS = "syntro-takeover-curtain";
|
|
981
|
+
var STYLE_ATTR = "data-syntro-takeover-curtain";
|
|
982
|
+
var styleRefs = 0;
|
|
983
|
+
function acquireStyle() {
|
|
984
|
+
styleRefs += 1;
|
|
985
|
+
if (document.head.querySelector(`style[${STYLE_ATTR}]`)) return;
|
|
986
|
+
const styleEl = document.createElement("style");
|
|
987
|
+
styleEl.setAttribute(STYLE_ATTR, "");
|
|
988
|
+
styleEl.textContent = `.${CLASS} { opacity: 0 !important; }`;
|
|
989
|
+
document.head.appendChild(styleEl);
|
|
990
|
+
}
|
|
991
|
+
function releaseStyle() {
|
|
992
|
+
styleRefs = Math.max(0, styleRefs - 1);
|
|
993
|
+
if (styleRefs === 0) {
|
|
994
|
+
document.head.querySelector(`style[${STYLE_ATTR}]`)?.remove();
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function applyRegionCurtain(region, opts = {}) {
|
|
998
|
+
const timeoutMs = opts.timeoutMs ?? 3e3;
|
|
999
|
+
acquireStyle();
|
|
1000
|
+
region.classList.add(CLASS);
|
|
1001
|
+
let lifted = false;
|
|
1002
|
+
const lift = () => {
|
|
1003
|
+
if (lifted) return;
|
|
1004
|
+
lifted = true;
|
|
1005
|
+
clearTimeout(timeoutId);
|
|
1006
|
+
region.classList.remove(CLASS);
|
|
1007
|
+
releaseStyle();
|
|
1008
|
+
};
|
|
1009
|
+
const timeoutId = setTimeout(lift, timeoutMs);
|
|
1010
|
+
return lift;
|
|
1011
|
+
}
|
|
1012
|
+
function applyCurtainToTargets(targets, opts = {}) {
|
|
1013
|
+
const lifts = targets.map((t2) => applyRegionCurtain(t2, opts));
|
|
1014
|
+
return () => {
|
|
1015
|
+
for (const lift of lifts) lift();
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// src/takeover/ports.ts
|
|
1020
|
+
function createDomPorts(config, deps) {
|
|
1021
|
+
return {
|
|
1022
|
+
hasSignals: () => deps.signalCount > 0,
|
|
1023
|
+
findSection(anchor) {
|
|
1024
|
+
try {
|
|
1025
|
+
return document.querySelector(anchor);
|
|
1026
|
+
} catch {
|
|
1027
|
+
return null;
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
1030
|
+
measureSection: (el) => el.getBoundingClientRect().height,
|
|
1031
|
+
reserve(minHeightPx) {
|
|
1032
|
+
if (minHeightPx > 0) deps.root.style.minHeight = `${Math.round(minHeightPx)}px`;
|
|
1033
|
+
},
|
|
1034
|
+
releaseReservation() {
|
|
1035
|
+
deps.root.style.removeProperty("min-height");
|
|
1036
|
+
},
|
|
1037
|
+
region: () => deps.root.parentElement ?? deps.root,
|
|
1038
|
+
ownsNode: (el) => deps.root.contains(el),
|
|
1039
|
+
curtain() {
|
|
1040
|
+
const targets = [deps.root];
|
|
1041
|
+
for (const section of config.hostSections) {
|
|
1042
|
+
try {
|
|
1043
|
+
const el = document.querySelector(section.anchor);
|
|
1044
|
+
if (el) targets.push(el);
|
|
1045
|
+
} catch {
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
const failsafe = Math.max(3e3, (config.commitTimeoutMs ?? 500) + 1e3);
|
|
1049
|
+
return applyCurtainToTargets(targets, { timeoutMs: failsafe });
|
|
1050
|
+
},
|
|
1051
|
+
deactivateRoot() {
|
|
1052
|
+
deps.root.replaceChildren();
|
|
1053
|
+
deps.root.style.display = "none";
|
|
1054
|
+
},
|
|
1055
|
+
fetchPlan: deps.fetchPlan,
|
|
1056
|
+
renderModules: deps.renderModules,
|
|
1057
|
+
telemetry: deps.telemetry
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
function attachLifecycleDispose(result) {
|
|
1061
|
+
const onHide = () => result.dispose();
|
|
1062
|
+
window.addEventListener("pagehide", onHide, { once: true });
|
|
1063
|
+
return () => window.removeEventListener("pagehide", onHide);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
737
1066
|
// src/widgets/PdpSectionHeaderLit.ts
|
|
738
1067
|
import { html, LitElement, nothing } from "lit";
|
|
739
1068
|
function ensureSectionHeaderStyles() {
|
|
@@ -898,11 +1227,298 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-sec
|
|
|
898
1227
|
customElements.define("syntro-pdp-section-header", PdpSectionHeaderLit);
|
|
899
1228
|
}
|
|
900
1229
|
|
|
1230
|
+
// src/widgets/PdpSlotLit.ts
|
|
1231
|
+
import { html as html2, LitElement as LitElement2, nothing as nothing2 } from "lit";
|
|
1232
|
+
function ensureSlotStyles() {
|
|
1233
|
+
if (typeof document === "undefined") return;
|
|
1234
|
+
if (document.getElementById("syntro-pdp-slot-style")) return;
|
|
1235
|
+
const s4 = document.createElement("style");
|
|
1236
|
+
s4.id = "syntro-pdp-slot-style";
|
|
1237
|
+
s4.textContent = SLOT_CSS;
|
|
1238
|
+
document.head.appendChild(s4);
|
|
1239
|
+
}
|
|
1240
|
+
var SLOT_CSS = `
|
|
1241
|
+
.syntro-pdp-slot {
|
|
1242
|
+
--sl-primary: var(--syntro-pdp-primary, hsl(150 32% 38%));
|
|
1243
|
+
--sl-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
1244
|
+
--sl-muted: var(--syntro-pdp-muted, hsl(30 8% 55%));
|
|
1245
|
+
--sl-border: var(--syntro-pdp-border, hsl(30 6% 18%));
|
|
1246
|
+
--sl-bg-a: var(--syntro-pdp-card-bg, hsl(25 10% 11%));
|
|
1247
|
+
--sl-bg-b: var(--syntro-pdp-card-bg-b, hsl(25 10% 9%));
|
|
1248
|
+
--sl-display: var(--syntro-pdp-display, inherit);
|
|
1249
|
+
|
|
1250
|
+
position: relative;
|
|
1251
|
+
display: grid;
|
|
1252
|
+
grid-template-areas: 'stack';
|
|
1253
|
+
font-family: var(--syntro-pdp-sans, inherit);
|
|
1254
|
+
}
|
|
1255
|
+
.syntro-pdp-slot > .syntro-pdp-slot-mount,
|
|
1256
|
+
.syntro-pdp-slot > .syntro-pdp-slot-pending,
|
|
1257
|
+
.syntro-pdp-slot > .syntro-pdp-slot-failed {
|
|
1258
|
+
grid-area: stack;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
.syntro-pdp-slot-mount {
|
|
1262
|
+
min-height: 220px;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
/* When mutation arrived, hide the pending layer */
|
|
1266
|
+
.syntro-pdp-slot[data-state='mounted'] .syntro-pdp-slot-pending,
|
|
1267
|
+
.syntro-pdp-slot[data-state='failed'] .syntro-pdp-slot-pending {
|
|
1268
|
+
display: none;
|
|
1269
|
+
}
|
|
1270
|
+
.syntro-pdp-slot[data-state='mounted'] .syntro-pdp-slot-failed,
|
|
1271
|
+
.syntro-pdp-slot[data-state='pending'] .syntro-pdp-slot-failed {
|
|
1272
|
+
display: none;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/* Pending \u2014 a quietly-animated waiting state. */
|
|
1276
|
+
.syntro-pdp-slot-pending {
|
|
1277
|
+
position: relative;
|
|
1278
|
+
border: 1px dashed var(--sl-border);
|
|
1279
|
+
border-radius: 22px;
|
|
1280
|
+
min-height: 220px;
|
|
1281
|
+
padding: 36px 28px;
|
|
1282
|
+
background: linear-gradient(180deg, var(--sl-bg-a), var(--sl-bg-b));
|
|
1283
|
+
overflow: hidden;
|
|
1284
|
+
display: flex;
|
|
1285
|
+
align-items: center;
|
|
1286
|
+
justify-content: flex-start;
|
|
1287
|
+
}
|
|
1288
|
+
.syntro-pdp-slot-pending-shimmer {
|
|
1289
|
+
position: absolute;
|
|
1290
|
+
inset: 0;
|
|
1291
|
+
background: linear-gradient(
|
|
1292
|
+
90deg,
|
|
1293
|
+
transparent,
|
|
1294
|
+
color-mix(in srgb, var(--sl-primary) 7%, transparent),
|
|
1295
|
+
transparent
|
|
1296
|
+
);
|
|
1297
|
+
transform: translateX(-100%);
|
|
1298
|
+
animation: syntro-pdp-slot-sweep 1800ms ease-in-out infinite;
|
|
1299
|
+
}
|
|
1300
|
+
.syntro-pdp-slot-pending-row {
|
|
1301
|
+
position: relative;
|
|
1302
|
+
z-index: 1;
|
|
1303
|
+
display: flex;
|
|
1304
|
+
align-items: center;
|
|
1305
|
+
gap: 12px;
|
|
1306
|
+
}
|
|
1307
|
+
.syntro-pdp-slot-pending-dot {
|
|
1308
|
+
flex: 0 0 auto;
|
|
1309
|
+
width: 8px;
|
|
1310
|
+
height: 8px;
|
|
1311
|
+
border-radius: 50%;
|
|
1312
|
+
background: var(--sl-primary);
|
|
1313
|
+
box-shadow: 0 0 0 6px color-mix(in srgb, var(--sl-primary) 14%, transparent);
|
|
1314
|
+
animation: syntro-pdp-slot-pulse 1600ms ease-in-out infinite;
|
|
1315
|
+
}
|
|
1316
|
+
.syntro-pdp-slot-pending-label {
|
|
1317
|
+
font-family: var(--sl-display);
|
|
1318
|
+
font-style: italic;
|
|
1319
|
+
font-size: 18px;
|
|
1320
|
+
font-variation-settings: 'SOFT' 70;
|
|
1321
|
+
color: var(--sl-muted);
|
|
1322
|
+
margin: 0;
|
|
1323
|
+
line-height: 1.4;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/* Failed \u2014 the planner or sub-agent failed; the host page still works. */
|
|
1327
|
+
.syntro-pdp-slot-failed {
|
|
1328
|
+
border: 1px solid var(--sl-border);
|
|
1329
|
+
border-radius: 22px;
|
|
1330
|
+
min-height: 180px;
|
|
1331
|
+
padding: 24px 28px;
|
|
1332
|
+
background: linear-gradient(180deg, var(--sl-bg-a), var(--sl-bg-b));
|
|
1333
|
+
display: flex;
|
|
1334
|
+
flex-direction: column;
|
|
1335
|
+
justify-content: center;
|
|
1336
|
+
gap: 6px;
|
|
1337
|
+
}
|
|
1338
|
+
.syntro-pdp-slot-failed-title {
|
|
1339
|
+
font-family: var(--sl-display);
|
|
1340
|
+
font-style: italic;
|
|
1341
|
+
font-size: 17px;
|
|
1342
|
+
color: var(--sl-fg);
|
|
1343
|
+
margin: 0;
|
|
1344
|
+
font-variation-settings: 'SOFT' 70;
|
|
1345
|
+
}
|
|
1346
|
+
.syntro-pdp-slot-failed-body {
|
|
1347
|
+
color: var(--sl-muted);
|
|
1348
|
+
font-size: 13.5px;
|
|
1349
|
+
margin: 0;
|
|
1350
|
+
line-height: 1.5;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
@media (max-width: 640px) {
|
|
1354
|
+
.syntro-pdp-slot-pending,
|
|
1355
|
+
.syntro-pdp-slot-failed {
|
|
1356
|
+
border-radius: 18px;
|
|
1357
|
+
padding: 24px 18px;
|
|
1358
|
+
min-height: 180px;
|
|
1359
|
+
}
|
|
1360
|
+
.syntro-pdp-slot-pending-label {
|
|
1361
|
+
font-size: 15.5px;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
@keyframes syntro-pdp-slot-sweep {
|
|
1366
|
+
0% { transform: translateX(-100%); }
|
|
1367
|
+
100% { transform: translateX(100%); }
|
|
1368
|
+
}
|
|
1369
|
+
@keyframes syntro-pdp-slot-pulse {
|
|
1370
|
+
0%, 100% {
|
|
1371
|
+
opacity: 0.6;
|
|
1372
|
+
box-shadow: 0 0 0 6px color-mix(in srgb, var(--sl-primary) 14%, transparent);
|
|
1373
|
+
}
|
|
1374
|
+
50% {
|
|
1375
|
+
opacity: 1;
|
|
1376
|
+
box-shadow: 0 0 0 8px color-mix(in srgb, var(--sl-primary) 26%, transparent);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
`;
|
|
1380
|
+
var PENDING_COPY = {
|
|
1381
|
+
"trending-news": "Composing this section\u2026",
|
|
1382
|
+
chart: "Drawing the comparison\u2026",
|
|
1383
|
+
qa: "Picking the questions worth answering\u2026",
|
|
1384
|
+
regional: "Locating you in time and place\u2026",
|
|
1385
|
+
"peer-feed": "Finding people like you\u2026"
|
|
1386
|
+
};
|
|
1387
|
+
var PENDING_FALLBACK = "Composing\u2026";
|
|
1388
|
+
var PdpSlotLit = class extends LitElement2 {
|
|
1389
|
+
constructor() {
|
|
1390
|
+
super(...arguments);
|
|
1391
|
+
/** DOM id the host's mutation runtime targets when appending the widget. */
|
|
1392
|
+
this.slotId = "";
|
|
1393
|
+
this.kind = "";
|
|
1394
|
+
this.state = "pending";
|
|
1395
|
+
}
|
|
1396
|
+
createRenderRoot() {
|
|
1397
|
+
return this;
|
|
1398
|
+
}
|
|
1399
|
+
connectedCallback() {
|
|
1400
|
+
super.connectedCallback();
|
|
1401
|
+
ensureSlotStyles();
|
|
1402
|
+
}
|
|
1403
|
+
willUpdate() {
|
|
1404
|
+
this.dataset.state = this.state;
|
|
1405
|
+
}
|
|
1406
|
+
render() {
|
|
1407
|
+
const pendingLabel = PENDING_COPY[this.kind] ?? PENDING_FALLBACK;
|
|
1408
|
+
return html2`
|
|
1409
|
+
<div class="syntro-pdp-slot" data-state=${this.state} data-kind=${this.kind}>
|
|
1410
|
+
<div
|
|
1411
|
+
class="syntro-pdp-slot-mount"
|
|
1412
|
+
id=${this.slotId || nothing2}
|
|
1413
|
+
data-pdp-slot=${this.kind}
|
|
1414
|
+
></div>
|
|
1415
|
+
<div class="syntro-pdp-slot-pending" aria-hidden=${this.state !== "pending"}>
|
|
1416
|
+
<div class="syntro-pdp-slot-pending-shimmer" aria-hidden="true"></div>
|
|
1417
|
+
<div class="syntro-pdp-slot-pending-row">
|
|
1418
|
+
<span class="syntro-pdp-slot-pending-dot" aria-hidden="true"></span>
|
|
1419
|
+
<p class="syntro-pdp-slot-pending-label">${pendingLabel}</p>
|
|
1420
|
+
</div>
|
|
1421
|
+
</div>
|
|
1422
|
+
<div class="syntro-pdp-slot-failed" aria-hidden=${this.state !== "failed"}>
|
|
1423
|
+
<p class="syntro-pdp-slot-failed-title">
|
|
1424
|
+
Personalized view couldn't load.
|
|
1425
|
+
</p>
|
|
1426
|
+
<p class="syntro-pdp-slot-failed-body">
|
|
1427
|
+
The standard page still has everything you need.
|
|
1428
|
+
</p>
|
|
1429
|
+
</div>
|
|
1430
|
+
</div>
|
|
1431
|
+
`;
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
PdpSlotLit.properties = {
|
|
1435
|
+
slotId: { type: String, attribute: "slot-id" },
|
|
1436
|
+
kind: { type: String },
|
|
1437
|
+
state: { type: String, reflect: true }
|
|
1438
|
+
};
|
|
1439
|
+
if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-slot")) {
|
|
1440
|
+
customElements.define("syntro-pdp-slot", PdpSlotLit);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// src/takeover/renderModules.ts
|
|
1444
|
+
function parsePlanEntry(entry) {
|
|
1445
|
+
const i2 = entry.indexOf(":");
|
|
1446
|
+
if (i2 === -1) return { kind: entry, hostSectionId: null };
|
|
1447
|
+
return { kind: entry.slice(0, i2), hostSectionId: entry.slice(i2 + 1) || null };
|
|
1448
|
+
}
|
|
1449
|
+
function createTakeoverModuleRenderer(deps) {
|
|
1450
|
+
return async (plan, onModuleFailed) => {
|
|
1451
|
+
const decisionArchetype = deps.getDecisionArchetype();
|
|
1452
|
+
const row = deps.getPresetRow(decisionArchetype) ?? {};
|
|
1453
|
+
const bakedWork = [];
|
|
1454
|
+
plan.order.forEach((entry, index) => {
|
|
1455
|
+
const { kind, hostSectionId } = parsePlanEntry(entry);
|
|
1456
|
+
const slotId = `pdp-slot-${kind}`;
|
|
1457
|
+
const preset = row[slotId];
|
|
1458
|
+
const tag = deps.resolveTag(kind, preset?.variant ?? null);
|
|
1459
|
+
if (!tag) {
|
|
1460
|
+
deps.telemetry("takeover.module_kind_unknown", { entry, kind });
|
|
1461
|
+
onModuleFailed(hostSectionId);
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
const envelope = buildPdpPlan([{ slot: slotId, preset }]);
|
|
1465
|
+
const task = envelope.tasks[0];
|
|
1466
|
+
const section = document.createElement("section");
|
|
1467
|
+
section.toggleAttribute("data-syntro-pdp-section", true);
|
|
1468
|
+
section.setAttribute("data-slot", slotId);
|
|
1469
|
+
section.setAttribute("data-syntro-takeover-entry", entry);
|
|
1470
|
+
const header = document.createElement("syntro-pdp-section-header");
|
|
1471
|
+
header.eyebrow = task.header.eyebrow ?? "";
|
|
1472
|
+
header.title = task.header.title ?? "";
|
|
1473
|
+
header.accent = task.header.accent ?? "";
|
|
1474
|
+
header.subtitle = task.header.subtitle ?? "";
|
|
1475
|
+
const inner = document.createElement("div");
|
|
1476
|
+
inner.toggleAttribute("data-syntro-pdp-section-inner", true);
|
|
1477
|
+
const slotEl = document.createElement("syntro-pdp-slot");
|
|
1478
|
+
slotEl.slotId = `syntro-takeover-${index}-${slotId}`;
|
|
1479
|
+
slotEl.kind = kind;
|
|
1480
|
+
slotEl.state = "pending";
|
|
1481
|
+
inner.appendChild(slotEl);
|
|
1482
|
+
section.append(header, inner);
|
|
1483
|
+
deps.root.appendChild(section);
|
|
1484
|
+
const fail = () => {
|
|
1485
|
+
if (hostSectionId) {
|
|
1486
|
+
section.remove();
|
|
1487
|
+
} else {
|
|
1488
|
+
slotEl.state = "failed";
|
|
1489
|
+
}
|
|
1490
|
+
deps.telemetry("takeover.module_failed", { entry, kind });
|
|
1491
|
+
onModuleFailed(hostSectionId);
|
|
1492
|
+
};
|
|
1493
|
+
const mountPayload = async (payload) => {
|
|
1494
|
+
await slotEl.updateComplete;
|
|
1495
|
+
const mount = slotEl.querySelector(".syntro-pdp-slot-mount");
|
|
1496
|
+
if (!mount) {
|
|
1497
|
+
fail();
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
mount.replaceChildren();
|
|
1501
|
+
const widget = document.createElement(tag);
|
|
1502
|
+
widget.data = payload;
|
|
1503
|
+
mount.appendChild(widget);
|
|
1504
|
+
slotEl.state = "mounted";
|
|
1505
|
+
};
|
|
1506
|
+
if (preset) {
|
|
1507
|
+
const data = selectPresetData(preset.kind, preset.data, decisionArchetype);
|
|
1508
|
+
bakedWork.push(mountPayload(data).catch(fail));
|
|
1509
|
+
} else {
|
|
1510
|
+
void deps.requestLiveModule(kind, decisionArchetype).then((payload) => payload ? mountPayload(payload) : fail()).catch(fail);
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
await Promise.all(bakedWork);
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
|
|
901
1517
|
// src/widgets/PdpOtherProductsCarouselLit.ts
|
|
902
|
-
import { html as
|
|
1518
|
+
import { html as html4, LitElement as LitElement4 } from "lit";
|
|
903
1519
|
|
|
904
1520
|
// src/widgets/OtherProductTileLit.ts
|
|
905
|
-
import { html as
|
|
1521
|
+
import { html as html3, LitElement as LitElement3 } from "lit";
|
|
906
1522
|
var RELATIONSHIP_LABELS = {
|
|
907
1523
|
discovery: "Recommended for you",
|
|
908
1524
|
complement: "Pairs with this",
|
|
@@ -1045,7 +1661,7 @@ var OTHER_PRODUCT_TILE_CSS = `
|
|
|
1045
1661
|
background: color-mix(in srgb, var(--opt-primary) 10%, transparent);
|
|
1046
1662
|
}
|
|
1047
1663
|
`;
|
|
1048
|
-
var OtherProductTileLit = class extends
|
|
1664
|
+
var OtherProductTileLit = class extends LitElement3 {
|
|
1049
1665
|
constructor() {
|
|
1050
1666
|
super(...arguments);
|
|
1051
1667
|
this.tile = null;
|
|
@@ -1060,24 +1676,24 @@ var OtherProductTileLit = class extends LitElement2 {
|
|
|
1060
1676
|
render() {
|
|
1061
1677
|
const t2 = this.tile;
|
|
1062
1678
|
if (!t2) {
|
|
1063
|
-
return
|
|
1679
|
+
return html3``;
|
|
1064
1680
|
}
|
|
1065
1681
|
const relationshipLabel = RELATIONSHIP_LABELS[t2.relationship];
|
|
1066
1682
|
const price = t2.price_cents == null ? "" : `$${(t2.price_cents / 100).toFixed(0)}`;
|
|
1067
|
-
return
|
|
1683
|
+
return html3`
|
|
1068
1684
|
<article class="opt-card" data-relationship="${t2.relationship}">
|
|
1069
|
-
${t2.image_url ?
|
|
1685
|
+
${t2.image_url ? html3`<div class="opt-image">
|
|
1070
1686
|
<img src="${t2.image_url}" alt="${t2.name}" loading="lazy" />
|
|
1071
1687
|
</div>` : null}
|
|
1072
1688
|
<div class="opt-relationship" data-relationship="${t2.relationship}">
|
|
1073
|
-
${relationshipLabel ?
|
|
1689
|
+
${relationshipLabel ? html3`${relationshipLabel}` : null}
|
|
1074
1690
|
<span class="opt-relationship-type">${t2.relationship}</span>
|
|
1075
1691
|
</div>
|
|
1076
1692
|
<h3 class="opt-name">${t2.name}</h3>
|
|
1077
|
-
${t2.tagline ?
|
|
1078
|
-
${t2.why_blurb ?
|
|
1079
|
-
${t2.evidence_chips.length ?
|
|
1080
|
-
${t2.evidence_chips.map((c2) =>
|
|
1693
|
+
${t2.tagline ? html3`<p class="opt-tagline">${t2.tagline}</p>` : null}
|
|
1694
|
+
${t2.why_blurb ? html3`<p class="opt-why">${t2.why_blurb}</p>` : null}
|
|
1695
|
+
${t2.evidence_chips.length ? html3`<div class="opt-chips">
|
|
1696
|
+
${t2.evidence_chips.map((c2) => html3`<span class="opt-chip">${c2.display}</span>`)}
|
|
1081
1697
|
</div>` : null}
|
|
1082
1698
|
<div class="opt-footer">
|
|
1083
1699
|
<span class="opt-price">${price}</span>
|
|
@@ -1160,7 +1776,7 @@ syntro-pdp-other-products-carousel {
|
|
|
1160
1776
|
}
|
|
1161
1777
|
}
|
|
1162
1778
|
`;
|
|
1163
|
-
var PdpOtherProductsCarouselLit = class extends
|
|
1779
|
+
var PdpOtherProductsCarouselLit = class extends LitElement4 {
|
|
1164
1780
|
constructor() {
|
|
1165
1781
|
super(...arguments);
|
|
1166
1782
|
this.data = null;
|
|
@@ -1173,12 +1789,12 @@ var PdpOtherProductsCarouselLit = class extends LitElement3 {
|
|
|
1173
1789
|
ensureCarouselStyles();
|
|
1174
1790
|
}
|
|
1175
1791
|
render() {
|
|
1176
|
-
if (!this.data) return
|
|
1177
|
-
return
|
|
1792
|
+
if (!this.data) return html4``;
|
|
1793
|
+
return html4`
|
|
1178
1794
|
<div class="opc-carousel">
|
|
1179
1795
|
<div class="carousel" role="list">
|
|
1180
1796
|
${this.data.tiles.map(
|
|
1181
|
-
(tile) =>
|
|
1797
|
+
(tile) => html4`
|
|
1182
1798
|
<div class="opc-item" role="listitem">
|
|
1183
1799
|
<syntro-pdp-other-product-tile .tile=${tile}></syntro-pdp-other-product-tile>
|
|
1184
1800
|
</div>
|
|
@@ -1197,7 +1813,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-oth
|
|
|
1197
1813
|
}
|
|
1198
1814
|
|
|
1199
1815
|
// src/widgets/PdpOtherProductsGridLit.ts
|
|
1200
|
-
import { html as
|
|
1816
|
+
import { html as html5, LitElement as LitElement5 } from "lit";
|
|
1201
1817
|
function ensureGridStyles() {
|
|
1202
1818
|
if (typeof document === "undefined") return;
|
|
1203
1819
|
if (document.getElementById("syntro-pdp-other-products-grid-style")) return;
|
|
@@ -1226,7 +1842,7 @@ syntro-pdp-other-products-grid {
|
|
|
1226
1842
|
}
|
|
1227
1843
|
}
|
|
1228
1844
|
`;
|
|
1229
|
-
var PdpOtherProductsGridLit = class extends
|
|
1845
|
+
var PdpOtherProductsGridLit = class extends LitElement5 {
|
|
1230
1846
|
constructor() {
|
|
1231
1847
|
super(...arguments);
|
|
1232
1848
|
this.data = null;
|
|
@@ -1239,11 +1855,11 @@ var PdpOtherProductsGridLit = class extends LitElement4 {
|
|
|
1239
1855
|
ensureGridStyles();
|
|
1240
1856
|
}
|
|
1241
1857
|
render() {
|
|
1242
|
-
if (!this.data) return
|
|
1243
|
-
return
|
|
1858
|
+
if (!this.data) return html5``;
|
|
1859
|
+
return html5`
|
|
1244
1860
|
<div class="grid" role="list">
|
|
1245
1861
|
${this.data.tiles.map(
|
|
1246
|
-
(tile) =>
|
|
1862
|
+
(tile) => html5`
|
|
1247
1863
|
<syntro-pdp-other-product-tile role="listitem" .tile=${tile}></syntro-pdp-other-product-tile>
|
|
1248
1864
|
`
|
|
1249
1865
|
)}
|
|
@@ -1750,6 +2366,9 @@ function gatherVisitorSignals() {
|
|
|
1750
2366
|
extras: gatherExtras(runtime2, context)
|
|
1751
2367
|
};
|
|
1752
2368
|
}
|
|
2369
|
+
function countTakeoverSignals(signals) {
|
|
2370
|
+
return _countSignals(signals.session_metrics) + (signals.chat_excerpt ? 1 : 0);
|
|
2371
|
+
}
|
|
1753
2372
|
var TEMPLATE_TO_TAG = {
|
|
1754
2373
|
// trending-news (TrendingNewsVariant)
|
|
1755
2374
|
"trending-news": "syntro-pdp-trending-news",
|
|
@@ -1794,12 +2413,21 @@ function resolveTagForMount(inst) {
|
|
|
1794
2413
|
}
|
|
1795
2414
|
return null;
|
|
1796
2415
|
}
|
|
1797
|
-
var _SyntroPdpLit = class _SyntroPdpLit extends
|
|
2416
|
+
var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
1798
2417
|
constructor() {
|
|
1799
2418
|
super(...arguments);
|
|
1800
2419
|
this.productId = "";
|
|
1801
2420
|
this.productName = "";
|
|
1802
2421
|
this.productBlurb = "";
|
|
2422
|
+
/**
|
|
2423
|
+
* Page-takeover anatomy (FEAT-1785977524), normally delivered through the
|
|
2424
|
+
* widget `props` below. Presence at CONNECT time latches the element into
|
|
2425
|
+
* takeover mode: the engine owns the root and the six-slot compose path
|
|
2426
|
+
* never starts. Setting it after a legacy connect is ignored (warned) —
|
|
2427
|
+
* flipping modes on a live element could leave both paths' DOM behind.
|
|
2428
|
+
*/
|
|
2429
|
+
this.takeover = void 0;
|
|
2430
|
+
this._propsValue = void 0;
|
|
1803
2431
|
/**
|
|
1804
2432
|
* SDK runtime. Null until the mountable wires it in. We only render
|
|
1805
2433
|
* the personalized surface once runtimeRef is present AND the
|
|
@@ -1840,6 +2468,48 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1840
2468
|
*/
|
|
1841
2469
|
this._nativeSlots = /* @__PURE__ */ new Set();
|
|
1842
2470
|
this._nativeHandles = /* @__PURE__ */ new Map();
|
|
2471
|
+
/**
|
|
2472
|
+
* Takeover mode latch — decided ONCE, at the element's FIRST connect, from
|
|
2473
|
+
* the `takeover` prop, and kept for the element's whole lifetime. While
|
|
2474
|
+
* true, Lit never renders into this element (see shouldUpdate): the engine
|
|
2475
|
+
* renders imperatively and its deactivateRoot() calls replaceChildren() on
|
|
2476
|
+
* this root, which would destroy Lit's child parts. The latch is one-way in
|
|
2477
|
+
* BOTH directions — an element that ever rendered legacy has live Lit parts
|
|
2478
|
+
* (engaging the engine would render next to them, then destroy them), and
|
|
2479
|
+
* an element the engine ever owned has no parts left for Lit to resume.
|
|
2480
|
+
*/
|
|
2481
|
+
this._takeoverMode = false;
|
|
2482
|
+
this._takeoverModeLatched = false;
|
|
2483
|
+
/** The single in-flight/settled takeover run for this connected lifetime. */
|
|
2484
|
+
this._takeoverRun = null;
|
|
2485
|
+
/**
|
|
2486
|
+
* Settles when the previous run's dispose has fully completed. A re-run
|
|
2487
|
+
* (reconnect, or a synchronous move between parents) chains on this so a
|
|
2488
|
+
* LATE dispose from the torn-down run can never deactivate the root the
|
|
2489
|
+
* new run is rendering into.
|
|
2490
|
+
*/
|
|
2491
|
+
this._takeoverDisposal = Promise.resolve();
|
|
2492
|
+
/** A previous run deactivated the root (display:none + emptied); a re-run
|
|
2493
|
+
* after reconnect must undo that before the engine renders again. */
|
|
2494
|
+
this._takeoverRanBefore = false;
|
|
2495
|
+
this._takeoverLateWarned = false;
|
|
2496
|
+
/**
|
|
2497
|
+
* bfcache re-entry (review F4). pagehide disposes the run
|
|
2498
|
+
* (attachLifecycleDispose) but a bfcache restore does NOT re-fire
|
|
2499
|
+
* connectedCallback — without this listener the restored page would sit on
|
|
2500
|
+
* the native fallback forever. `persisted` is true only for bfcache
|
|
2501
|
+
* restores; the normal-load pageshow is owned by the connect path. Arrow
|
|
2502
|
+
* function per Lit event-handler convention (stable identity for
|
|
2503
|
+
* add/removeEventListener).
|
|
2504
|
+
*/
|
|
2505
|
+
this._onTakeoverPageShow = (event) => {
|
|
2506
|
+
if (!event.persisted) return;
|
|
2507
|
+
if (!this._takeoverMode || !this.isConnected) return;
|
|
2508
|
+
const config = this.takeover;
|
|
2509
|
+
if (!config) return;
|
|
2510
|
+
this._teardownTakeover();
|
|
2511
|
+
this._startTakeover(config);
|
|
2512
|
+
};
|
|
1843
2513
|
/**
|
|
1844
2514
|
* The scored axes from the current config pass — the SAME values
|
|
1845
2515
|
* `_fireFromConfig` resolved (sport-archetype preset key + decision archetype).
|
|
@@ -1860,6 +2530,23 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1860
2530
|
this._dispatchDecision({ type: "dossier-toggled", open });
|
|
1861
2531
|
};
|
|
1862
2532
|
}
|
|
2533
|
+
/**
|
|
2534
|
+
* Widget props (the `adaptive-product:pdp` mountable sets `el.props` before
|
|
2535
|
+
* appending). Mapped IMMEDIATELY in the setter — not in willUpdate — so
|
|
2536
|
+
* `takeover` is already on the element when connectedCallback latches the
|
|
2537
|
+
* mode. Absent fields leave the element's current values untouched.
|
|
2538
|
+
*/
|
|
2539
|
+
get props() {
|
|
2540
|
+
return this._propsValue;
|
|
2541
|
+
}
|
|
2542
|
+
set props(value) {
|
|
2543
|
+
this._propsValue = value;
|
|
2544
|
+
if (!value) return;
|
|
2545
|
+
if (value.productId) this.productId = value.productId;
|
|
2546
|
+
if (value.productName) this.productName = value.productName;
|
|
2547
|
+
if (value.productBlurb) this.productBlurb = value.productBlurb;
|
|
2548
|
+
if (value.takeover) this.takeover = value.takeover;
|
|
2549
|
+
}
|
|
1863
2550
|
_dispatchDecision(event) {
|
|
1864
2551
|
const transition = transitionPdp(this._decisionState, event);
|
|
1865
2552
|
this._decisionState = transition.state;
|
|
@@ -1869,6 +2556,25 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1869
2556
|
return this;
|
|
1870
2557
|
}
|
|
1871
2558
|
connectedCallback() {
|
|
2559
|
+
if (!this._takeoverModeLatched) {
|
|
2560
|
+
this._takeoverModeLatched = true;
|
|
2561
|
+
this._takeoverMode = Boolean(this.takeover);
|
|
2562
|
+
} else if (!this._takeoverMode && this.takeover && !this._takeoverLateWarned) {
|
|
2563
|
+
this._takeoverLateWarned = true;
|
|
2564
|
+
console.warn(
|
|
2565
|
+
"[syntro-pdp] takeover prop set on an element that already connected on the standard compose path \u2014 ignored (mode is latched per element). Mount a fresh element with `takeover` in its widget props."
|
|
2566
|
+
);
|
|
2567
|
+
}
|
|
2568
|
+
if (this._takeoverMode) {
|
|
2569
|
+
super.connectedCallback();
|
|
2570
|
+
ensureSectionLayoutStyles();
|
|
2571
|
+
if (typeof window !== "undefined") {
|
|
2572
|
+
window.removeEventListener("pageshow", this._onTakeoverPageShow);
|
|
2573
|
+
window.addEventListener("pageshow", this._onTakeoverPageShow);
|
|
2574
|
+
}
|
|
2575
|
+
if (this.takeover) this._startTakeover(this.takeover);
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
1872
2578
|
const reconnecting = this._decisionState.connection === "disconnected";
|
|
1873
2579
|
super.connectedCallback();
|
|
1874
2580
|
ensureSectionLayoutStyles();
|
|
@@ -1885,6 +2591,14 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1885
2591
|
}
|
|
1886
2592
|
}
|
|
1887
2593
|
disconnectedCallback() {
|
|
2594
|
+
if (this._takeoverMode) {
|
|
2595
|
+
super.disconnectedCallback();
|
|
2596
|
+
if (typeof window !== "undefined") {
|
|
2597
|
+
window.removeEventListener("pageshow", this._onTakeoverPageShow);
|
|
2598
|
+
}
|
|
2599
|
+
this._teardownTakeover();
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
1888
2602
|
super.disconnectedCallback();
|
|
1889
2603
|
this._navUnsub?.();
|
|
1890
2604
|
this._navUnsub = null;
|
|
@@ -1892,6 +2606,16 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1892
2606
|
this._nativeHandles.clear();
|
|
1893
2607
|
this._dispatchDecision({ type: "disconnected" });
|
|
1894
2608
|
}
|
|
2609
|
+
shouldUpdate(changed) {
|
|
2610
|
+
if (this._takeoverMode) return false;
|
|
2611
|
+
if (changed.has("takeover") && this.takeover && !this._takeoverLateWarned) {
|
|
2612
|
+
this._takeoverLateWarned = true;
|
|
2613
|
+
console.warn(
|
|
2614
|
+
"[syntro-pdp] takeover prop set after the element already connected on the standard compose path \u2014 ignored. Provide `takeover` in the widget props before the element connects (or re-mount it)."
|
|
2615
|
+
);
|
|
2616
|
+
}
|
|
2617
|
+
return true;
|
|
2618
|
+
}
|
|
1895
2619
|
willUpdate(changed) {
|
|
1896
2620
|
if (changed.has("runtimeRef")) {
|
|
1897
2621
|
this._syncDecisionRuntime(false);
|
|
@@ -2014,6 +2738,135 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2014
2738
|
_syncDecisionPortal() {
|
|
2015
2739
|
this._dispatchDecision({ type: "portal-updated", present: this._whyPortalEl() !== null });
|
|
2016
2740
|
}
|
|
2741
|
+
// ── Takeover wiring (FEAT-1785977524) ────────────────────────────────────
|
|
2742
|
+
/**
|
|
2743
|
+
* Run the takeover engine with real ports: this element as the root,
|
|
2744
|
+
* `SynOS.authedFetch` as the plan/module transport and the existing slot
|
|
2745
|
+
* machinery as the renderer. Activation is config delivery itself: the
|
|
2746
|
+
* served config either carries `takeover` or it doesn't. Runs at
|
|
2747
|
+
* most once per connected lifetime (`_takeoverRun` latch); disconnect tears
|
|
2748
|
+
* it down and a reconnect re-evaluates fresh.
|
|
2749
|
+
*/
|
|
2750
|
+
_startTakeover(config) {
|
|
2751
|
+
if (this._takeoverRun) return;
|
|
2752
|
+
if (!this.productId) {
|
|
2753
|
+
this._track("takeover.missing_product_id", {});
|
|
2754
|
+
console.warn(
|
|
2755
|
+
"[syntro-pdp] takeover requires a product id (widget props / product-id attr) \u2014 skipping takeover, native page untouched."
|
|
2756
|
+
);
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2759
|
+
const synos = _readSynOS();
|
|
2760
|
+
const authedFetch = synos?.authedFetch;
|
|
2761
|
+
const signals = { ...gatherVisitorSignals(), chat_excerpt: readChatExcerpt() };
|
|
2762
|
+
const decisionDescriptions = _readPdpDecisionArchetypes() ?? {};
|
|
2763
|
+
const telemetry2 = (event, props) => this._track(event, props);
|
|
2764
|
+
const scored = { decisionArchetype: "unknown" };
|
|
2765
|
+
const fetchPlan = authedFetch ? createTakeoverPlanFetcher({
|
|
2766
|
+
authedFetch,
|
|
2767
|
+
config,
|
|
2768
|
+
productId: this.productId,
|
|
2769
|
+
productName: this.productName,
|
|
2770
|
+
decisionArchetypes: decisionDescriptions,
|
|
2771
|
+
// No client-side summary exists on the takeover path (the server
|
|
2772
|
+
// scores); the field mirrors /api/pdp/module's for the scorer.
|
|
2773
|
+
behaviorSummary: "",
|
|
2774
|
+
signals
|
|
2775
|
+
}) : null;
|
|
2776
|
+
const run = {
|
|
2777
|
+
disposed: false,
|
|
2778
|
+
detach: null,
|
|
2779
|
+
result: Promise.resolve(null)
|
|
2780
|
+
};
|
|
2781
|
+
const renderModules = createTakeoverModuleRenderer({
|
|
2782
|
+
root: this,
|
|
2783
|
+
getPresetRow: (decisionArchetype) => {
|
|
2784
|
+
const table = _readPdpPresets()?.[this.productId];
|
|
2785
|
+
return table?.[decisionArchetype] ?? table?.unknown;
|
|
2786
|
+
},
|
|
2787
|
+
resolveTag: (kind, variant) => resolveTagForMount({
|
|
2788
|
+
slot: `pdp-slot-${kind}`,
|
|
2789
|
+
kind,
|
|
2790
|
+
variant,
|
|
2791
|
+
header: { eyebrow: "", title: "", accent: null, subtitle: null }
|
|
2792
|
+
}),
|
|
2793
|
+
requestLiveModule: async (kind, decisionArchetype) => {
|
|
2794
|
+
if (!authedFetch) return null;
|
|
2795
|
+
return composeModule(authedFetch, {
|
|
2796
|
+
productName: this.productName,
|
|
2797
|
+
// Guaranteed non-empty: _startTakeover gates on a missing product id.
|
|
2798
|
+
productId: this.productId,
|
|
2799
|
+
kind,
|
|
2800
|
+
variant: null,
|
|
2801
|
+
// The takeover round trip scores only the decision axis (the server
|
|
2802
|
+
// resolves the plan from it); the sport-archetype axis is unscored
|
|
2803
|
+
// on this path, so it is sent as an explicit 'unknown' — never a
|
|
2804
|
+
// silently-invented value.
|
|
2805
|
+
archetype: { slug: "unknown", description: "" },
|
|
2806
|
+
decisionArchetype: decisionArchetype === "unknown" ? null : {
|
|
2807
|
+
slug: decisionArchetype,
|
|
2808
|
+
description: decisionDescriptions[decisionArchetype] ?? ""
|
|
2809
|
+
},
|
|
2810
|
+
behaviorSummary: "",
|
|
2811
|
+
signals
|
|
2812
|
+
});
|
|
2813
|
+
},
|
|
2814
|
+
telemetry: telemetry2,
|
|
2815
|
+
getDecisionArchetype: () => scored.decisionArchetype
|
|
2816
|
+
});
|
|
2817
|
+
const ports = createDomPorts(config, {
|
|
2818
|
+
root: this,
|
|
2819
|
+
signalCount: countTakeoverSignals(signals),
|
|
2820
|
+
fetchPlan: fetchPlan ? async () => {
|
|
2821
|
+
const response = await fetchPlan();
|
|
2822
|
+
scored.decisionArchetype = response.decisionArchetype;
|
|
2823
|
+
return response;
|
|
2824
|
+
} : async () => {
|
|
2825
|
+
throw new Error("SynOS.authedFetch unavailable \u2014 cannot fetch takeover plan");
|
|
2826
|
+
},
|
|
2827
|
+
// Torn-down guard: the progressive path's dispose is a no-op (nothing
|
|
2828
|
+
// was detached), so its BACKGROUND render has no cancellation handle —
|
|
2829
|
+
// this gate is what stops a stale run from appending into a root a
|
|
2830
|
+
// newer run may own by then.
|
|
2831
|
+
renderModules: async (plan, onModuleFailed) => {
|
|
2832
|
+
if (run.disposed) return;
|
|
2833
|
+
return renderModules(plan, onModuleFailed);
|
|
2834
|
+
},
|
|
2835
|
+
telemetry: telemetry2
|
|
2836
|
+
});
|
|
2837
|
+
run.result = this._takeoverDisposal.then(() => {
|
|
2838
|
+
if (run.disposed) return null;
|
|
2839
|
+
if (this._takeoverRanBefore) {
|
|
2840
|
+
this.style.removeProperty("display");
|
|
2841
|
+
this.replaceChildren();
|
|
2842
|
+
}
|
|
2843
|
+
this._takeoverRanBefore = true;
|
|
2844
|
+
return runTakeover(config, ports, { commitTimeoutMs: config.commitTimeoutMs });
|
|
2845
|
+
}).then((result) => {
|
|
2846
|
+
if (!result) return null;
|
|
2847
|
+
telemetry2("takeover.outcome", { outcome: result.outcome });
|
|
2848
|
+
if (run.disposed) {
|
|
2849
|
+
result.dispose();
|
|
2850
|
+
return result;
|
|
2851
|
+
}
|
|
2852
|
+
run.detach = attachLifecycleDispose(result);
|
|
2853
|
+
return result;
|
|
2854
|
+
}).catch((err) => {
|
|
2855
|
+
telemetry2("takeover.run_failed", { error: String(err).slice(0, 200) });
|
|
2856
|
+
return null;
|
|
2857
|
+
});
|
|
2858
|
+
this._takeoverRun = run;
|
|
2859
|
+
}
|
|
2860
|
+
/** Dispose the current takeover run (idempotent). */
|
|
2861
|
+
_teardownTakeover() {
|
|
2862
|
+
const run = this._takeoverRun;
|
|
2863
|
+
if (!run) return;
|
|
2864
|
+
this._takeoverRun = null;
|
|
2865
|
+
run.disposed = true;
|
|
2866
|
+
run.detach?.();
|
|
2867
|
+
run.detach = null;
|
|
2868
|
+
this._takeoverDisposal = run.result.then((result) => result?.dispose());
|
|
2869
|
+
}
|
|
2017
2870
|
/**
|
|
2018
2871
|
* Emit a telemetry event via the runtime's track API.
|
|
2019
2872
|
* Wrapped in try/catch so a broken telemetry integration never crashes the
|
|
@@ -2377,7 +3230,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2377
3230
|
if (view.status === "hidden") return "";
|
|
2378
3231
|
if (view.status === "loading") return this._renderWhyBoxLoading();
|
|
2379
3232
|
const e2 = view.explanation;
|
|
2380
|
-
return
|
|
3233
|
+
return html6`
|
|
2381
3234
|
<style>
|
|
2382
3235
|
/* Token bridge: every color resolves from a --syntro-pdp-* var the
|
|
2383
3236
|
* host (vela pages + SPA) publishes, falling back to a NEUTRAL host
|
|
@@ -2464,29 +3317,29 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2464
3317
|
Shown in demo mode only — a peek under the hood at the live personalization.
|
|
2465
3318
|
</div>
|
|
2466
3319
|
<div data-syntro-pdp-why-archetype>
|
|
2467
|
-
Shopping as: <strong>${e2.archetypeLabel}</strong>${e2.archetypeDesc ?
|
|
3320
|
+
Shopping as: <strong>${e2.archetypeLabel}</strong>${e2.archetypeDesc ? html6` — ${e2.archetypeDesc}` : ""}
|
|
2468
3321
|
</div>
|
|
2469
|
-
${e2.decisionLabel ?
|
|
2470
|
-
What they're weighing: <strong>${e2.decisionLabel}</strong>${e2.decisionDesc ?
|
|
3322
|
+
${e2.decisionLabel ? html6`<div data-syntro-pdp-why-archetype>
|
|
3323
|
+
What they're weighing: <strong>${e2.decisionLabel}</strong>${e2.decisionDesc ? html6` — “${e2.decisionDesc}”` : ""}
|
|
2471
3324
|
</div>` : ""}
|
|
2472
|
-
${e2.summary ?
|
|
3325
|
+
${e2.summary ? html6`<div data-syntro-pdp-why-summary>"${e2.summary}"</div>` : ""}
|
|
2473
3326
|
<div data-syntro-pdp-why-metrics>
|
|
2474
3327
|
${e2.metrics.map(
|
|
2475
|
-
(m) =>
|
|
3328
|
+
(m) => html6`<div data-syntro-pdp-why-metric>
|
|
2476
3329
|
<span data-syntro-pdp-why-metric-value>${m.value}</span>
|
|
2477
3330
|
<span data-syntro-pdp-why-metric-label>${m.label}</span>
|
|
2478
3331
|
</div>`
|
|
2479
3332
|
)}
|
|
2480
3333
|
</div>
|
|
2481
|
-
${view.dossier.length ?
|
|
3334
|
+
${view.dossier.length ? html6`<details data-syntro-pdp-why-dossier ?open=${view.dossierOpen} @toggle=${this._onDossierToggle}>
|
|
2482
3335
|
<summary>What was measured</summary>
|
|
2483
3336
|
<div data-syntro-pdp-why-dossier-list>
|
|
2484
3337
|
${view.dossier.map(
|
|
2485
|
-
(m) =>
|
|
3338
|
+
(m) => html6`<div data-syntro-pdp-why-dossier-row>
|
|
2486
3339
|
<span data-syntro-pdp-why-dossier-value>${m.value}</span>
|
|
2487
3340
|
<div data-syntro-pdp-why-dossier-text>
|
|
2488
3341
|
<span data-syntro-pdp-why-dossier-label>${m.label}</span>
|
|
2489
|
-
${m.hint ?
|
|
3342
|
+
${m.hint ? html6`<span data-syntro-pdp-why-dossier-hint>${m.hint}</span>` : ""}
|
|
2490
3343
|
</div>
|
|
2491
3344
|
</div>`
|
|
2492
3345
|
)}
|
|
@@ -2503,7 +3356,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2503
3356
|
* so the swap to the filled box is a state change, not a first appearance.
|
|
2504
3357
|
*/
|
|
2505
3358
|
_renderWhyBoxLoading() {
|
|
2506
|
-
return
|
|
3359
|
+
return html6`
|
|
2507
3360
|
<style>
|
|
2508
3361
|
[data-syntro-pdp-why] {
|
|
2509
3362
|
margin: 0; padding: 4px 0;
|
|
@@ -2543,18 +3396,18 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2543
3396
|
`;
|
|
2544
3397
|
}
|
|
2545
3398
|
render() {
|
|
2546
|
-
if (this._pageUnresolvable) return
|
|
3399
|
+
if (this._pageUnresolvable) return html6``;
|
|
2547
3400
|
if (this._presetsSettled && this._decisionState.stream.status === "idle" && this._decisionState.plan.status === "empty" && !_readPdpPresets()?.[this.productId]) {
|
|
2548
|
-
return
|
|
3401
|
+
return html6``;
|
|
2549
3402
|
}
|
|
2550
3403
|
const skeletonStyles = _SyntroPdpLit._skeletonStyles;
|
|
2551
3404
|
const view = selectPdpView(this._decisionState);
|
|
2552
3405
|
if (view.layout === "skeleton") {
|
|
2553
|
-
return
|
|
3406
|
+
return html6`
|
|
2554
3407
|
${skeletonStyles}
|
|
2555
3408
|
<div data-syntro-pdp-root data-state="pending" data-syntro-pdp-skeleton>
|
|
2556
3409
|
${[0, 1, 2].map(
|
|
2557
|
-
(i2) =>
|
|
3410
|
+
(i2) => html6`
|
|
2558
3411
|
<section
|
|
2559
3412
|
data-syntro-pdp-skeleton-section
|
|
2560
3413
|
style="animation-delay: ${i2 * 0.15}s"
|
|
@@ -2571,7 +3424,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2571
3424
|
</div>
|
|
2572
3425
|
`;
|
|
2573
3426
|
}
|
|
2574
|
-
return
|
|
3427
|
+
return html6`
|
|
2575
3428
|
${skeletonStyles}
|
|
2576
3429
|
<div data-syntro-pdp-root data-state=${view.rootState}>
|
|
2577
3430
|
${view.whyBox.destination === "inline" ? this._renderWhyBox(view.whyBox) : ""}
|
|
@@ -2579,7 +3432,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2579
3432
|
if (!slot.visible) return "";
|
|
2580
3433
|
const task = slot.task;
|
|
2581
3434
|
if (this._nativeSlots.has(task.slot)) return "";
|
|
2582
|
-
return
|
|
3435
|
+
return html6`
|
|
2583
3436
|
<section data-syntro-pdp-section data-slot=${task.slot}>
|
|
2584
3437
|
<syntro-pdp-section-header
|
|
2585
3438
|
.eyebrow=${task.header.eyebrow}
|
|
@@ -2603,7 +3456,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2603
3456
|
* change) without ever touching the imperatively-managed
|
|
2604
3457
|
* slot subtree. See adversarial-review failure mode #2.
|
|
2605
3458
|
*/
|
|
2606
|
-
slot.showLoading ?
|
|
3459
|
+
slot.showLoading ? html6`<div data-syntro-pdp-slot-shimmer>
|
|
2607
3460
|
<div data-syntro-pdp-skeleton-body></div>
|
|
2608
3461
|
</div>` : ""}
|
|
2609
3462
|
</div>
|
|
@@ -2619,6 +3472,7 @@ _SyntroPdpLit.properties = {
|
|
|
2619
3472
|
productName: { type: String, attribute: "product-name" },
|
|
2620
3473
|
productBlurb: { type: String, attribute: "product-blurb" },
|
|
2621
3474
|
runtimeRef: { attribute: false },
|
|
3475
|
+
takeover: { attribute: false },
|
|
2622
3476
|
_decisionState: { state: true },
|
|
2623
3477
|
_pageUnresolvable: { state: true },
|
|
2624
3478
|
_presetsSettled: { state: true }
|
|
@@ -2630,7 +3484,7 @@ _SyntroPdpLit.properties = {
|
|
|
2630
3484
|
* (see `ensureSectionLayoutStyles`), not here, so they're emitted once
|
|
2631
3485
|
* per document rather than once per `<syntro-pdp>` instance.
|
|
2632
3486
|
*/
|
|
2633
|
-
_SyntroPdpLit._skeletonStyles =
|
|
3487
|
+
_SyntroPdpLit._skeletonStyles = html6`
|
|
2634
3488
|
<style>
|
|
2635
3489
|
[data-syntro-pdp-skeleton] {
|
|
2636
3490
|
display: flex;
|
|
@@ -3033,6 +3887,10 @@ async function flush() {
|
|
|
3033
3887
|
pending.clear();
|
|
3034
3888
|
return;
|
|
3035
3889
|
}
|
|
3890
|
+
if (typeof document !== "undefined" && document.querySelector("syntro-pdp")) {
|
|
3891
|
+
pending.clear();
|
|
3892
|
+
return;
|
|
3893
|
+
}
|
|
3036
3894
|
const ids = [...pending];
|
|
3037
3895
|
pending.clear();
|
|
3038
3896
|
for (const id of ids) await onProductAppeared(id);
|
|
@@ -3110,7 +3968,7 @@ function startNativeSlotWatcher() {
|
|
|
3110
3968
|
}
|
|
3111
3969
|
|
|
3112
3970
|
// src/widgets/ProductCardLit.ts
|
|
3113
|
-
import { html as
|
|
3971
|
+
import { html as html8, LitElement as LitElement8, nothing as nothing4 } from "lit";
|
|
3114
3972
|
import { styleMap } from "lit/directives/style-map.js";
|
|
3115
3973
|
|
|
3116
3974
|
// src/bind/dom-selector.ts
|
|
@@ -4138,7 +4996,7 @@ cancel_fn = function() {
|
|
|
4138
4996
|
};
|
|
4139
4997
|
|
|
4140
4998
|
// src/widgets/VariantPanelLit.ts
|
|
4141
|
-
import { html as
|
|
4999
|
+
import { html as html7, LitElement as LitElement7, nothing as nothing3 } from "lit";
|
|
4142
5000
|
var PANEL_CSS = `
|
|
4143
5001
|
syntro-variant-panel {
|
|
4144
5002
|
display: grid;
|
|
@@ -4370,7 +5228,7 @@ var PANEL_CSS = `
|
|
|
4370
5228
|
.svp-live { position: absolute; left: -9999px; top: 0; width: 1px; height: 1px; overflow: hidden; }
|
|
4371
5229
|
`;
|
|
4372
5230
|
var _VariantPanelLit_instances, healthProbe_fn;
|
|
4373
|
-
var _VariantPanelLit = class _VariantPanelLit extends
|
|
5231
|
+
var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
4374
5232
|
constructor() {
|
|
4375
5233
|
super();
|
|
4376
5234
|
__privateAdd(this, _VariantPanelLit_instances);
|
|
@@ -4472,7 +5330,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4472
5330
|
}
|
|
4473
5331
|
render() {
|
|
4474
5332
|
if (this.state === "error") {
|
|
4475
|
-
return
|
|
5333
|
+
return html7`
|
|
4476
5334
|
<div class="svp-error-block" data-error-block>
|
|
4477
5335
|
<div>Couldn't load options. ${this.error ?? ""}</div>
|
|
4478
5336
|
<button class="svp-retry-btn" data-retry-button @click=${this._onRetry}>Try again</button>
|
|
@@ -4480,7 +5338,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4480
5338
|
`;
|
|
4481
5339
|
}
|
|
4482
5340
|
if (this.state === "loading" || !this.payload) {
|
|
4483
|
-
return
|
|
5341
|
+
return html7`
|
|
4484
5342
|
<div data-loading-skeleton>
|
|
4485
5343
|
<div class="svp-skeleton"></div>
|
|
4486
5344
|
<div class="svp-skeleton"></div>
|
|
@@ -4493,26 +5351,26 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4493
5351
|
const compareAt = matching?.compare_at_price ?? null;
|
|
4494
5352
|
const singleVariant = this.payload.attributes.length === 0;
|
|
4495
5353
|
const heroSrc = this._heroSrc();
|
|
4496
|
-
const backBtn =
|
|
5354
|
+
const backBtn = html7`<button
|
|
4497
5355
|
class="svp-back-btn"
|
|
4498
5356
|
data-back-button
|
|
4499
5357
|
@click=${this._onBack}
|
|
4500
5358
|
aria-label="Back"
|
|
4501
5359
|
>‹ Back</button>`;
|
|
4502
|
-
return
|
|
5360
|
+
return html7`
|
|
4503
5361
|
<div class="svp-image-col" data-image-col>
|
|
4504
|
-
${heroSrc ?
|
|
5362
|
+
${heroSrc ? html7`<img
|
|
4505
5363
|
class="svp-image"
|
|
4506
5364
|
data-variant-image
|
|
4507
5365
|
src=${heroSrc}
|
|
4508
5366
|
alt=${this.productImage?.alt ?? ""}
|
|
4509
5367
|
loading="lazy"
|
|
4510
|
-
/>` :
|
|
5368
|
+
/>` : nothing3}
|
|
4511
5369
|
</div>
|
|
4512
5370
|
<div class="svp-body" data-panel-body>
|
|
4513
|
-
${this.productTitle ?
|
|
5371
|
+
${this.productTitle ? html7`<div class="svp-product-title" data-product-title data-critical-text>${this.productTitle}</div>` : nothing3}
|
|
4514
5372
|
<div class="svp-attributes-scroll" data-attributes-scroll data-clip-ok>
|
|
4515
|
-
${singleVariant ?
|
|
5373
|
+
${singleVariant ? html7`<div class="svp-single-variant-note" data-single-variant-note>
|
|
4516
5374
|
Just one option — ready to add.
|
|
4517
5375
|
</div>` : this.payload.attributes.map((attr) => this._renderAttributeRow(attr))}
|
|
4518
5376
|
</div>
|
|
@@ -4527,23 +5385,23 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4527
5385
|
${// Short label: "Add · $64" (the price commits HERE — the card's
|
|
4528
5386
|
// front face carries no price; round-3 feedback shortened this
|
|
4529
5387
|
// so Back fits beside it on the same row).
|
|
4530
|
-
ctaPrice != null ?
|
|
5388
|
+
ctaPrice != null ? html7`Add ${compareAt ? html7`<span class="svp-compare">$${compareAt.toFixed(2)}</span>` : nothing3}· $${ctaPrice.toFixed(2)}` : html7`Add to cart`}
|
|
4531
5389
|
</button>
|
|
4532
5390
|
${backBtn}
|
|
4533
5391
|
</div>
|
|
4534
5392
|
<div class="svp-live" data-live data-clip-ok aria-live="polite">
|
|
4535
|
-
${this.productTitle ? `Showing options for ${this.productTitle}` :
|
|
5393
|
+
${this.productTitle ? `Showing options for ${this.productTitle}` : nothing3}
|
|
4536
5394
|
</div>
|
|
4537
5395
|
</div>
|
|
4538
5396
|
`;
|
|
4539
5397
|
}
|
|
4540
5398
|
_renderAttributeRow(attr) {
|
|
4541
5399
|
const oosVisible = this._hasAnyOOSForAttribute(attr);
|
|
4542
|
-
return
|
|
5400
|
+
return html7`
|
|
4543
5401
|
<div class="svp-attribute-row" data-attribute-row data-attribute-key=${attr.key}>
|
|
4544
5402
|
<div class="svp-attribute-label" data-attribute-label id=${`attrlabel-${attr.key}`}>${attr.label}</div>
|
|
4545
5403
|
${attr.values.length >= _VariantPanelLit.COLLAPSE_MIN_OPTIONS ? this._renderCollapsedAxis(attr) : attr.kind === "swatch" ? this._renderSwatchRow(attr) : this._renderPillRow(attr)}
|
|
4546
|
-
${oosVisible ?
|
|
5404
|
+
${oosVisible ? html7`<div class="svp-oos-microcopy" data-out-of-stock-microcopy>Out of stock for one or more options</div>` : nothing3}
|
|
4547
5405
|
</div>
|
|
4548
5406
|
`;
|
|
4549
5407
|
}
|
|
@@ -4552,12 +5410,12 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4552
5410
|
}
|
|
4553
5411
|
_renderPillRow(attr) {
|
|
4554
5412
|
const segmented = attr.values.length <= _VariantPanelLit.SEGMENTED_MAX_OPTIONS;
|
|
4555
|
-
return
|
|
4556
|
-
<div class="svp-pill-row" data-segmented=${segmented ? "" :
|
|
5413
|
+
return html7`
|
|
5414
|
+
<div class="svp-pill-row" data-segmented=${segmented ? "" : nothing3} data-scroll=${this._isScrollTier(attr) ? "" : nothing3} role="radiogroup" aria-labelledby=${`attrlabel-${attr.key}`}>
|
|
4557
5415
|
${attr.values.map((v) => {
|
|
4558
5416
|
const disabled2 = this._isValueDisabled(attr.key, v.id);
|
|
4559
5417
|
const checked = this._selection[attr.key] === v.id;
|
|
4560
|
-
return
|
|
5418
|
+
return html7`<button
|
|
4561
5419
|
class="svp-pill"
|
|
4562
5420
|
data-pill
|
|
4563
5421
|
data-attribute-key=${attr.key}
|
|
@@ -4575,14 +5433,14 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4575
5433
|
_renderSwatchRow(attr) {
|
|
4576
5434
|
const selectedValueId = this._selection[attr.key];
|
|
4577
5435
|
const selectedLabel = attr.values.find((v) => v.id === selectedValueId)?.label ?? "";
|
|
4578
|
-
return
|
|
5436
|
+
return html7`
|
|
4579
5437
|
<div class="svp-swatch-label">${selectedLabel}</div>
|
|
4580
|
-
<div class="svp-swatch-row" data-scroll=${this._isScrollTier(attr) ? "" :
|
|
5438
|
+
<div class="svp-swatch-row" data-scroll=${this._isScrollTier(attr) ? "" : nothing3} role="radiogroup" aria-labelledby=${`attrlabel-${attr.key}`}>
|
|
4581
5439
|
${attr.values.map((v) => {
|
|
4582
5440
|
const disabled2 = this._isValueDisabled(attr.key, v.id);
|
|
4583
5441
|
const checked = this._selection[attr.key] === v.id;
|
|
4584
5442
|
const color = v.swatch?.kind === "color" ? v.swatch.value : "transparent";
|
|
4585
|
-
return
|
|
5443
|
+
return html7`<button
|
|
4586
5444
|
class="svp-swatch-pill"
|
|
4587
5445
|
data-pill
|
|
4588
5446
|
data-swatch-dot
|
|
@@ -4636,8 +5494,8 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4636
5494
|
const filter = (this._axisFilter[attr.key] ?? "").trim().toLowerCase();
|
|
4637
5495
|
const filtered = filter ? attr.values.filter((v) => v.label.toLowerCase().includes(filter)) : attr.values;
|
|
4638
5496
|
const showFilter = attr.values.length >= _VariantPanelLit.FILTER_MIN_OPTIONS;
|
|
4639
|
-
const dot = (v) => v?.swatch?.kind === "color" ?
|
|
4640
|
-
return
|
|
5497
|
+
const dot = (v) => v?.swatch?.kind === "color" ? html7`<span class="svp-swatch-dot" style="background-color: ${v.swatch.value}"></span>` : nothing3;
|
|
5498
|
+
return html7`
|
|
4641
5499
|
<div
|
|
4642
5500
|
class="svp-collapse"
|
|
4643
5501
|
data-collapsed-axis
|
|
@@ -4667,7 +5525,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4667
5525
|
<span class="svp-collapse-value">${selected ? selected.label : `Choose ${attr.label.toLowerCase()}`}</span>
|
|
4668
5526
|
<span class="svp-collapse-caret">${expanded ? "\u25B4" : "\u25BE"}</span>
|
|
4669
5527
|
</button>
|
|
4670
|
-
${expanded ?
|
|
5528
|
+
${expanded ? html7`
|
|
4671
5529
|
<div
|
|
4672
5530
|
class="svp-collapse-list"
|
|
4673
5531
|
data-clip-ok
|
|
@@ -4675,18 +5533,18 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4675
5533
|
role="listbox"
|
|
4676
5534
|
aria-labelledby=${`attrlabel-${attr.key}`}
|
|
4677
5535
|
>
|
|
4678
|
-
${showFilter ?
|
|
5536
|
+
${showFilter ? html7`<input
|
|
4679
5537
|
class="svp-collapse-filter"
|
|
4680
5538
|
data-collapse-filter
|
|
4681
5539
|
type="text"
|
|
4682
5540
|
placeholder=${`Filter ${attr.label.toLowerCase()}\u2026`}
|
|
4683
5541
|
.value=${this._axisFilter[attr.key] ?? ""}
|
|
4684
5542
|
@input=${(e2) => this._onAxisFilterInput(attr.key, e2)}
|
|
4685
|
-
/>` :
|
|
5543
|
+
/>` : nothing3}
|
|
4686
5544
|
${filtered.map((v) => {
|
|
4687
5545
|
const disabled2 = this._isValueDisabled(attr.key, v.id);
|
|
4688
5546
|
const isSelected = this._selection[attr.key] === v.id;
|
|
4689
|
-
return
|
|
5547
|
+
return html7`<button
|
|
4690
5548
|
class="svp-collapse-option"
|
|
4691
5549
|
data-collapse-option
|
|
4692
5550
|
data-value-id=${v.id}
|
|
@@ -4697,8 +5555,8 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement6 {
|
|
|
4697
5555
|
@click=${() => this._onCollapsedSelect(attr.key, v.id)}
|
|
4698
5556
|
>${dot(v)}<span class="svp-collapse-value">${v.label}</span></button>`;
|
|
4699
5557
|
})}
|
|
4700
|
-
${filtered.length === 0 ?
|
|
4701
|
-
</div>` :
|
|
5558
|
+
${filtered.length === 0 ? html7`<div class="svp-collapse-empty">No matches</div>` : nothing3}
|
|
5559
|
+
</div>` : nothing3}
|
|
4702
5560
|
</div>
|
|
4703
5561
|
`;
|
|
4704
5562
|
}
|
|
@@ -4823,7 +5681,7 @@ var CARD_FLIP_CSS = `
|
|
|
4823
5681
|
}
|
|
4824
5682
|
`;
|
|
4825
5683
|
var _effectExecutor, _controller, _unsubDevice, _lastFailureFingerprint, _reportedHealthStates, _healthRaf, _ProductCardLit_instances, scheduleHealthCheck_fn, measureAndReportHealth_fn, cleanupHostResources_fn, dispatchCard_fn, _onFlipAnimationEnd, startBind_fn, readProductObservation_fn, syncDecisionInputsBeforeIntent_fn;
|
|
4826
|
-
var ProductCardLit = class extends
|
|
5684
|
+
var ProductCardLit = class extends LitElement8 {
|
|
4827
5685
|
constructor() {
|
|
4828
5686
|
super(...arguments);
|
|
4829
5687
|
__privateAdd(this, _ProductCardLit_instances);
|
|
@@ -4925,9 +5783,9 @@ var ProductCardLit = class extends LitElement7 {
|
|
|
4925
5783
|
super.disconnectedCallback();
|
|
4926
5784
|
}
|
|
4927
5785
|
render() {
|
|
4928
|
-
if (this.validationError || !this.props) return
|
|
5786
|
+
if (this.validationError || !this.props) return html8``;
|
|
4929
5787
|
const parsed = cardSchema.safeParse(this.props);
|
|
4930
|
-
if (!parsed.success) return
|
|
5788
|
+
if (!parsed.success) return html8``;
|
|
4931
5789
|
const density = this._device === "mobile" ? "compact" : parsed.data.density;
|
|
4932
5790
|
const { product, visibleFacts } = parsed.data;
|
|
4933
5791
|
const resolved = this.resolved[product.id];
|
|
@@ -4954,7 +5812,7 @@ var ProductCardLit = class extends LitElement7 {
|
|
|
4954
5812
|
onPrimaryCtaClick,
|
|
4955
5813
|
!view.primaryCta.disabled
|
|
4956
5814
|
);
|
|
4957
|
-
const backFace =
|
|
5815
|
+
const backFace = html8`<syntro-variant-panel
|
|
4958
5816
|
.payload=${view.panel.payload}
|
|
4959
5817
|
.productTitle=${product.name ?? ""}
|
|
4960
5818
|
.productImage=${product.image ? { src: product.image.src, alt: product.image.alt } : null}
|
|
@@ -4965,27 +5823,27 @@ var ProductCardLit = class extends LitElement7 {
|
|
|
4965
5823
|
@variant-panel-retry=${this._onVariantRetry}
|
|
4966
5824
|
@variant-panel-selection-change=${this._onVariantSelectionChange}
|
|
4967
5825
|
></syntro-variant-panel>
|
|
4968
|
-
${view.addError ?
|
|
5826
|
+
${view.addError ? html8`<div data-add-to-cart-error>${view.addError}</div>` : nothing4}`;
|
|
4969
5827
|
const backView = view.face === "back" && view.flipAnimating ? (
|
|
4970
5828
|
// Mid-flip: both faces coexist inside a preserve-3d container. The
|
|
4971
5829
|
// front turns away (inert + aria-hidden) while the back turns in.
|
|
4972
|
-
|
|
5830
|
+
html8`<div data-card-flip @animationend=${__privateGet(this, _onFlipAnimationEnd)}>
|
|
4973
5831
|
<div data-face="front" aria-hidden="true" inert>${frontContent}</div>
|
|
4974
5832
|
<div data-face="back">${backFace}</div>
|
|
4975
5833
|
</div>`
|
|
4976
5834
|
) : view.face === "back" ? (
|
|
4977
5835
|
// Settled back: flat, untransformed — no flip container, no perspective.
|
|
4978
|
-
|
|
4979
|
-
) :
|
|
5836
|
+
html8`<div data-face="back">${backFace}</div>`
|
|
5837
|
+
) : html8`<div data-face="front">
|
|
4980
5838
|
${frontContent}
|
|
4981
|
-
${view.addError ?
|
|
5839
|
+
${view.addError ? html8`<div data-add-to-cart-error>${view.addError}</div>` : nothing4}
|
|
4982
5840
|
</div>`;
|
|
4983
|
-
return
|
|
5841
|
+
return html8`
|
|
4984
5842
|
<article
|
|
4985
5843
|
class="sc-product-card"
|
|
4986
5844
|
data-active-face=${view.face}
|
|
4987
5845
|
data-density=${density}
|
|
4988
|
-
data-labels=${product.labels ? JSON.stringify(product.labels) :
|
|
5846
|
+
data-labels=${product.labels ? JSON.stringify(product.labels) : nothing4}
|
|
4989
5847
|
style=${styleMap(articleStyles)}
|
|
4990
5848
|
>
|
|
4991
5849
|
${backView}
|
|
@@ -5515,7 +6373,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5515
6373
|
maxWidth: "100%",
|
|
5516
6374
|
flexShrink: "1"
|
|
5517
6375
|
};
|
|
5518
|
-
const ctaRow =
|
|
6376
|
+
const ctaRow = html8`<div class="sc-product-card__ctas" style=${styleMap(ctasStyles)}>
|
|
5519
6377
|
${ctas.map((c2, i2) => {
|
|
5520
6378
|
const disabled2 = c2.actionId === "add_to_cart" && !commerceAvailable;
|
|
5521
6379
|
const ctaStyles = {
|
|
@@ -5523,13 +6381,13 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5523
6381
|
...disabled2 ? { opacity: "0.5", "pointer-events": "none", cursor: "not-allowed" } : {}
|
|
5524
6382
|
};
|
|
5525
6383
|
const ctaLabel = c2.label;
|
|
5526
|
-
return
|
|
6384
|
+
return html8`<a
|
|
5527
6385
|
class="sc-product-card__cta"
|
|
5528
6386
|
data-product-cta=${i2 === 0 ? "primary" : "secondary"}
|
|
5529
6387
|
data-critical-text
|
|
5530
6388
|
data-variant=${c2.variant}
|
|
5531
|
-
data-disabled=${disabled2 ? "true" :
|
|
5532
|
-
aria-disabled=${disabled2 ? "true" :
|
|
6389
|
+
data-disabled=${disabled2 ? "true" : nothing4}
|
|
6390
|
+
aria-disabled=${disabled2 ? "true" : nothing4}
|
|
5533
6391
|
href=${c2.actionId === "add_to_cart" ? "#" : c2.href ?? "#"}
|
|
5534
6392
|
target=${c2.target}
|
|
5535
6393
|
rel=${c2.target === "_blank" ? "noopener noreferrer" : ""}
|
|
@@ -5546,7 +6404,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5546
6404
|
>`;
|
|
5547
6405
|
})}
|
|
5548
6406
|
</div>`;
|
|
5549
|
-
const ratingOverlay = overlay && product.rating && isVisible("rating") ?
|
|
6407
|
+
const ratingOverlay = overlay && product.rating && isVisible("rating") ? html8`<div
|
|
5550
6408
|
class="sc-product-card__rating sc-product-card__rating--overlay"
|
|
5551
6409
|
data-product-rating
|
|
5552
6410
|
data-rating-value=${product.rating.value}
|
|
@@ -5561,20 +6419,20 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5561
6419
|
style=${styleMap({ opacity: "0.8", marginLeft: "0.15rem", display: "var(--sc-card-rating-count-display, inline)" })}
|
|
5562
6420
|
>(${product.rating.count.toLocaleString()})</span
|
|
5563
6421
|
>
|
|
5564
|
-
</div>` :
|
|
5565
|
-
const priceOverlay = overlay && isVisible("price") && (product.price || product.salePrice) ?
|
|
6422
|
+
</div>` : nothing4;
|
|
6423
|
+
const priceOverlay = overlay && isVisible("price") && (product.price || product.salePrice) ? html8`<div
|
|
5566
6424
|
class="sc-product-card__price sc-product-card__price--overlay"
|
|
5567
6425
|
data-product-price
|
|
5568
6426
|
style=${styleMap(overlayPriceStyles)}
|
|
5569
6427
|
>
|
|
5570
6428
|
<span
|
|
5571
6429
|
class="sc-product-card__amount"
|
|
5572
|
-
data-product-price-sale=${hasSalePrice ? "true" :
|
|
6430
|
+
data-product-price-sale=${hasSalePrice ? "true" : nothing4}
|
|
5573
6431
|
data-critical-text
|
|
5574
6432
|
>${activePriceText}</span
|
|
5575
6433
|
>
|
|
5576
|
-
</div>` :
|
|
5577
|
-
const mediaBlock = overlay ?
|
|
6434
|
+
</div>` : nothing4;
|
|
6435
|
+
const mediaBlock = overlay ? html8`<div class="sc-product-card__media" style=${styleMap(mediaWrapperStyles)}>
|
|
5578
6436
|
<img
|
|
5579
6437
|
class="sc-product-card__image"
|
|
5580
6438
|
data-product-image
|
|
@@ -5585,7 +6443,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5585
6443
|
style=${styleMap(overlayImgStyles)}
|
|
5586
6444
|
/>
|
|
5587
6445
|
${ratingOverlay}${priceOverlay}
|
|
5588
|
-
</div>` :
|
|
6446
|
+
</div>` : html8`<img
|
|
5589
6447
|
class="sc-product-card__image"
|
|
5590
6448
|
data-product-image
|
|
5591
6449
|
src=${imgSrc}
|
|
@@ -5594,7 +6452,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5594
6452
|
@error=${onImageError}
|
|
5595
6453
|
style=${styleMap(imageStyles)}
|
|
5596
6454
|
/>`;
|
|
5597
|
-
return
|
|
6455
|
+
return html8`
|
|
5598
6456
|
<div class="sc-product-card__grid" data-product-card-grid style=${styleMap(rootGridStyles)}>
|
|
5599
6457
|
${mediaBlock}
|
|
5600
6458
|
<div class="sc-product-card__content" style=${styleMap(contentColStyles)}>
|
|
@@ -5658,7 +6516,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5658
6516
|
// the product page reachable.
|
|
5659
6517
|
(() => {
|
|
5660
6518
|
const navCta = ctas.find((c2) => c2.href);
|
|
5661
|
-
return navCta?.href ?
|
|
6519
|
+
return navCta?.href ? html8`<a
|
|
5662
6520
|
data-product-name-link
|
|
5663
6521
|
href=${navCta.href}
|
|
5664
6522
|
target=${navCta.target ?? "_self"}
|
|
@@ -5668,7 +6526,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5668
6526
|
})()}</h3>
|
|
5669
6527
|
${// On compact the rating renders as an image OVERLAY pill (item 2),
|
|
5670
6528
|
// so it is omitted from the header identity row here.
|
|
5671
|
-
!overlay && product.rating && isVisible("rating") ?
|
|
6529
|
+
!overlay && product.rating && isVisible("rating") ? html8`<div
|
|
5672
6530
|
class="sc-product-card__rating"
|
|
5673
6531
|
data-product-rating
|
|
5674
6532
|
data-rating-value=${product.rating.value}
|
|
@@ -5683,9 +6541,9 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5683
6541
|
<span class="sc-product-card__rating-stars" style="color: #f5a623;">★</span>
|
|
5684
6542
|
<span style=${styleMap({ marginLeft: "0.2rem", fontWeight: "600" })}>${product.rating.value}</span>
|
|
5685
6543
|
<span class="sc-product-card__rating-count" style=${styleMap({ opacity: "0.65", marginLeft: "0.2rem", display: "var(--sc-card-rating-count-display, inline)" })}>(${product.rating.count.toLocaleString()})</span>
|
|
5686
|
-
</div>` :
|
|
6544
|
+
</div>` : nothing4}
|
|
5687
6545
|
</div>
|
|
5688
|
-
${(product.tagline || product.framing) && isVisible("tagline") ?
|
|
6546
|
+
${(product.tagline || product.framing) && isVisible("tagline") ? html8`<p
|
|
5689
6547
|
class="sc-product-card__tagline"
|
|
5690
6548
|
data-product-tagline
|
|
5691
6549
|
style=${styleMap({
|
|
@@ -5719,7 +6577,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5719
6577
|
minWidth: "0",
|
|
5720
6578
|
overflowWrap: "anywhere"
|
|
5721
6579
|
})}
|
|
5722
|
-
>${product.tagline || product.framing}</p>` :
|
|
6580
|
+
>${product.tagline || product.framing}</p>` : nothing4}
|
|
5723
6581
|
</header>
|
|
5724
6582
|
<div
|
|
5725
6583
|
class="sc-product-card__body"
|
|
@@ -5733,36 +6591,36 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5733
6591
|
>
|
|
5734
6592
|
${factOrder.map((key) => {
|
|
5735
6593
|
if (key === "price") {
|
|
5736
|
-
if (overlay) return
|
|
5737
|
-
if (!isVisible("price") || !product.price && !product.salePrice) return
|
|
5738
|
-
return
|
|
6594
|
+
if (overlay) return nothing4;
|
|
6595
|
+
if (!isVisible("price") || !product.price && !product.salePrice) return nothing4;
|
|
6596
|
+
return html8`<div
|
|
5739
6597
|
class="sc-product-card__price"
|
|
5740
6598
|
data-product-price
|
|
5741
6599
|
style=${styleMap(priceBlockStyles)}
|
|
5742
6600
|
>
|
|
5743
|
-
${hasSalePrice && product.price ?
|
|
6601
|
+
${hasSalePrice && product.price ? html8`<span
|
|
5744
6602
|
class="sc-product-card__amount-original"
|
|
5745
6603
|
data-product-price-original
|
|
5746
6604
|
style=${styleMap(priceOriginalStyles)}
|
|
5747
6605
|
>${product.price.amount}</span
|
|
5748
|
-
>` :
|
|
6606
|
+
>` : nothing4}
|
|
5749
6607
|
<span
|
|
5750
6608
|
class="sc-product-card__amount"
|
|
5751
|
-
data-product-price-sale=${hasSalePrice ? "true" :
|
|
6609
|
+
data-product-price-sale=${hasSalePrice ? "true" : nothing4}
|
|
5752
6610
|
data-critical-text
|
|
5753
6611
|
style=${styleMap(priceSaleStyles)}
|
|
5754
6612
|
>${activePriceText}</span
|
|
5755
6613
|
>
|
|
5756
|
-
${activeCadence ?
|
|
6614
|
+
${activeCadence ? html8`<span
|
|
5757
6615
|
class="sc-product-card__cadence"
|
|
5758
6616
|
style=${styleMap(priceCadenceStyles)}
|
|
5759
6617
|
>${activeCadence}</span
|
|
5760
|
-
>` :
|
|
6618
|
+
>` : nothing4}
|
|
5761
6619
|
</div>`;
|
|
5762
6620
|
}
|
|
5763
6621
|
if (key === "availability") {
|
|
5764
|
-
if (!isVisible("availability") || !product.availability) return
|
|
5765
|
-
return
|
|
6622
|
+
if (!isVisible("availability") || !product.availability) return nothing4;
|
|
6623
|
+
return html8`<span
|
|
5766
6624
|
class="sc-product-card__availability"
|
|
5767
6625
|
data-product-availability
|
|
5768
6626
|
data-availability=${product.availability}
|
|
@@ -5771,8 +6629,8 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5771
6629
|
>`;
|
|
5772
6630
|
}
|
|
5773
6631
|
if (key === "badge") {
|
|
5774
|
-
if (!isVisible("badge") || !product.badge) return
|
|
5775
|
-
return
|
|
6632
|
+
if (!isVisible("badge") || !product.badge) return nothing4;
|
|
6633
|
+
return html8`<span
|
|
5776
6634
|
class="sc-product-card__badge"
|
|
5777
6635
|
data-product-badge
|
|
5778
6636
|
data-tone=${product.badge.tone}
|
|
@@ -5788,12 +6646,12 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5788
6646
|
>`;
|
|
5789
6647
|
}
|
|
5790
6648
|
if (key === "specs") {
|
|
5791
|
-
if (!isVisible("specs") || specGroups.length === 0) return
|
|
6649
|
+
if (!isVisible("specs") || specGroups.length === 0) return nothing4;
|
|
5792
6650
|
if (density === "compact") {
|
|
5793
6651
|
const allRows = specGroups.flatMap((g) => g.rows);
|
|
5794
6652
|
const highlights = allRows.slice(0, 4);
|
|
5795
|
-
if (highlights.length === 0) return
|
|
5796
|
-
return
|
|
6653
|
+
if (highlights.length === 0) return nothing4;
|
|
6654
|
+
return html8`<div
|
|
5797
6655
|
class="sc-product-card__specs"
|
|
5798
6656
|
data-density="compact"
|
|
5799
6657
|
data-clip-ok
|
|
@@ -5803,7 +6661,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5803
6661
|
style=${styleMap(specChipRowStyles)}
|
|
5804
6662
|
>
|
|
5805
6663
|
${highlights.map(
|
|
5806
|
-
(r) =>
|
|
6664
|
+
(r) => html8`<span
|
|
5807
6665
|
data-spec-chip
|
|
5808
6666
|
style=${styleMap(specChipStyles)}
|
|
5809
6667
|
><span style="font-weight: 600;">${r.name}</span
|
|
@@ -5812,9 +6670,9 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5812
6670
|
)}
|
|
5813
6671
|
</div>`;
|
|
5814
6672
|
}
|
|
5815
|
-
return
|
|
6673
|
+
return html8`<div class="sc-product-card__specs" style=${styleMap(specsContainerStyles)}>
|
|
5816
6674
|
${specGroups.map(
|
|
5817
|
-
(g) =>
|
|
6675
|
+
(g) => html8`<section
|
|
5818
6676
|
class="sc-product-card__spec-section"
|
|
5819
6677
|
data-spec-section=${g.section}
|
|
5820
6678
|
style=${styleMap(specGroupStyles)}
|
|
@@ -5832,7 +6690,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5832
6690
|
style=${styleMap(specRowsStyles)}
|
|
5833
6691
|
>
|
|
5834
6692
|
${g.rows.map(
|
|
5835
|
-
(r) =>
|
|
6693
|
+
(r) => html8`<li
|
|
5836
6694
|
data-spec-row
|
|
5837
6695
|
data-spec-chip
|
|
5838
6696
|
data-attribute-emphasis=${r.emphasis ? "true" : "false"}
|
|
@@ -5857,12 +6715,12 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5857
6715
|
style=${styleMap(specValueStyles)}
|
|
5858
6716
|
>${r.value}</span
|
|
5859
6717
|
>
|
|
5860
|
-
${r.unit ?
|
|
6718
|
+
${r.unit ? html8`<span
|
|
5861
6719
|
class="sc-product-card__spec-unit"
|
|
5862
6720
|
data-spec-unit
|
|
5863
6721
|
style="opacity: 0.72;"
|
|
5864
6722
|
>${r.unit}</span
|
|
5865
|
-
>` :
|
|
6723
|
+
>` : nothing4}
|
|
5866
6724
|
</li>`
|
|
5867
6725
|
)}
|
|
5868
6726
|
</ul>
|
|
@@ -5871,8 +6729,8 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5871
6729
|
</div>`;
|
|
5872
6730
|
}
|
|
5873
6731
|
if (key === "framing") {
|
|
5874
|
-
if (!isVisible("framing") || !showFraming) return
|
|
5875
|
-
return
|
|
6732
|
+
if (!isVisible("framing") || !showFraming) return nothing4;
|
|
6733
|
+
return html8`<p
|
|
5876
6734
|
class="sc-product-card__framing"
|
|
5877
6735
|
style=${styleMap({
|
|
5878
6736
|
margin: "0",
|
|
@@ -5885,7 +6743,7 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5885
6743
|
.innerHTML=${sanitizeHtml(product.framing ?? "")}
|
|
5886
6744
|
></p>`;
|
|
5887
6745
|
}
|
|
5888
|
-
return
|
|
6746
|
+
return nothing4;
|
|
5889
6747
|
})}
|
|
5890
6748
|
</div>
|
|
5891
6749
|
</div>
|
|
@@ -5895,11 +6753,11 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
5895
6753
|
}
|
|
5896
6754
|
function renderProductCard(product, density, resolved, visibleFacts) {
|
|
5897
6755
|
const articleStyles = _buildArticleStyles(density);
|
|
5898
|
-
return
|
|
6756
|
+
return html8`
|
|
5899
6757
|
<article
|
|
5900
6758
|
class="sc-product-card"
|
|
5901
6759
|
data-density=${density}
|
|
5902
|
-
data-labels=${product.labels ? JSON.stringify(product.labels) :
|
|
6760
|
+
data-labels=${product.labels ? JSON.stringify(product.labels) : nothing4}
|
|
5903
6761
|
style=${styleMap(articleStyles)}
|
|
5904
6762
|
>
|
|
5905
6763
|
${renderProductCardInner(product, density, resolved, visibleFacts, product.ctas, void 0)}
|
|
@@ -5913,9 +6771,9 @@ function onImageError(e2) {
|
|
|
5913
6771
|
}
|
|
5914
6772
|
|
|
5915
6773
|
// src/widgets/ProductComparisonLit.ts
|
|
5916
|
-
import { html as
|
|
6774
|
+
import { html as html9, LitElement as LitElement9, nothing as nothing5 } from "lit";
|
|
5917
6775
|
var _controller2, _parsed, _ProductComparisonLit_instances, healthProbe_fn2, _scrollListenerEl, _onBodyScroll, syncScrollCue_fn, startBind_fn2, renderDiveDeeper_fn, onDiveDeeper_fn, visibleRows_fn, rowComplete_fn, maxFields_fn, synthesizeRows_fn, renderMatrix_fn;
|
|
5918
|
-
var ProductComparisonLit = class extends
|
|
6776
|
+
var ProductComparisonLit = class extends LitElement9 {
|
|
5919
6777
|
constructor() {
|
|
5920
6778
|
super();
|
|
5921
6779
|
__privateAdd(this, _ProductComparisonLit_instances);
|
|
@@ -5957,7 +6815,7 @@ var ProductComparisonLit = class extends LitElement8 {
|
|
|
5957
6815
|
__privateGet(this, _controller2)?.destroy();
|
|
5958
6816
|
}
|
|
5959
6817
|
render() {
|
|
5960
|
-
if (this.validationError || !__privateGet(this, _parsed)) return
|
|
6818
|
+
if (this.validationError || !__privateGet(this, _parsed)) return html9``;
|
|
5961
6819
|
const parsed = __privateGet(this, _parsed);
|
|
5962
6820
|
const bodyStyle = (
|
|
5963
6821
|
// touch-action:pan-y is REQUIRED for the deck swipe-to-cycle to work over a
|
|
@@ -5971,7 +6829,7 @@ var ProductComparisonLit = class extends LitElement8 {
|
|
|
5971
6829
|
// rows and hands horizontal drags back to the deck's swipe handler.
|
|
5972
6830
|
"flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;touch-action:pan-y;scrollbar-width:none;-ms-overflow-style:none;overscroll-behavior:contain;" + (this.scrollFade ? "mask-image:linear-gradient(to bottom,#000 calc(100% - 18px),transparent);-webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 18px),transparent);" : "")
|
|
5973
6831
|
);
|
|
5974
|
-
return
|
|
6832
|
+
return html9`
|
|
5975
6833
|
<section
|
|
5976
6834
|
class="sc-product-comparison"
|
|
5977
6835
|
style="display:flex;flex-direction:column;flex:1 1 auto;padding:2px var(--sc-comparison-inset,12px) var(--sc-comparison-inset,12px);box-sizing:border-box;max-width:100%;min-width:0;min-height:0;max-height:100%;overflow:hidden;"
|
|
@@ -6006,11 +6864,11 @@ var ProductComparisonLit = class extends LitElement8 {
|
|
|
6006
6864
|
}
|
|
6007
6865
|
}
|
|
6008
6866
|
</style>
|
|
6009
|
-
${parsed.heading ?
|
|
6867
|
+
${parsed.heading ? html9`<h2
|
|
6010
6868
|
class="sc-product-comparison__heading"
|
|
6011
6869
|
data-comparison-heading
|
|
6012
6870
|
style="flex:0 0 auto;margin:0 0 4px;font:600 14px/1.3 var(--sc-font-family,inherit);color:var(--sc-tile-title-color,inherit);min-width:0;overflow-wrap:break-word;"
|
|
6013
|
-
>${parsed.heading}</h2>` :
|
|
6871
|
+
>${parsed.heading}</h2>` : nothing5}
|
|
6014
6872
|
<div
|
|
6015
6873
|
class="sc-product-comparison__scroll-wrap"
|
|
6016
6874
|
style="position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-height:0;"
|
|
@@ -6031,7 +6889,7 @@ var ProductComparisonLit = class extends LitElement8 {
|
|
|
6031
6889
|
// it's in-theme; pointer-events:none so it never blocks scroll/taps;
|
|
6032
6890
|
// gated on scrollFade (content below the current scroll) so it hides
|
|
6033
6891
|
// at the end. Bounce is dropped under prefers-reduced-motion (style).
|
|
6034
|
-
this.scrollFade ?
|
|
6892
|
+
this.scrollFade ? html9`<span class="sc-product-comparison__scroll-cue" aria-hidden="true"></span>` : nothing5}
|
|
6035
6893
|
</div>
|
|
6036
6894
|
${__privateMethod(this, _ProductComparisonLit_instances, renderDiveDeeper_fn).call(this, parsed)}
|
|
6037
6895
|
</section>
|
|
@@ -6116,9 +6974,9 @@ startBind_fn2 = function() {
|
|
|
6116
6974
|
* chat-bar mountable turns it into a user turn.
|
|
6117
6975
|
*/
|
|
6118
6976
|
renderDiveDeeper_fn = function(parsed) {
|
|
6119
|
-
if (parsed.products.length < 2) return
|
|
6977
|
+
if (parsed.products.length < 2) return nothing5;
|
|
6120
6978
|
const chipStyle = "flex:0 0 auto;align-self:flex-end;margin-top:6px;padding:4px 10px;border-radius:9999px;border:1px solid var(--sc-color-border,rgba(0,0,0,0.14));background:transparent;cursor:pointer;font:600 12px/1.2 var(--sc-font-family,inherit);color:var(--sc-color-primary,#3d8a5e);white-space:nowrap;";
|
|
6121
|
-
return
|
|
6979
|
+
return html9`<button
|
|
6122
6980
|
type="button"
|
|
6123
6981
|
class="sc-product-comparison__dive-deeper"
|
|
6124
6982
|
data-dive-deeper
|
|
@@ -6197,7 +7055,7 @@ renderMatrix_fn = function(products, rows) {
|
|
|
6197
7055
|
const colHeadStyle = "display:block;padding:6px 3px;text-align:left;font-weight:600;min-width:0;";
|
|
6198
7056
|
const labelStyle = "padding:8px 4px 8px 2px;font-weight:600;font-size:0.75rem;opacity:0.75;border-top:1px solid var(--sc-tile-border-color,rgba(0,0,0,0.08));align-self:stretch;min-width:0;overflow-wrap:break-word;hyphens:auto;";
|
|
6199
7057
|
const cellStyle = "padding:8px 6px;border-top:1px solid var(--sc-tile-border-color,rgba(0,0,0,0.08));align-self:stretch;min-width:0;overflow-wrap:anywhere;";
|
|
6200
|
-
return
|
|
7058
|
+
return html9`
|
|
6201
7059
|
<div
|
|
6202
7060
|
class="sc-product-comparison__matrix"
|
|
6203
7061
|
data-comparison-matrix
|
|
@@ -6231,7 +7089,7 @@ renderMatrix_fn = function(products, rows) {
|
|
|
6231
7089
|
const showRatingPill = !compactHeaders && p.rating;
|
|
6232
7090
|
const nameOverlayStyle = compactHeaders ? "display:block;font-weight:600;font-size:0.66rem;line-height:1.2;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.55);white-space:normal;overflow-wrap:break-word;min-width:0;" : "display:block;font-weight:600;font-size:0.76rem;line-height:1.25;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.55);white-space:normal;overflow-wrap:break-word;min-width:0;";
|
|
6233
7091
|
const mediaStyle = "position:relative;display:block;width:100%;aspect-ratio:var(--sc-comparison-image-aspect, 19 / 10);border-radius:10px;overflow:hidden;background:#e5e7eb;text-decoration:none;";
|
|
6234
|
-
const cardInner =
|
|
7092
|
+
const cardInner = html9`
|
|
6235
7093
|
<img
|
|
6236
7094
|
class="sc-product-comparison__thumb"
|
|
6237
7095
|
src=${imgSrc}
|
|
@@ -6240,33 +7098,33 @@ renderMatrix_fn = function(products, rows) {
|
|
|
6240
7098
|
style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;"
|
|
6241
7099
|
/>
|
|
6242
7100
|
<span aria-hidden="true" style="position:absolute;inset:auto 0 0 0;height:62%;background:linear-gradient(transparent,rgba(0,0,0,.62));"></span>
|
|
6243
|
-
${showRatingPill && p.rating ?
|
|
7101
|
+
${showRatingPill && p.rating ? html9`<span
|
|
6244
7102
|
class="sc-product-comparison__rating-pill"
|
|
6245
7103
|
data-product-rating
|
|
6246
7104
|
data-rating-value=${p.rating.value}
|
|
6247
7105
|
aria-label=${`${p.rating.value} out of ${p.rating.max ?? 5}, ${p.rating.count} reviews`}
|
|
6248
7106
|
style=${`${pillBase}left:4px;`}
|
|
6249
7107
|
><span style="color:#f5a623;">★</span><span style="margin-left:0.12rem;">${p.rating.value}</span></span
|
|
6250
|
-
>` :
|
|
6251
|
-
${priceText ?
|
|
7108
|
+
>` : nothing5}
|
|
7109
|
+
${priceText ? html9`<span
|
|
6252
7110
|
class="sc-product-comparison__price"
|
|
6253
7111
|
data-critical-text
|
|
6254
7112
|
style=${`${pillBase}right:4px;`}
|
|
6255
7113
|
>${priceText}</span
|
|
6256
|
-
>` :
|
|
7114
|
+
>` : nothing5}
|
|
6257
7115
|
<span
|
|
6258
7116
|
class="sc-product-comparison__name"
|
|
6259
7117
|
data-critical-text
|
|
6260
7118
|
style=${`position:absolute;left:8px;right:8px;bottom:6px;${nameOverlayStyle}`}
|
|
6261
7119
|
>${p.name}</span
|
|
6262
7120
|
>`;
|
|
6263
|
-
return
|
|
7121
|
+
return html9`<div
|
|
6264
7122
|
class="sc-product-comparison__matrix-col"
|
|
6265
7123
|
role="columnheader"
|
|
6266
7124
|
data-product-id=${p.id}
|
|
6267
7125
|
style=${colHeadStyle}
|
|
6268
7126
|
>
|
|
6269
|
-
${pdpHref ?
|
|
7127
|
+
${pdpHref ? html9`<a
|
|
6270
7128
|
class="sc-product-comparison__media"
|
|
6271
7129
|
data-comparison-media
|
|
6272
7130
|
data-product-cta="primary"
|
|
@@ -6277,19 +7135,19 @@ renderMatrix_fn = function(products, rows) {
|
|
|
6277
7135
|
data-action-id=${navCta?.actionId ?? ""}
|
|
6278
7136
|
data-variant=${navCta?.variant ?? ""}
|
|
6279
7137
|
style=${mediaStyle}
|
|
6280
|
-
>${cardInner}</a>` :
|
|
7138
|
+
>${cardInner}</a>` : html9`<div class="sc-product-comparison__media" data-comparison-media style=${mediaStyle}>${cardInner}</div>`}
|
|
6281
7139
|
</div>`;
|
|
6282
7140
|
})}
|
|
6283
7141
|
</div>
|
|
6284
7142
|
${rows.map((row) => {
|
|
6285
7143
|
const cells = [row.valueA, row.valueB, row.valueC, row.valueD];
|
|
6286
|
-
return
|
|
7144
|
+
return html9`
|
|
6287
7145
|
<div role="row" style="display:contents;">
|
|
6288
7146
|
<span class="sc-product-comparison__matrix-label" role="rowheader" style=${labelStyle}
|
|
6289
7147
|
>${row.label}</span
|
|
6290
7148
|
>
|
|
6291
7149
|
${products.map(
|
|
6292
|
-
(_p, i2) =>
|
|
7150
|
+
(_p, i2) => html9`<span class="sc-product-comparison__matrix-cell" role="cell" style=${cellStyle}
|
|
6293
7151
|
>${cells[i2] ?? "\u2014"}</span
|
|
6294
7152
|
>`
|
|
6295
7153
|
)}
|
|
@@ -6307,9 +7165,9 @@ ProductComparisonLit.properties = {
|
|
|
6307
7165
|
};
|
|
6308
7166
|
|
|
6309
7167
|
// src/widgets/ProductGridLit.ts
|
|
6310
|
-
import { html as
|
|
7168
|
+
import { html as html10, LitElement as LitElement10, nothing as nothing6 } from "lit";
|
|
6311
7169
|
var _controller3, _ProductGridLit_instances, startBind_fn3;
|
|
6312
|
-
var ProductGridLit = class extends
|
|
7170
|
+
var ProductGridLit = class extends LitElement10 {
|
|
6313
7171
|
constructor() {
|
|
6314
7172
|
super(...arguments);
|
|
6315
7173
|
__privateAdd(this, _ProductGridLit_instances);
|
|
@@ -6335,20 +7193,20 @@ var ProductGridLit = class extends LitElement9 {
|
|
|
6335
7193
|
__privateGet(this, _controller3)?.destroy();
|
|
6336
7194
|
}
|
|
6337
7195
|
render() {
|
|
6338
|
-
if (this.validationError || !this.props) return
|
|
7196
|
+
if (this.validationError || !this.props) return html10``;
|
|
6339
7197
|
const parsed = gridSchema.safeParse(this.props);
|
|
6340
|
-
if (!parsed.success) return
|
|
7198
|
+
if (!parsed.success) return html10``;
|
|
6341
7199
|
const { products, desktopColumns, heading } = parsed.data;
|
|
6342
|
-
return
|
|
7200
|
+
return html10`
|
|
6343
7201
|
<section class="sc-product-grid">
|
|
6344
|
-
${heading ?
|
|
7202
|
+
${heading ? html10`<h2 data-grid-heading class="sc-product-grid__heading">${heading}</h2>` : nothing6}
|
|
6345
7203
|
<div
|
|
6346
7204
|
data-grid-root
|
|
6347
7205
|
class="sc-product-grid__cells"
|
|
6348
7206
|
style=${`--sc-product-grid-cols:${desktopColumns}`}
|
|
6349
7207
|
>
|
|
6350
7208
|
${products.map(
|
|
6351
|
-
(p) =>
|
|
7209
|
+
(p) => html10`<div data-grid-cell class="sc-product-grid__cell">
|
|
6352
7210
|
${renderProductCard(p, "standard", this.resolved[p.id])}
|
|
6353
7211
|
</div>`
|
|
6354
7212
|
)}
|
|
@@ -6389,9 +7247,9 @@ ProductGridLit.properties = {
|
|
|
6389
7247
|
};
|
|
6390
7248
|
|
|
6391
7249
|
// src/widgets/ProductHeroLit.ts
|
|
6392
|
-
import { html as
|
|
7250
|
+
import { html as html11, LitElement as LitElement11, nothing as nothing7 } from "lit";
|
|
6393
7251
|
var _controller4, _ProductHeroLit_instances, startBind_fn4;
|
|
6394
|
-
var ProductHeroLit = class extends
|
|
7252
|
+
var ProductHeroLit = class extends LitElement11 {
|
|
6395
7253
|
constructor() {
|
|
6396
7254
|
super(...arguments);
|
|
6397
7255
|
__privateAdd(this, _ProductHeroLit_instances);
|
|
@@ -6417,9 +7275,9 @@ var ProductHeroLit = class extends LitElement10 {
|
|
|
6417
7275
|
__privateGet(this, _controller4)?.destroy();
|
|
6418
7276
|
}
|
|
6419
7277
|
render() {
|
|
6420
|
-
if (this.validationError || !this.props) return
|
|
7278
|
+
if (this.validationError || !this.props) return html11``;
|
|
6421
7279
|
const parsed = heroSchema.safeParse(this.props);
|
|
6422
|
-
if (!parsed.success) return
|
|
7280
|
+
if (!parsed.success) return html11``;
|
|
6423
7281
|
const { product, layout, longCopy } = parsed.data;
|
|
6424
7282
|
const resolved = this.resolved[product.id];
|
|
6425
7283
|
const imgSrc = resolved?.image ?? product.image.src;
|
|
@@ -6428,7 +7286,7 @@ var ProductHeroLit = class extends LitElement10 {
|
|
|
6428
7286
|
const sectionStyles = isTopLayout ? "display:block;" : `display:flex;flex-direction:${layout === "image-right" ? "row-reverse" : "row"};gap:1rem;align-items:flex-start;`;
|
|
6429
7287
|
const imageStyles = isTopLayout ? "width:100%;max-height:160px;object-fit:cover;border-radius:8px;display:block;margin-bottom:0.75rem;" : "width:128px;height:128px;object-fit:cover;border-radius:8px;flex-shrink:0;";
|
|
6430
7288
|
const contentStyles = isTopLayout ? "" : "flex:1;min-width:0;";
|
|
6431
|
-
return
|
|
7289
|
+
return html11`
|
|
6432
7290
|
<section class="sc-product-hero" data-hero-layout=${layout} style=${sectionStyles}>
|
|
6433
7291
|
<img
|
|
6434
7292
|
class="sc-product-hero__image"
|
|
@@ -6439,18 +7297,18 @@ var ProductHeroLit = class extends LitElement10 {
|
|
|
6439
7297
|
/>
|
|
6440
7298
|
<div class="sc-product-hero__content" style=${contentStyles}>
|
|
6441
7299
|
<h2 class="sc-product-hero__name">${product.name}</h2>
|
|
6442
|
-
${product.tagline ?
|
|
6443
|
-
${product.price ?
|
|
6444
|
-
${priceText}${product.price.cadence ?
|
|
6445
|
-
</p>` :
|
|
6446
|
-
${longCopy ?
|
|
7300
|
+
${product.tagline ? html11`<p class="sc-product-hero__tagline">${product.tagline}</p>` : nothing7}
|
|
7301
|
+
${product.price ? html11`<p class="sc-product-hero__price">
|
|
7302
|
+
${priceText}${product.price.cadence ? html11` <small>${product.price.cadence}</small>` : nothing7}
|
|
7303
|
+
</p>` : nothing7}
|
|
7304
|
+
${longCopy ? html11`<div
|
|
6447
7305
|
data-hero-longcopy
|
|
6448
7306
|
class="sc-product-hero__longcopy"
|
|
6449
7307
|
.innerHTML=${sanitizeHtml(longCopy)}
|
|
6450
|
-
></div>` :
|
|
7308
|
+
></div>` : nothing7}
|
|
6451
7309
|
<div class="sc-product-hero__ctas">
|
|
6452
7310
|
${product.ctas.map(
|
|
6453
|
-
(c2, i2) =>
|
|
7311
|
+
(c2, i2) => html11`<a
|
|
6454
7312
|
class="sc-product-hero__cta"
|
|
6455
7313
|
data-product-cta=${i2 === 0 ? "primary" : "secondary"}
|
|
6456
7314
|
data-variant=${c2.variant}
|
|
@@ -6498,10 +7356,10 @@ ProductHeroLit.properties = {
|
|
|
6498
7356
|
};
|
|
6499
7357
|
|
|
6500
7358
|
// src/widgets/ProductRecoCarouselLit.ts
|
|
6501
|
-
import { html as
|
|
7359
|
+
import { html as html12, LitElement as LitElement12, nothing as nothing8 } from "lit";
|
|
6502
7360
|
import { styleMap as styleMap2 } from "lit/directives/style-map.js";
|
|
6503
7361
|
var _ProductRecoCarouselLit_instances, healthProbe_fn3;
|
|
6504
|
-
var ProductRecoCarouselLit = class extends
|
|
7362
|
+
var ProductRecoCarouselLit = class extends LitElement12 {
|
|
6505
7363
|
constructor() {
|
|
6506
7364
|
super();
|
|
6507
7365
|
__privateAdd(this, _ProductRecoCarouselLit_instances);
|
|
@@ -6520,20 +7378,20 @@ var ProductRecoCarouselLit = class extends LitElement11 {
|
|
|
6520
7378
|
}
|
|
6521
7379
|
render() {
|
|
6522
7380
|
const items = this._parsed?.items ?? [];
|
|
6523
|
-
if (items.length === 0) return
|
|
6524
|
-
return
|
|
7381
|
+
if (items.length === 0) return html12``;
|
|
7382
|
+
return html12`
|
|
6525
7383
|
<section class="sc-reco" style="box-sizing:border-box;display:flex;flex-direction:column;height:100%;padding:2px var(--sc-reco-inset,12px) var(--sc-reco-inset,12px);">
|
|
6526
7384
|
<div class="sc-reco__carousel" role="list" style="display:flex;align-items:stretch;gap:8px;flex:1 1 auto;min-height:0;overflow-x:auto;scroll-snap-type:x mandatory;">
|
|
6527
7385
|
${items.map(
|
|
6528
|
-
(it) =>
|
|
7386
|
+
(it) => html12`
|
|
6529
7387
|
<div role="listitem" style="display:contents;">
|
|
6530
7388
|
<a data-reco-item data-product-cta="primary" href=${it.pdp_href}
|
|
6531
7389
|
style=${styleMap2({ position: "relative", flex: "0 0 auto", aspectRatio: "4 / 5", minHeight: "var(--sc-reco-item-min-h, 162px)", maxHeight: "var(--sc-reco-item-max-h, 340px)", borderRadius: "12px", overflow: "hidden", scrollSnapAlign: "start", textDecoration: "none", display: "block", background: "#e5e7eb" })}>
|
|
6532
|
-
${it.image_url ?
|
|
7390
|
+
${it.image_url ? html12`<img src=${it.image_url} alt=${it.hook ?? "Recommended product"} loading="lazy" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;">` : nothing8}
|
|
6533
7391
|
<span style="position:absolute;inset:auto 0 0 0;height:60%;background:linear-gradient(transparent,rgba(0,0,0,.62));"></span>
|
|
6534
7392
|
<span style="position:absolute;left:8px;right:8px;bottom:8px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.5);">
|
|
6535
|
-
${it.price ?
|
|
6536
|
-
${it.hook ?
|
|
7393
|
+
${it.price ? html12`<span style="display:block;font-weight:700;font-size:0.9rem;">${it.price}</span>` : nothing8}
|
|
7394
|
+
${it.hook ? html12`<span style="display:block;font-size:0.72rem;line-height:1.2;opacity:.95;">${it.hook}</span>` : nothing8}
|
|
6537
7395
|
</span>
|
|
6538
7396
|
</a>
|
|
6539
7397
|
</div>`
|
|
@@ -6568,7 +7426,7 @@ if (!customElements.get("syntro-product-reco-carousel"))
|
|
|
6568
7426
|
customElements.define("syntro-product-reco-carousel", ProductRecoCarouselLit);
|
|
6569
7427
|
|
|
6570
7428
|
// src/widgets/PdpTrendingNewsLit.ts
|
|
6571
|
-
import { html as
|
|
7429
|
+
import { html as html13, LitElement as LitElement13 } from "lit";
|
|
6572
7430
|
|
|
6573
7431
|
// src/widgets/entrance.ts
|
|
6574
7432
|
function armEntranceSettle(onSettle, container, settleMs, fastPath = false) {
|
|
@@ -6915,7 +7773,7 @@ var TRENDING_NEWS_CSS = `
|
|
|
6915
7773
|
}
|
|
6916
7774
|
}
|
|
6917
7775
|
`;
|
|
6918
|
-
var PdpTrendingNewsLit = class extends
|
|
7776
|
+
var PdpTrendingNewsLit = class extends LitElement13 {
|
|
6919
7777
|
constructor() {
|
|
6920
7778
|
super(...arguments);
|
|
6921
7779
|
this.data = void 0;
|
|
@@ -6951,7 +7809,7 @@ var PdpTrendingNewsLit = class extends LitElement12 {
|
|
|
6951
7809
|
}
|
|
6952
7810
|
render() {
|
|
6953
7811
|
if (!this.data) {
|
|
6954
|
-
return
|
|
7812
|
+
return html13`<div data-skeleton class="pdp-trending-news-skeleton">
|
|
6955
7813
|
<div class="pdp-skeleton-bar" style="width:60%"></div>
|
|
6956
7814
|
<div class="pdp-skeleton-bar" style="width:90%"></div>
|
|
6957
7815
|
<div class="pdp-skeleton-bar" style="width:80%"></div>
|
|
@@ -6961,7 +7819,7 @@ var PdpTrendingNewsLit = class extends LitElement12 {
|
|
|
6961
7819
|
const items = this.data.items;
|
|
6962
7820
|
const featured = items[0];
|
|
6963
7821
|
const rest = items.slice(1);
|
|
6964
|
-
return
|
|
7822
|
+
return html13`
|
|
6965
7823
|
<section
|
|
6966
7824
|
class="${this._entering ? "pdp-trending-news is-entering" : "pdp-trending-news"}"
|
|
6967
7825
|
aria-label="Trending news"
|
|
@@ -6978,7 +7836,7 @@ var PdpTrendingNewsLit = class extends LitElement12 {
|
|
|
6978
7836
|
<p class="pdp-tn-blurb">${featured.blurb}</p>
|
|
6979
7837
|
</article>
|
|
6980
7838
|
${rest.map(
|
|
6981
|
-
(item) =>
|
|
7839
|
+
(item) => html13`
|
|
6982
7840
|
<article class="pdp-tn-card">
|
|
6983
7841
|
<div class="pdp-tn-meta">
|
|
6984
7842
|
<span class="pdp-tn-dot" aria-hidden="true"></span>
|
|
@@ -7000,8 +7858,8 @@ PdpTrendingNewsLit.properties = {
|
|
|
7000
7858
|
_entering: { state: true }
|
|
7001
7859
|
};
|
|
7002
7860
|
function renderHeadlineLink(item) {
|
|
7003
|
-
if (!item.url) return
|
|
7004
|
-
return
|
|
7861
|
+
if (!item.url) return html13`${item.headline}`;
|
|
7862
|
+
return html13`<a
|
|
7005
7863
|
class="pdp-tn-headline-link"
|
|
7006
7864
|
href=${item.url}
|
|
7007
7865
|
target="_blank"
|
|
@@ -7014,7 +7872,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
7014
7872
|
}
|
|
7015
7873
|
|
|
7016
7874
|
// src/widgets/PdpTrendingNewsMagazineLit.ts
|
|
7017
|
-
import { html as
|
|
7875
|
+
import { html as html14, LitElement as LitElement14 } from "lit";
|
|
7018
7876
|
function ensureMagazineStyles() {
|
|
7019
7877
|
if (typeof document === "undefined") return;
|
|
7020
7878
|
if (document.getElementById("syntro-pdp-trending-news-magazine-style")) return;
|
|
@@ -7194,7 +8052,7 @@ var MAGAZINE_CSS = `
|
|
|
7194
8052
|
}
|
|
7195
8053
|
}
|
|
7196
8054
|
`;
|
|
7197
|
-
var PdpTrendingNewsMagazineLit = class extends
|
|
8055
|
+
var PdpTrendingNewsMagazineLit = class extends LitElement14 {
|
|
7198
8056
|
constructor() {
|
|
7199
8057
|
super(...arguments);
|
|
7200
8058
|
this.data = void 0;
|
|
@@ -7208,13 +8066,13 @@ var PdpTrendingNewsMagazineLit = class extends LitElement13 {
|
|
|
7208
8066
|
}
|
|
7209
8067
|
render() {
|
|
7210
8068
|
if (!this.data) {
|
|
7211
|
-
return
|
|
8069
|
+
return html14`<div aria-busy="true" class="pdp-tn-mag" style="opacity:0.5;">…</div>`;
|
|
7212
8070
|
}
|
|
7213
8071
|
const items = this.data.items;
|
|
7214
8072
|
const [lead, ...rest] = items;
|
|
7215
|
-
return
|
|
8073
|
+
return html14`
|
|
7216
8074
|
<section class="pdp-tn-mag" aria-label="Trending news">
|
|
7217
|
-
${lead ?
|
|
8075
|
+
${lead ? html14`<article class="pdp-tn-mag__lead">
|
|
7218
8076
|
<div class="pdp-tn-mag__meta">
|
|
7219
8077
|
<span class="pdp-tn-mag__meta-source">${lead.source}</span>
|
|
7220
8078
|
· ${lead.published_relative}
|
|
@@ -7223,7 +8081,7 @@ var PdpTrendingNewsMagazineLit = class extends LitElement13 {
|
|
|
7223
8081
|
<p class="pdp-tn-mag__blurb">${lead.blurb}</p>
|
|
7224
8082
|
</article>` : ""}
|
|
7225
8083
|
${rest.map(
|
|
7226
|
-
(it) =>
|
|
8084
|
+
(it) => html14`<article class="pdp-tn-mag__item">
|
|
7227
8085
|
<div class="pdp-tn-mag__meta">
|
|
7228
8086
|
<span class="pdp-tn-mag__meta-source">${it.source}</span>
|
|
7229
8087
|
· ${it.published_relative}
|
|
@@ -7240,8 +8098,8 @@ PdpTrendingNewsMagazineLit.properties = {
|
|
|
7240
8098
|
data: { attribute: false }
|
|
7241
8099
|
};
|
|
7242
8100
|
function renderHeadlineLink2(item) {
|
|
7243
|
-
if (!item.url) return
|
|
7244
|
-
return
|
|
8101
|
+
if (!item.url) return html14`${item.headline}`;
|
|
8102
|
+
return html14`<a
|
|
7245
8103
|
class="pdp-tn-mag__headline-link"
|
|
7246
8104
|
href=${item.url}
|
|
7247
8105
|
target="_blank"
|
|
@@ -7254,7 +8112,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
7254
8112
|
}
|
|
7255
8113
|
|
|
7256
8114
|
// src/widgets/PdpTrendingNewsHeroLit.ts
|
|
7257
|
-
import { html as
|
|
8115
|
+
import { html as html15, LitElement as LitElement15 } from "lit";
|
|
7258
8116
|
function ensureHeroStyles() {
|
|
7259
8117
|
if (typeof document === "undefined") return;
|
|
7260
8118
|
if (document.getElementById("syntro-pdp-trending-news-hero-style")) return;
|
|
@@ -7445,7 +8303,7 @@ var HERO_CSS = `
|
|
|
7445
8303
|
}
|
|
7446
8304
|
}
|
|
7447
8305
|
`;
|
|
7448
|
-
var PdpTrendingNewsHeroLit = class extends
|
|
8306
|
+
var PdpTrendingNewsHeroLit = class extends LitElement15 {
|
|
7449
8307
|
constructor() {
|
|
7450
8308
|
super(...arguments);
|
|
7451
8309
|
this.data = void 0;
|
|
@@ -7459,11 +8317,11 @@ var PdpTrendingNewsHeroLit = class extends LitElement14 {
|
|
|
7459
8317
|
}
|
|
7460
8318
|
render() {
|
|
7461
8319
|
if (!this.data) {
|
|
7462
|
-
return
|
|
8320
|
+
return html15`<div aria-busy="true" class="pdp-tn-hero" style="opacity:0.5;">…</div>`;
|
|
7463
8321
|
}
|
|
7464
8322
|
const [lead, ...rest] = this.data.items;
|
|
7465
|
-
if (!lead) return
|
|
7466
|
-
return
|
|
8323
|
+
if (!lead) return html15``;
|
|
8324
|
+
return html15`
|
|
7467
8325
|
<section class="pdp-tn-hero" aria-label="Trending news">
|
|
7468
8326
|
<article class="pdp-tn-hero__panel">
|
|
7469
8327
|
<blockquote class="pdp-tn-hero__quote">${lead.blurb}</blockquote>
|
|
@@ -7474,9 +8332,9 @@ var PdpTrendingNewsHeroLit = class extends LitElement14 {
|
|
|
7474
8332
|
</footer>
|
|
7475
8333
|
<p class="pdp-tn-hero__headline">${renderHeadlineLink3(lead)}</p>
|
|
7476
8334
|
</article>
|
|
7477
|
-
${rest.length ?
|
|
8335
|
+
${rest.length ? html15`<div class="pdp-tn-hero__followon">
|
|
7478
8336
|
${rest.map(
|
|
7479
|
-
(it) =>
|
|
8337
|
+
(it) => html15`<div class="pdp-tn-hero__row">
|
|
7480
8338
|
<p class="pdp-tn-hero__row-headline">${renderHeadlineLink3(it)}</p>
|
|
7481
8339
|
<span class="pdp-tn-hero__row-meta">${it.source}</span>
|
|
7482
8340
|
</div>`
|
|
@@ -7490,8 +8348,8 @@ PdpTrendingNewsHeroLit.properties = {
|
|
|
7490
8348
|
data: { attribute: false }
|
|
7491
8349
|
};
|
|
7492
8350
|
function renderHeadlineLink3(item) {
|
|
7493
|
-
if (!item.url) return
|
|
7494
|
-
return
|
|
8351
|
+
if (!item.url) return html15`${item.headline}`;
|
|
8352
|
+
return html15`<a
|
|
7495
8353
|
class="pdp-tn-hero__headline-link"
|
|
7496
8354
|
href=${item.url}
|
|
7497
8355
|
target="_blank"
|
|
@@ -7504,7 +8362,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
7504
8362
|
}
|
|
7505
8363
|
|
|
7506
8364
|
// src/widgets/PdpPeerFeedMasonryLit.ts
|
|
7507
|
-
import { html as
|
|
8365
|
+
import { html as html16, LitElement as LitElement16 } from "lit";
|
|
7508
8366
|
function ensureMasonryStyles() {
|
|
7509
8367
|
if (typeof document === "undefined") return;
|
|
7510
8368
|
if (document.getElementById("syntro-pdp-peer-feed-masonry-style")) return;
|
|
@@ -7648,7 +8506,7 @@ function personaHue(s4) {
|
|
|
7648
8506
|
for (let i2 = 0; i2 < s4.length; i2++) h = h * 31 + s4.charCodeAt(i2) | 0;
|
|
7649
8507
|
return Math.abs(h) % 360;
|
|
7650
8508
|
}
|
|
7651
|
-
var PdpPeerFeedMasonryLit = class extends
|
|
8509
|
+
var PdpPeerFeedMasonryLit = class extends LitElement16 {
|
|
7652
8510
|
constructor() {
|
|
7653
8511
|
super(...arguments);
|
|
7654
8512
|
this.data = void 0;
|
|
@@ -7662,13 +8520,13 @@ var PdpPeerFeedMasonryLit = class extends LitElement15 {
|
|
|
7662
8520
|
}
|
|
7663
8521
|
render() {
|
|
7664
8522
|
if (!this.data) {
|
|
7665
|
-
return
|
|
8523
|
+
return html16`<div aria-busy="true" class="pdp-pf-mas" style="opacity:0.5;">…</div>`;
|
|
7666
8524
|
}
|
|
7667
|
-
return
|
|
8525
|
+
return html16`
|
|
7668
8526
|
<section class="pdp-pf-mas" aria-label="Peer voices">
|
|
7669
8527
|
<div class="pdp-pf-mas__grid">
|
|
7670
8528
|
${this.data.peers.map(
|
|
7671
|
-
(p) =>
|
|
8529
|
+
(p) => html16`<article
|
|
7672
8530
|
class="pdp-pf-mas__card"
|
|
7673
8531
|
style=${`--pf-avatar-hue: ${personaHue(p.persona)};`}
|
|
7674
8532
|
>
|
|
@@ -7696,7 +8554,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-pee
|
|
|
7696
8554
|
}
|
|
7697
8555
|
|
|
7698
8556
|
// src/widgets/PdpPeerFeedCarouselLit.ts
|
|
7699
|
-
import { html as
|
|
8557
|
+
import { html as html17, LitElement as LitElement17, svg } from "lit";
|
|
7700
8558
|
function ensureCarouselStyles2() {
|
|
7701
8559
|
if (typeof document === "undefined") return;
|
|
7702
8560
|
if (document.getElementById("syntro-pdp-peer-feed-carousel-style")) return;
|
|
@@ -7912,7 +8770,7 @@ function renderGlyph(kind) {
|
|
|
7912
8770
|
<path d="M30 6 L34 26 L54 30 L34 34 L30 54 L26 34 L6 30 L26 26 Z" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linejoin="round"/>
|
|
7913
8771
|
</svg>`;
|
|
7914
8772
|
}
|
|
7915
|
-
var PdpPeerFeedCarouselLit = class extends
|
|
8773
|
+
var PdpPeerFeedCarouselLit = class extends LitElement17 {
|
|
7916
8774
|
constructor() {
|
|
7917
8775
|
super(...arguments);
|
|
7918
8776
|
this.data = void 0;
|
|
@@ -7926,15 +8784,15 @@ var PdpPeerFeedCarouselLit = class extends LitElement16 {
|
|
|
7926
8784
|
}
|
|
7927
8785
|
render() {
|
|
7928
8786
|
if (!this.data) {
|
|
7929
|
-
return
|
|
8787
|
+
return html17`<div aria-busy="true" class="pdp-pf-car" style="opacity:0.5;">…</div>`;
|
|
7930
8788
|
}
|
|
7931
|
-
return
|
|
8789
|
+
return html17`
|
|
7932
8790
|
<section class="pdp-pf-car" aria-label="Peer voices">
|
|
7933
8791
|
<div class="pdp-pf-car__track">
|
|
7934
8792
|
${this.data.peers.map((p) => {
|
|
7935
8793
|
const kind = glyphFor(p.persona);
|
|
7936
8794
|
const hue = glyphHue(p.persona);
|
|
7937
|
-
return
|
|
8795
|
+
return html17`<article class="pdp-pf-car__card" style=${`--pf-glyph-hue: ${hue};`}>
|
|
7938
8796
|
<span class="pdp-pf-car__glyph" aria-hidden="true">${renderGlyph(kind)}</span>
|
|
7939
8797
|
<span class="pdp-pf-car__persona">${p.persona}</span>
|
|
7940
8798
|
<blockquote class="pdp-pf-car__quote">${p.quote}</blockquote>
|
|
@@ -7959,7 +8817,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-pee
|
|
|
7959
8817
|
}
|
|
7960
8818
|
|
|
7961
8819
|
// src/widgets/PdpChartLit.ts
|
|
7962
|
-
import { html as
|
|
8820
|
+
import { html as html18, LitElement as LitElement18, nothing as nothing9, svg as svg2 } from "lit";
|
|
7963
8821
|
function formatNum(v) {
|
|
7964
8822
|
if (v === void 0 || !Number.isFinite(v)) return "\u2014";
|
|
7965
8823
|
if (Number.isInteger(v)) return v.toLocaleString("en-US");
|
|
@@ -8582,7 +9440,7 @@ function colorVar(seriesIdx) {
|
|
|
8582
9440
|
return `var(--pc-color-${seriesIdx % PALETTE_SIZE})`;
|
|
8583
9441
|
}
|
|
8584
9442
|
var _PdpChartLit_instances, renderLegend_fn, renderTable_fn, renderBars_fn, renderLine_fn;
|
|
8585
|
-
var PdpChartLit = class extends
|
|
9443
|
+
var PdpChartLit = class extends LitElement18 {
|
|
8586
9444
|
constructor() {
|
|
8587
9445
|
super(...arguments);
|
|
8588
9446
|
__privateAdd(this, _PdpChartLit_instances);
|
|
@@ -8618,7 +9476,7 @@ var PdpChartLit = class extends LitElement17 {
|
|
|
8618
9476
|
}
|
|
8619
9477
|
render() {
|
|
8620
9478
|
if (!this.data) {
|
|
8621
|
-
return
|
|
9479
|
+
return html18`<div data-skeleton class="pdp-chart-skeleton">
|
|
8622
9480
|
<div class="pdp-skeleton-bar" style="width:50%"></div>
|
|
8623
9481
|
<div class="pdp-skeleton-rect" style="height:200px"></div>
|
|
8624
9482
|
</div>`;
|
|
@@ -8627,7 +9485,7 @@ var PdpChartLit = class extends LitElement17 {
|
|
|
8627
9485
|
const allSeriesOnePoint = this.data.series.every((s4) => s4.points.length <= 1);
|
|
8628
9486
|
const effectiveKind = isTable ? "table" : this.data.viz_kind === "line" && !allSeriesOnePoint ? "line" : "bar";
|
|
8629
9487
|
const isMultiSeries = this.data.series.length > 1;
|
|
8630
|
-
return
|
|
9488
|
+
return html18`
|
|
8631
9489
|
<section
|
|
8632
9490
|
class="${this._entering ? "pdp-chart pdp-chart-frame is-entering" : "pdp-chart pdp-chart-frame"}"
|
|
8633
9491
|
aria-label="${this.data.title}"
|
|
@@ -8638,11 +9496,11 @@ var PdpChartLit = class extends LitElement17 {
|
|
|
8638
9496
|
</div>
|
|
8639
9497
|
<h3 class="pdp-chart-title">${this.data.title}</h3>
|
|
8640
9498
|
|
|
8641
|
-
${effectiveKind === "table" ? __privateMethod(this, _PdpChartLit_instances, renderTable_fn).call(this) :
|
|
8642
|
-
${effectiveKind === "bar" ? __privateMethod(this, _PdpChartLit_instances, renderBars_fn).call(this) :
|
|
8643
|
-
${effectiveKind === "line" ? __privateMethod(this, _PdpChartLit_instances, renderLine_fn).call(this) :
|
|
8644
|
-
${effectiveKind !== "table" && isMultiSeries ? __privateMethod(this, _PdpChartLit_instances, renderLegend_fn).call(this) :
|
|
8645
|
-
${this.data.rationale ?
|
|
9499
|
+
${effectiveKind === "table" ? __privateMethod(this, _PdpChartLit_instances, renderTable_fn).call(this) : nothing9}
|
|
9500
|
+
${effectiveKind === "bar" ? __privateMethod(this, _PdpChartLit_instances, renderBars_fn).call(this) : nothing9}
|
|
9501
|
+
${effectiveKind === "line" ? __privateMethod(this, _PdpChartLit_instances, renderLine_fn).call(this) : nothing9}
|
|
9502
|
+
${effectiveKind !== "table" && isMultiSeries ? __privateMethod(this, _PdpChartLit_instances, renderLegend_fn).call(this) : nothing9}
|
|
9503
|
+
${this.data.rationale ? html18`<p class="pdp-chart-rationale">${this.data.rationale}</p>` : nothing9}
|
|
8646
9504
|
</section>
|
|
8647
9505
|
`;
|
|
8648
9506
|
}
|
|
@@ -8650,10 +9508,10 @@ var PdpChartLit = class extends LitElement17 {
|
|
|
8650
9508
|
_PdpChartLit_instances = new WeakSet();
|
|
8651
9509
|
renderLegend_fn = function() {
|
|
8652
9510
|
const data = this.data;
|
|
8653
|
-
return
|
|
9511
|
+
return html18`
|
|
8654
9512
|
<ol class="pdp-chart-legend" aria-label="Legend">
|
|
8655
9513
|
${data.series.map(
|
|
8656
|
-
(s4, idx) =>
|
|
9514
|
+
(s4, idx) => html18`
|
|
8657
9515
|
<li class="pdp-chart-legend-item">
|
|
8658
9516
|
<span
|
|
8659
9517
|
class="pdp-chart-legend-swatch"
|
|
@@ -8669,22 +9527,22 @@ renderLegend_fn = function() {
|
|
|
8669
9527
|
};
|
|
8670
9528
|
renderTable_fn = function() {
|
|
8671
9529
|
const matrix = buildComparisonMatrix(this.data);
|
|
8672
|
-
return
|
|
9530
|
+
return html18`
|
|
8673
9531
|
<div class="pdp-chart-table-wrap">
|
|
8674
9532
|
<table class="pdp-chart-table">
|
|
8675
9533
|
<thead>
|
|
8676
9534
|
<tr>
|
|
8677
9535
|
<th></th>
|
|
8678
|
-
${matrix.colLabels.map((label) =>
|
|
9536
|
+
${matrix.colLabels.map((label) => html18`<th>${label}</th>`)}
|
|
8679
9537
|
</tr>
|
|
8680
9538
|
</thead>
|
|
8681
9539
|
<tbody>
|
|
8682
9540
|
${matrix.rowLabels.map(
|
|
8683
|
-
(row, rowIdx) =>
|
|
9541
|
+
(row, rowIdx) => html18`
|
|
8684
9542
|
<tr class=${rowIdx === matrix.winningRow ? "is-winning" : ""}>
|
|
8685
9543
|
<td>${row}</td>
|
|
8686
9544
|
${matrix.cells[rowIdx].map(
|
|
8687
|
-
(cell) =>
|
|
9545
|
+
(cell) => html18`<td class="pdp-chart-numeric-cell">${formatNum(cell)}</td>`
|
|
8688
9546
|
)}
|
|
8689
9547
|
</tr>
|
|
8690
9548
|
`
|
|
@@ -8699,7 +9557,7 @@ renderBars_fn = function() {
|
|
|
8699
9557
|
const { buckets, yMax } = buildBarBuckets(data);
|
|
8700
9558
|
const ticks = niceTicks(yMax);
|
|
8701
9559
|
const scaleMax = Math.max(yMax, ticks[ticks.length - 1]);
|
|
8702
|
-
return
|
|
9560
|
+
return html18`
|
|
8703
9561
|
<div
|
|
8704
9562
|
class="pdp-chart-bars"
|
|
8705
9563
|
role="figure"
|
|
@@ -8708,7 +9566,7 @@ renderBars_fn = function() {
|
|
|
8708
9566
|
>
|
|
8709
9567
|
<div class="pdp-chart-yaxis" aria-hidden="true">
|
|
8710
9568
|
${ticks.map(
|
|
8711
|
-
(t2) =>
|
|
9569
|
+
(t2) => html18`
|
|
8712
9570
|
<span
|
|
8713
9571
|
class="pdp-chart-yaxis-tick"
|
|
8714
9572
|
style="bottom:${t2 / scaleMax * 100}%"
|
|
@@ -8720,7 +9578,7 @@ renderBars_fn = function() {
|
|
|
8720
9578
|
</div>
|
|
8721
9579
|
<div class="pdp-chart-bars-area">
|
|
8722
9580
|
${ticks.map(
|
|
8723
|
-
(t2) =>
|
|
9581
|
+
(t2) => html18`
|
|
8724
9582
|
<div
|
|
8725
9583
|
class="pdp-chart-bars-gridline"
|
|
8726
9584
|
style="bottom:${t2 / scaleMax * 100}%"
|
|
@@ -8730,11 +9588,11 @@ renderBars_fn = function() {
|
|
|
8730
9588
|
)}
|
|
8731
9589
|
<div class="pdp-chart-bars-baseline" aria-hidden="true"></div>
|
|
8732
9590
|
${buckets.map(
|
|
8733
|
-
(bucket, bIdx) =>
|
|
9591
|
+
(bucket, bIdx) => html18`
|
|
8734
9592
|
<div class="pdp-chart-bars-bucket">
|
|
8735
9593
|
<div class="pdp-chart-bars-stack">
|
|
8736
9594
|
${bucket.bars.map(
|
|
8737
|
-
(bar, barIdxInBucket) =>
|
|
9595
|
+
(bar, barIdxInBucket) => html18`
|
|
8738
9596
|
<div
|
|
8739
9597
|
class="pdp-chart-bar"
|
|
8740
9598
|
style="
|
|
@@ -8745,9 +9603,9 @@ renderBars_fn = function() {
|
|
|
8745
9603
|
role="img"
|
|
8746
9604
|
aria-label="${bucket.label} — ${bar.seriesName}: ${formatNum(bar.value)}"
|
|
8747
9605
|
>
|
|
8748
|
-
${bucket.bars.length === 1 ?
|
|
9606
|
+
${bucket.bars.length === 1 ? html18`<span class="pdp-chart-bar-value"
|
|
8749
9607
|
>${formatNum(bar.value)}</span
|
|
8750
|
-
>` :
|
|
9608
|
+
>` : nothing9}
|
|
8751
9609
|
</div>
|
|
8752
9610
|
`
|
|
8753
9611
|
)}
|
|
@@ -8765,7 +9623,7 @@ renderLine_fn = function() {
|
|
|
8765
9623
|
const { series, xLabels, yMax } = buildLinePaths(data);
|
|
8766
9624
|
const ticks = niceTicks(yMax);
|
|
8767
9625
|
const scaleMax = Math.max(yMax, ticks[ticks.length - 1]);
|
|
8768
|
-
return
|
|
9626
|
+
return html18`
|
|
8769
9627
|
<div
|
|
8770
9628
|
class="pdp-chart-line"
|
|
8771
9629
|
role="figure"
|
|
@@ -8775,7 +9633,7 @@ renderLine_fn = function() {
|
|
|
8775
9633
|
>
|
|
8776
9634
|
<div class="pdp-chart-line-yaxis" aria-hidden="true">
|
|
8777
9635
|
${ticks.map(
|
|
8778
|
-
(t2) =>
|
|
9636
|
+
(t2) => html18`
|
|
8779
9637
|
<span
|
|
8780
9638
|
class="pdp-chart-yaxis-tick"
|
|
8781
9639
|
style="bottom:${t2 / scaleMax * 100}%"
|
|
@@ -8824,7 +9682,7 @@ renderLine_fn = function() {
|
|
|
8824
9682
|
style="--col-count:${xLabels.length}"
|
|
8825
9683
|
aria-hidden="true"
|
|
8826
9684
|
>
|
|
8827
|
-
${xLabels.map((label) =>
|
|
9685
|
+
${xLabels.map((label) => html18`<span>${label}</span>`)}
|
|
8828
9686
|
</div>
|
|
8829
9687
|
`;
|
|
8830
9688
|
};
|
|
@@ -8837,7 +9695,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
8837
9695
|
}
|
|
8838
9696
|
|
|
8839
9697
|
// src/widgets/PdpChartBarStackedLit.ts
|
|
8840
|
-
import { html as
|
|
9698
|
+
import { html as html19, LitElement as LitElement19 } from "lit";
|
|
8841
9699
|
function ensureStyles() {
|
|
8842
9700
|
if (typeof document === "undefined") return;
|
|
8843
9701
|
if (document.getElementById("syntro-pdp-chart-bar-style")) return;
|
|
@@ -8925,7 +9783,7 @@ var CSS = `
|
|
|
8925
9783
|
}
|
|
8926
9784
|
`;
|
|
8927
9785
|
var PALETTE = ["hsl(150 32% 50%)", "hsl(40 75% 60%)", "hsl(208 50% 55%)", "hsl(320 38% 60%)"];
|
|
8928
|
-
var PdpChartBarStackedLit = class extends
|
|
9786
|
+
var PdpChartBarStackedLit = class extends LitElement19 {
|
|
8929
9787
|
constructor() {
|
|
8930
9788
|
super(...arguments);
|
|
8931
9789
|
this.data = void 0;
|
|
@@ -8938,16 +9796,16 @@ var PdpChartBarStackedLit = class extends LitElement18 {
|
|
|
8938
9796
|
ensureStyles();
|
|
8939
9797
|
}
|
|
8940
9798
|
render() {
|
|
8941
|
-
if (!this.data) return
|
|
9799
|
+
if (!this.data) return html19`<div class="pdp-chart-bar" style="opacity:0.5;">…</div>`;
|
|
8942
9800
|
const { title, series } = this.data;
|
|
8943
9801
|
const allX = Array.from(new Set(series.flatMap((s4) => s4.points.map((p) => p.x))));
|
|
8944
9802
|
const max = Math.max(...series.flatMap((s4) => s4.points.map((p) => p.y)), 1);
|
|
8945
|
-
return
|
|
9803
|
+
return html19`
|
|
8946
9804
|
<section class="pdp-chart-bar" aria-label=${title}>
|
|
8947
9805
|
<h3 class="pdp-chart-bar__title">${title}</h3>
|
|
8948
|
-
${series.length > 1 ?
|
|
9806
|
+
${series.length > 1 ? html19`<div class="pdp-chart-bar__legend">
|
|
8949
9807
|
${series.map(
|
|
8950
|
-
(s4, i2) =>
|
|
9808
|
+
(s4, i2) => html19`<span
|
|
8951
9809
|
><span
|
|
8952
9810
|
class="pdp-chart-bar__legend-dot"
|
|
8953
9811
|
style=${`background: ${PALETTE[i2 % PALETTE.length]};`}
|
|
@@ -8957,14 +9815,14 @@ var PdpChartBarStackedLit = class extends LitElement18 {
|
|
|
8957
9815
|
)}
|
|
8958
9816
|
</div>` : ""}
|
|
8959
9817
|
${allX.map(
|
|
8960
|
-
(x) =>
|
|
9818
|
+
(x) => html19`<div class="pdp-chart-bar__row">
|
|
8961
9819
|
<div class="pdp-chart-bar__row-label">${x}</div>
|
|
8962
9820
|
<div class="pdp-chart-bar__row-bars">
|
|
8963
9821
|
${series.map((s4, i2) => {
|
|
8964
9822
|
const point = s4.points.find((p) => p.x === x);
|
|
8965
9823
|
if (!point) return "";
|
|
8966
9824
|
const pct = point.y / max * 100;
|
|
8967
|
-
return
|
|
9825
|
+
return html19`<div class="pdp-chart-bar__bar-track">
|
|
8968
9826
|
<div
|
|
8969
9827
|
class="pdp-chart-bar__bar-fill"
|
|
8970
9828
|
style=${`width: ${pct.toFixed(1)}%; background: ${PALETTE[i2 % PALETTE.length]};`}
|
|
@@ -8987,7 +9845,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
8987
9845
|
}
|
|
8988
9846
|
|
|
8989
9847
|
// src/widgets/PdpChartMetricPullLit.ts
|
|
8990
|
-
import { html as
|
|
9848
|
+
import { html as html20, LitElement as LitElement20, svg as svg3 } from "lit";
|
|
8991
9849
|
function ensureStyles2() {
|
|
8992
9850
|
if (typeof document === "undefined") return;
|
|
8993
9851
|
if (document.getElementById("syntro-pdp-chart-metric-style")) return;
|
|
@@ -9099,7 +9957,7 @@ function buildSparkline(points) {
|
|
|
9099
9957
|
const peakIndex = points.reduce((best, p, i2) => p.y > points[best].y ? i2 : best, 0);
|
|
9100
9958
|
return { linePath: line, areaPath: area, peakIndex };
|
|
9101
9959
|
}
|
|
9102
|
-
var PdpChartMetricPullLit = class extends
|
|
9960
|
+
var PdpChartMetricPullLit = class extends LitElement20 {
|
|
9103
9961
|
constructor() {
|
|
9104
9962
|
super(...arguments);
|
|
9105
9963
|
this.data = void 0;
|
|
@@ -9112,10 +9970,10 @@ var PdpChartMetricPullLit = class extends LitElement19 {
|
|
|
9112
9970
|
ensureStyles2();
|
|
9113
9971
|
}
|
|
9114
9972
|
render() {
|
|
9115
|
-
if (!this.data) return
|
|
9973
|
+
if (!this.data) return html20`<div class="pdp-chart-met" style="opacity:0.5;">…</div>`;
|
|
9116
9974
|
const { title, series } = this.data;
|
|
9117
9975
|
const first = series[0];
|
|
9118
|
-
if (!first || first.points.length === 0) return
|
|
9976
|
+
if (!first || first.points.length === 0) return html20``;
|
|
9119
9977
|
const points = first.points;
|
|
9120
9978
|
const peak = points.reduce((b, p) => p.y > b.y ? p : b, points[0]);
|
|
9121
9979
|
const mean = points.reduce((s4, p) => s4 + p.y, 0) / points.length;
|
|
@@ -9128,7 +9986,7 @@ var PdpChartMetricPullLit = class extends LitElement19 {
|
|
|
9128
9986
|
const max = Math.max(...points.map((p) => p.y));
|
|
9129
9987
|
const range = max - min || 1;
|
|
9130
9988
|
const py = h - 4 - (peak.y - min) / range * (h - 8);
|
|
9131
|
-
return
|
|
9989
|
+
return html20`
|
|
9132
9990
|
<section class="pdp-chart-met" aria-label=${title}>
|
|
9133
9991
|
<div class="pdp-chart-met__metric">
|
|
9134
9992
|
<span class="pdp-chart-met__big">${peak.y}</span>
|
|
@@ -9160,7 +10018,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
9160
10018
|
}
|
|
9161
10019
|
|
|
9162
10020
|
// src/widgets/PdpQaAccordionLit.ts
|
|
9163
|
-
import { html as
|
|
10021
|
+
import { html as html21, LitElement as LitElement21 } from "lit";
|
|
9164
10022
|
function ensureStyles3() {
|
|
9165
10023
|
if (typeof document === "undefined") return;
|
|
9166
10024
|
if (document.getElementById("syntro-pdp-qa-accordion-style")) return;
|
|
@@ -9242,7 +10100,7 @@ var CSS3 = `
|
|
|
9242
10100
|
max-width: 56ch;
|
|
9243
10101
|
}
|
|
9244
10102
|
`;
|
|
9245
|
-
var PdpQaAccordionLit = class extends
|
|
10103
|
+
var PdpQaAccordionLit = class extends LitElement21 {
|
|
9246
10104
|
constructor() {
|
|
9247
10105
|
super(...arguments);
|
|
9248
10106
|
this.data = void 0;
|
|
@@ -9255,11 +10113,11 @@ var PdpQaAccordionLit = class extends LitElement20 {
|
|
|
9255
10113
|
ensureStyles3();
|
|
9256
10114
|
}
|
|
9257
10115
|
render() {
|
|
9258
|
-
if (!this.data) return
|
|
9259
|
-
return
|
|
10116
|
+
if (!this.data) return html21`<div class="pdp-qa-acc" style="opacity:0.5;">…</div>`;
|
|
10117
|
+
return html21`
|
|
9260
10118
|
<section class="pdp-qa-acc" aria-label="Frequently asked questions">
|
|
9261
10119
|
${this.data.items.map(
|
|
9262
|
-
(it) =>
|
|
10120
|
+
(it) => html21`<details class="pdp-qa-acc__item">
|
|
9263
10121
|
<summary>
|
|
9264
10122
|
<span class="pdp-qa-acc__chev" aria-hidden="true"></span>
|
|
9265
10123
|
<span>${it.question}</span>
|
|
@@ -9279,7 +10137,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-qa-
|
|
|
9279
10137
|
}
|
|
9280
10138
|
|
|
9281
10139
|
// src/widgets/PdpQaSideBySideLit.ts
|
|
9282
|
-
import { html as
|
|
10140
|
+
import { html as html22, LitElement as LitElement22 } from "lit";
|
|
9283
10141
|
function ensureStyles4() {
|
|
9284
10142
|
if (typeof document === "undefined") return;
|
|
9285
10143
|
if (document.getElementById("syntro-pdp-qa-side-style")) return;
|
|
@@ -9353,7 +10211,7 @@ var CSS4 = `
|
|
|
9353
10211
|
margin: 0;
|
|
9354
10212
|
}
|
|
9355
10213
|
`;
|
|
9356
|
-
var PdpQaSideBySideLit = class extends
|
|
10214
|
+
var PdpQaSideBySideLit = class extends LitElement22 {
|
|
9357
10215
|
constructor() {
|
|
9358
10216
|
super(...arguments);
|
|
9359
10217
|
this.data = void 0;
|
|
@@ -9366,11 +10224,11 @@ var PdpQaSideBySideLit = class extends LitElement21 {
|
|
|
9366
10224
|
ensureStyles4();
|
|
9367
10225
|
}
|
|
9368
10226
|
render() {
|
|
9369
|
-
if (!this.data) return
|
|
9370
|
-
return
|
|
10227
|
+
if (!this.data) return html22`<div class="pdp-qa-side" style="opacity:0.5;">…</div>`;
|
|
10228
|
+
return html22`
|
|
9371
10229
|
<section class="pdp-qa-side" aria-label="Q&A">
|
|
9372
10230
|
${this.data.items.map(
|
|
9373
|
-
(it) =>
|
|
10231
|
+
(it) => html22`<div class="pdp-qa-side__item">
|
|
9374
10232
|
<h4 class="pdp-qa-side__q">${it.question}</h4>
|
|
9375
10233
|
<p class="pdp-qa-side__a">${it.answer}</p>
|
|
9376
10234
|
</div>`
|
|
@@ -9387,7 +10245,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-qa-
|
|
|
9387
10245
|
}
|
|
9388
10246
|
|
|
9389
10247
|
// src/widgets/PdpRegionalFullBleedLit.ts
|
|
9390
|
-
import { html as
|
|
10248
|
+
import { html as html23, LitElement as LitElement23 } from "lit";
|
|
9391
10249
|
function ensureStyles5() {
|
|
9392
10250
|
if (typeof document === "undefined") return;
|
|
9393
10251
|
if (document.getElementById("syntro-pdp-regional-fullbleed-style")) return;
|
|
@@ -9478,7 +10336,7 @@ var CSS5 = `
|
|
|
9478
10336
|
font-style: italic;
|
|
9479
10337
|
}
|
|
9480
10338
|
`;
|
|
9481
|
-
var PdpRegionalFullBleedLit = class extends
|
|
10339
|
+
var PdpRegionalFullBleedLit = class extends LitElement23 {
|
|
9482
10340
|
constructor() {
|
|
9483
10341
|
super(...arguments);
|
|
9484
10342
|
this.data = void 0;
|
|
@@ -9491,9 +10349,9 @@ var PdpRegionalFullBleedLit = class extends LitElement22 {
|
|
|
9491
10349
|
ensureStyles5();
|
|
9492
10350
|
}
|
|
9493
10351
|
render() {
|
|
9494
|
-
if (!this.data) return
|
|
10352
|
+
if (!this.data) return html23`<div class="pdp-reg-fb" style="opacity:0.5;">…</div>`;
|
|
9495
10353
|
const d = this.data;
|
|
9496
|
-
return
|
|
10354
|
+
return html23`
|
|
9497
10355
|
<section class="pdp-reg-fb" aria-label="Where you are">
|
|
9498
10356
|
<article class="pdp-reg-fb__panel">
|
|
9499
10357
|
<svg class="pdp-reg-fb__glyph" viewBox="0 0 56 56" aria-hidden="true">
|
|
@@ -9536,7 +10394,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-reg
|
|
|
9536
10394
|
}
|
|
9537
10395
|
|
|
9538
10396
|
// src/widgets/PdpRegionalInlineNoteLit.ts
|
|
9539
|
-
import { html as
|
|
10397
|
+
import { html as html24, LitElement as LitElement24 } from "lit";
|
|
9540
10398
|
function ensureStyles6() {
|
|
9541
10399
|
if (typeof document === "undefined") return;
|
|
9542
10400
|
if (document.getElementById("syntro-pdp-regional-inline-style")) return;
|
|
@@ -9597,7 +10455,7 @@ var CSS6 = `
|
|
|
9597
10455
|
font-style: italic;
|
|
9598
10456
|
}
|
|
9599
10457
|
`;
|
|
9600
|
-
var PdpRegionalInlineNoteLit = class extends
|
|
10458
|
+
var PdpRegionalInlineNoteLit = class extends LitElement24 {
|
|
9601
10459
|
constructor() {
|
|
9602
10460
|
super(...arguments);
|
|
9603
10461
|
this.data = void 0;
|
|
@@ -9610,9 +10468,9 @@ var PdpRegionalInlineNoteLit = class extends LitElement23 {
|
|
|
9610
10468
|
ensureStyles6();
|
|
9611
10469
|
}
|
|
9612
10470
|
render() {
|
|
9613
|
-
if (!this.data) return
|
|
10471
|
+
if (!this.data) return html24`<div class="pdp-reg-in" style="opacity:0.5;">…</div>`;
|
|
9614
10472
|
const d = this.data;
|
|
9615
|
-
return
|
|
10473
|
+
return html24`
|
|
9616
10474
|
<aside class="pdp-reg-in" aria-label="Regional note">
|
|
9617
10475
|
<svg class="pdp-reg-in__icon" viewBox="0 0 22 22" aria-hidden="true">
|
|
9618
10476
|
<circle cx="11" cy="11" r="9" fill="none" stroke="currentColor" stroke-width="1.6"/>
|
|
@@ -9640,8 +10498,8 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-reg
|
|
|
9640
10498
|
}
|
|
9641
10499
|
|
|
9642
10500
|
// src/widgets/PdpSlotPlaceholderLit.ts
|
|
9643
|
-
import { html as
|
|
9644
|
-
var PdpSlotPlaceholderLit = class extends
|
|
10501
|
+
import { html as html25, LitElement as LitElement25 } from "lit";
|
|
10502
|
+
var PdpSlotPlaceholderLit = class extends LitElement25 {
|
|
9645
10503
|
constructor() {
|
|
9646
10504
|
super(...arguments);
|
|
9647
10505
|
this.label = "More personalized content coming soon";
|
|
@@ -9650,7 +10508,7 @@ var PdpSlotPlaceholderLit = class extends LitElement24 {
|
|
|
9650
10508
|
return this;
|
|
9651
10509
|
}
|
|
9652
10510
|
render() {
|
|
9653
|
-
return
|
|
10511
|
+
return html25`
|
|
9654
10512
|
<div class="pdp-slot-placeholder" data-placeholder>
|
|
9655
10513
|
<p>${this.label}</p>
|
|
9656
10514
|
<small>Ask in chat to explore this product further.</small>
|
|
@@ -9666,7 +10524,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-slo
|
|
|
9666
10524
|
}
|
|
9667
10525
|
|
|
9668
10526
|
// src/widgets/PdpRibbonLit.ts
|
|
9669
|
-
import { html as
|
|
10527
|
+
import { html as html26, LitElement as LitElement26, nothing as nothing10 } from "lit";
|
|
9670
10528
|
function ensureRibbonStyles() {
|
|
9671
10529
|
if (typeof document === "undefined") return;
|
|
9672
10530
|
if (document.getElementById("syntro-pdp-ribbon-style")) return;
|
|
@@ -9795,7 +10653,7 @@ var RIBBON_CSS = `
|
|
|
9795
10653
|
100% { transform: translateX(110%); }
|
|
9796
10654
|
}
|
|
9797
10655
|
`;
|
|
9798
|
-
var PdpRibbonLit = class extends
|
|
10656
|
+
var PdpRibbonLit = class extends LitElement26 {
|
|
9799
10657
|
constructor() {
|
|
9800
10658
|
super(...arguments);
|
|
9801
10659
|
this.topic = "";
|
|
@@ -9833,7 +10691,7 @@ var PdpRibbonLit = class extends LitElement25 {
|
|
|
9833
10691
|
}
|
|
9834
10692
|
render() {
|
|
9835
10693
|
const cls = this._entering ? "syntro-pdp-ribbon is-entering" : "syntro-pdp-ribbon";
|
|
9836
|
-
return
|
|
10694
|
+
return html26`
|
|
9837
10695
|
<div class="${cls}" role="status" aria-live="polite">
|
|
9838
10696
|
<span class="syntro-pdp-ribbon-sweep" aria-hidden="true"></span>
|
|
9839
10697
|
<svg
|
|
@@ -9853,7 +10711,7 @@ var PdpRibbonLit = class extends LitElement25 {
|
|
|
9853
10711
|
</svg>
|
|
9854
10712
|
<p class="syntro-pdp-ribbon-copy">
|
|
9855
10713
|
Tailored to your conversation about
|
|
9856
|
-
${this.topic ?
|
|
10714
|
+
${this.topic ? html26` <span class="syntro-pdp-ribbon-topic">${this.topic}</span>.` : nothing10}
|
|
9857
10715
|
<span class="syntro-pdp-ribbon-trail"> ${this.subtitle}</span>
|
|
9858
10716
|
</p>
|
|
9859
10717
|
</div>
|
|
@@ -9869,219 +10727,6 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-rib
|
|
|
9869
10727
|
customElements.define("syntro-pdp-ribbon", PdpRibbonLit);
|
|
9870
10728
|
}
|
|
9871
10729
|
|
|
9872
|
-
// src/widgets/PdpSlotLit.ts
|
|
9873
|
-
import { html as html26, LitElement as LitElement26, nothing as nothing10 } from "lit";
|
|
9874
|
-
function ensureSlotStyles() {
|
|
9875
|
-
if (typeof document === "undefined") return;
|
|
9876
|
-
if (document.getElementById("syntro-pdp-slot-style")) return;
|
|
9877
|
-
const s4 = document.createElement("style");
|
|
9878
|
-
s4.id = "syntro-pdp-slot-style";
|
|
9879
|
-
s4.textContent = SLOT_CSS;
|
|
9880
|
-
document.head.appendChild(s4);
|
|
9881
|
-
}
|
|
9882
|
-
var SLOT_CSS = `
|
|
9883
|
-
.syntro-pdp-slot {
|
|
9884
|
-
--sl-primary: var(--syntro-pdp-primary, hsl(150 32% 38%));
|
|
9885
|
-
--sl-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
9886
|
-
--sl-muted: var(--syntro-pdp-muted, hsl(30 8% 55%));
|
|
9887
|
-
--sl-border: var(--syntro-pdp-border, hsl(30 6% 18%));
|
|
9888
|
-
--sl-bg-a: var(--syntro-pdp-card-bg, hsl(25 10% 11%));
|
|
9889
|
-
--sl-bg-b: var(--syntro-pdp-card-bg-b, hsl(25 10% 9%));
|
|
9890
|
-
--sl-display: var(--syntro-pdp-display, inherit);
|
|
9891
|
-
|
|
9892
|
-
position: relative;
|
|
9893
|
-
display: grid;
|
|
9894
|
-
grid-template-areas: 'stack';
|
|
9895
|
-
font-family: var(--syntro-pdp-sans, inherit);
|
|
9896
|
-
}
|
|
9897
|
-
.syntro-pdp-slot > .syntro-pdp-slot-mount,
|
|
9898
|
-
.syntro-pdp-slot > .syntro-pdp-slot-pending,
|
|
9899
|
-
.syntro-pdp-slot > .syntro-pdp-slot-failed {
|
|
9900
|
-
grid-area: stack;
|
|
9901
|
-
}
|
|
9902
|
-
|
|
9903
|
-
.syntro-pdp-slot-mount {
|
|
9904
|
-
min-height: 220px;
|
|
9905
|
-
}
|
|
9906
|
-
|
|
9907
|
-
/* When mutation arrived, hide the pending layer */
|
|
9908
|
-
.syntro-pdp-slot[data-state='mounted'] .syntro-pdp-slot-pending,
|
|
9909
|
-
.syntro-pdp-slot[data-state='failed'] .syntro-pdp-slot-pending {
|
|
9910
|
-
display: none;
|
|
9911
|
-
}
|
|
9912
|
-
.syntro-pdp-slot[data-state='mounted'] .syntro-pdp-slot-failed,
|
|
9913
|
-
.syntro-pdp-slot[data-state='pending'] .syntro-pdp-slot-failed {
|
|
9914
|
-
display: none;
|
|
9915
|
-
}
|
|
9916
|
-
|
|
9917
|
-
/* Pending \u2014 a quietly-animated waiting state. */
|
|
9918
|
-
.syntro-pdp-slot-pending {
|
|
9919
|
-
position: relative;
|
|
9920
|
-
border: 1px dashed var(--sl-border);
|
|
9921
|
-
border-radius: 22px;
|
|
9922
|
-
min-height: 220px;
|
|
9923
|
-
padding: 36px 28px;
|
|
9924
|
-
background: linear-gradient(180deg, var(--sl-bg-a), var(--sl-bg-b));
|
|
9925
|
-
overflow: hidden;
|
|
9926
|
-
display: flex;
|
|
9927
|
-
align-items: center;
|
|
9928
|
-
justify-content: flex-start;
|
|
9929
|
-
}
|
|
9930
|
-
.syntro-pdp-slot-pending-shimmer {
|
|
9931
|
-
position: absolute;
|
|
9932
|
-
inset: 0;
|
|
9933
|
-
background: linear-gradient(
|
|
9934
|
-
90deg,
|
|
9935
|
-
transparent,
|
|
9936
|
-
color-mix(in srgb, var(--sl-primary) 7%, transparent),
|
|
9937
|
-
transparent
|
|
9938
|
-
);
|
|
9939
|
-
transform: translateX(-100%);
|
|
9940
|
-
animation: syntro-pdp-slot-sweep 1800ms ease-in-out infinite;
|
|
9941
|
-
}
|
|
9942
|
-
.syntro-pdp-slot-pending-row {
|
|
9943
|
-
position: relative;
|
|
9944
|
-
z-index: 1;
|
|
9945
|
-
display: flex;
|
|
9946
|
-
align-items: center;
|
|
9947
|
-
gap: 12px;
|
|
9948
|
-
}
|
|
9949
|
-
.syntro-pdp-slot-pending-dot {
|
|
9950
|
-
flex: 0 0 auto;
|
|
9951
|
-
width: 8px;
|
|
9952
|
-
height: 8px;
|
|
9953
|
-
border-radius: 50%;
|
|
9954
|
-
background: var(--sl-primary);
|
|
9955
|
-
box-shadow: 0 0 0 6px color-mix(in srgb, var(--sl-primary) 14%, transparent);
|
|
9956
|
-
animation: syntro-pdp-slot-pulse 1600ms ease-in-out infinite;
|
|
9957
|
-
}
|
|
9958
|
-
.syntro-pdp-slot-pending-label {
|
|
9959
|
-
font-family: var(--sl-display);
|
|
9960
|
-
font-style: italic;
|
|
9961
|
-
font-size: 18px;
|
|
9962
|
-
font-variation-settings: 'SOFT' 70;
|
|
9963
|
-
color: var(--sl-muted);
|
|
9964
|
-
margin: 0;
|
|
9965
|
-
line-height: 1.4;
|
|
9966
|
-
}
|
|
9967
|
-
|
|
9968
|
-
/* Failed \u2014 the planner or sub-agent failed; the host page still works. */
|
|
9969
|
-
.syntro-pdp-slot-failed {
|
|
9970
|
-
border: 1px solid var(--sl-border);
|
|
9971
|
-
border-radius: 22px;
|
|
9972
|
-
min-height: 180px;
|
|
9973
|
-
padding: 24px 28px;
|
|
9974
|
-
background: linear-gradient(180deg, var(--sl-bg-a), var(--sl-bg-b));
|
|
9975
|
-
display: flex;
|
|
9976
|
-
flex-direction: column;
|
|
9977
|
-
justify-content: center;
|
|
9978
|
-
gap: 6px;
|
|
9979
|
-
}
|
|
9980
|
-
.syntro-pdp-slot-failed-title {
|
|
9981
|
-
font-family: var(--sl-display);
|
|
9982
|
-
font-style: italic;
|
|
9983
|
-
font-size: 17px;
|
|
9984
|
-
color: var(--sl-fg);
|
|
9985
|
-
margin: 0;
|
|
9986
|
-
font-variation-settings: 'SOFT' 70;
|
|
9987
|
-
}
|
|
9988
|
-
.syntro-pdp-slot-failed-body {
|
|
9989
|
-
color: var(--sl-muted);
|
|
9990
|
-
font-size: 13.5px;
|
|
9991
|
-
margin: 0;
|
|
9992
|
-
line-height: 1.5;
|
|
9993
|
-
}
|
|
9994
|
-
|
|
9995
|
-
@media (max-width: 640px) {
|
|
9996
|
-
.syntro-pdp-slot-pending,
|
|
9997
|
-
.syntro-pdp-slot-failed {
|
|
9998
|
-
border-radius: 18px;
|
|
9999
|
-
padding: 24px 18px;
|
|
10000
|
-
min-height: 180px;
|
|
10001
|
-
}
|
|
10002
|
-
.syntro-pdp-slot-pending-label {
|
|
10003
|
-
font-size: 15.5px;
|
|
10004
|
-
}
|
|
10005
|
-
}
|
|
10006
|
-
|
|
10007
|
-
@keyframes syntro-pdp-slot-sweep {
|
|
10008
|
-
0% { transform: translateX(-100%); }
|
|
10009
|
-
100% { transform: translateX(100%); }
|
|
10010
|
-
}
|
|
10011
|
-
@keyframes syntro-pdp-slot-pulse {
|
|
10012
|
-
0%, 100% {
|
|
10013
|
-
opacity: 0.6;
|
|
10014
|
-
box-shadow: 0 0 0 6px color-mix(in srgb, var(--sl-primary) 14%, transparent);
|
|
10015
|
-
}
|
|
10016
|
-
50% {
|
|
10017
|
-
opacity: 1;
|
|
10018
|
-
box-shadow: 0 0 0 8px color-mix(in srgb, var(--sl-primary) 26%, transparent);
|
|
10019
|
-
}
|
|
10020
|
-
}
|
|
10021
|
-
`;
|
|
10022
|
-
var PENDING_COPY = {
|
|
10023
|
-
"trending-news": "Composing this section\u2026",
|
|
10024
|
-
chart: "Drawing the comparison\u2026",
|
|
10025
|
-
qa: "Picking the questions worth answering\u2026",
|
|
10026
|
-
regional: "Locating you in time and place\u2026",
|
|
10027
|
-
"peer-feed": "Finding people like you\u2026"
|
|
10028
|
-
};
|
|
10029
|
-
var PENDING_FALLBACK = "Composing\u2026";
|
|
10030
|
-
var PdpSlotLit = class extends LitElement26 {
|
|
10031
|
-
constructor() {
|
|
10032
|
-
super(...arguments);
|
|
10033
|
-
/** DOM id the host's mutation runtime targets when appending the widget. */
|
|
10034
|
-
this.slotId = "";
|
|
10035
|
-
this.kind = "";
|
|
10036
|
-
this.state = "pending";
|
|
10037
|
-
}
|
|
10038
|
-
createRenderRoot() {
|
|
10039
|
-
return this;
|
|
10040
|
-
}
|
|
10041
|
-
connectedCallback() {
|
|
10042
|
-
super.connectedCallback();
|
|
10043
|
-
ensureSlotStyles();
|
|
10044
|
-
}
|
|
10045
|
-
willUpdate() {
|
|
10046
|
-
this.dataset.state = this.state;
|
|
10047
|
-
}
|
|
10048
|
-
render() {
|
|
10049
|
-
const pendingLabel = PENDING_COPY[this.kind] ?? PENDING_FALLBACK;
|
|
10050
|
-
return html26`
|
|
10051
|
-
<div class="syntro-pdp-slot" data-state=${this.state} data-kind=${this.kind}>
|
|
10052
|
-
<div
|
|
10053
|
-
class="syntro-pdp-slot-mount"
|
|
10054
|
-
id=${this.slotId || nothing10}
|
|
10055
|
-
data-pdp-slot=${this.kind}
|
|
10056
|
-
></div>
|
|
10057
|
-
<div class="syntro-pdp-slot-pending" aria-hidden=${this.state !== "pending"}>
|
|
10058
|
-
<div class="syntro-pdp-slot-pending-shimmer" aria-hidden="true"></div>
|
|
10059
|
-
<div class="syntro-pdp-slot-pending-row">
|
|
10060
|
-
<span class="syntro-pdp-slot-pending-dot" aria-hidden="true"></span>
|
|
10061
|
-
<p class="syntro-pdp-slot-pending-label">${pendingLabel}</p>
|
|
10062
|
-
</div>
|
|
10063
|
-
</div>
|
|
10064
|
-
<div class="syntro-pdp-slot-failed" aria-hidden=${this.state !== "failed"}>
|
|
10065
|
-
<p class="syntro-pdp-slot-failed-title">
|
|
10066
|
-
Personalized view couldn't load.
|
|
10067
|
-
</p>
|
|
10068
|
-
<p class="syntro-pdp-slot-failed-body">
|
|
10069
|
-
The standard page still has everything you need.
|
|
10070
|
-
</p>
|
|
10071
|
-
</div>
|
|
10072
|
-
</div>
|
|
10073
|
-
`;
|
|
10074
|
-
}
|
|
10075
|
-
};
|
|
10076
|
-
PdpSlotLit.properties = {
|
|
10077
|
-
slotId: { type: String, attribute: "slot-id" },
|
|
10078
|
-
kind: { type: String },
|
|
10079
|
-
state: { type: String, reflect: true }
|
|
10080
|
-
};
|
|
10081
|
-
if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-slot")) {
|
|
10082
|
-
customElements.define("syntro-pdp-slot", PdpSlotLit);
|
|
10083
|
-
}
|
|
10084
|
-
|
|
10085
10730
|
// src/widgets/PdpQaLit.ts
|
|
10086
10731
|
import { html as html27, LitElement as LitElement27, nothing as nothing11 } from "lit";
|
|
10087
10732
|
function ensureQaStyles() {
|
|
@@ -11050,6 +11695,8 @@ var runtime = {
|
|
|
11050
11695
|
var runtime_default = runtime;
|
|
11051
11696
|
|
|
11052
11697
|
export {
|
|
11698
|
+
detachSection,
|
|
11699
|
+
runTakeover,
|
|
11053
11700
|
_testing,
|
|
11054
11701
|
onActivate,
|
|
11055
11702
|
ProductCardMountable,
|
|
@@ -11089,4 +11736,4 @@ export {
|
|
|
11089
11736
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
11090
11737
|
*)
|
|
11091
11738
|
*/
|
|
11092
|
-
//# sourceMappingURL=chunk-
|
|
11739
|
+
//# sourceMappingURL=chunk-SLHBNATX.js.map
|