@aranova/tracking-react 1.17.0 → 1.19.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/client.js CHANGED
@@ -34,10 +34,15 @@ __export(client_exports, {
34
34
  AdPlatformTracking: () => AdPlatformTracking,
35
35
  BookingHandoffLink: () => BookingHandoffLink,
36
36
  ConsentBanner: () => ConsentBanner,
37
+ DEFAULT_VIDEO_CHECKPOINTS: () => DEFAULT_VIDEO_CHECKPOINTS,
37
38
  GoogleAdsTracking: () => GoogleAdsTracking,
38
39
  PhoneField: () => PhoneField,
40
+ TrackedVideo: () => TrackedVideo,
41
+ TrackedVideoStage: () => TrackedVideoStage,
39
42
  createLeadFormSubmit: () => createLeadFormSubmit,
40
43
  createTracking: () => createTracking,
44
+ streamManifestUrl: () => streamManifestUrl,
45
+ streamPosterUrl: () => streamPosterUrl,
41
46
  useBookingHandoffUrl: () => useBookingHandoffUrl,
42
47
  useBookingIntegration: () => useBookingIntegration,
43
48
  useBookingSlots: () => useBookingSlots,
@@ -48,6 +53,7 @@ __export(client_exports, {
48
53
  useGclid: () => useGclid,
49
54
  usePhoneConfig: () => usePhoneConfig,
50
55
  usePhoneField: () => usePhoneField,
56
+ useTrackedVideoConfig: () => useTrackedVideoConfig,
51
57
  useTrackingParams: () => useTrackingParams
52
58
  });
53
59
  module.exports = __toCommonJS(client_exports);
@@ -307,7 +313,8 @@ var TRACKING_CAPABILITIES = [
307
313
  "form_capture",
308
314
  "phone_click_capture",
309
315
  "phone_fields",
310
- "typed_form_submit"
316
+ "typed_form_submit",
317
+ "video_tracking"
311
318
  ];
312
319
  var CAPABILITY_DOM_ATTRIBUTE = "data-aranova-capability";
313
320
  var registered = /* @__PURE__ */ new Set();
@@ -1088,8 +1095,48 @@ function parseTrigger(value) {
1088
1095
  if (typeof t.threshold_seconds === "number") spec.threshold_seconds = t.threshold_seconds;
1089
1096
  if (typeof t.page_threshold === "number") spec.page_threshold = t.page_threshold;
1090
1097
  if (typeof t.page_name === "string") spec.page_name = t.page_name;
1098
+ if (t.metric === "percent" || t.metric === "seconds") spec.metric = t.metric;
1099
+ if (typeof t.threshold === "number") spec.threshold = t.threshold;
1100
+ if (typeof t.video_id === "string" || t.video_id === null) spec.video_id = t.video_id;
1091
1101
  return spec;
1092
1102
  }
1103
+ function parseVideoEntry(fallbackKey2, entry) {
1104
+ const key = typeof entry.key === "string" && entry.key ? entry.key : fallbackKey2;
1105
+ if (!key) return null;
1106
+ const host = typeof entry.host === "string" ? entry.host : "";
1107
+ const title = typeof entry.title === "string" && entry.title ? entry.title : key;
1108
+ const sourcesRaw = isRecord(entry.sources) ? entry.sources : null;
1109
+ const desktop = sourcesRaw && typeof sourcesRaw.desktop === "string" ? sourcesRaw.desktop : typeof entry.desktop_uid === "string" ? entry.desktop_uid : "";
1110
+ if (!host || !desktop) return null;
1111
+ const mobile = sourcesRaw && typeof sourcesRaw.mobile === "string" ? sourcesRaw.mobile : typeof entry.mobile_uid === "string" ? entry.mobile_uid : null;
1112
+ const posterTime = typeof entry.poster_time === "number" ? entry.poster_time : typeof entry.poster_time_s === "number" ? entry.poster_time_s : 4;
1113
+ return {
1114
+ key,
1115
+ title,
1116
+ host,
1117
+ sources: { desktop, mobile },
1118
+ poster_time: posterTime,
1119
+ duration_s: typeof entry.duration_s === "number" ? entry.duration_s : null
1120
+ };
1121
+ }
1122
+ function parseVideos(value) {
1123
+ const out = {};
1124
+ if (Array.isArray(value)) {
1125
+ for (const item of value) {
1126
+ if (!isRecord(item)) continue;
1127
+ const parsed = parseVideoEntry("", item);
1128
+ if (parsed) out[parsed.key] = parsed;
1129
+ }
1130
+ return out;
1131
+ }
1132
+ if (!isRecord(value)) return out;
1133
+ for (const [key, entry] of Object.entries(value)) {
1134
+ if (!isRecord(entry)) continue;
1135
+ const parsed = parseVideoEntry(key, entry);
1136
+ if (parsed) out[parsed.key] = parsed;
1137
+ }
1138
+ return out;
1139
+ }
1093
1140
  function parseConversionConfig(raw) {
1094
1141
  if (!raw || typeof raw !== "object") return null;
1095
1142
  const obj = raw;
@@ -1140,7 +1187,8 @@ function parseConversionConfig(raw) {
1140
1187
  goals,
1141
1188
  linked_hostnames: Array.isArray(obj.linked_hostnames) ? obj.linked_hostnames.filter((h) => typeof h === "string" && h.length > 0) : [],
1142
1189
  integrations: parseIntegrations(obj.integrations),
1143
- lead_forms: parseLeadForms(obj.lead_forms)
1190
+ lead_forms: parseLeadForms(obj.lead_forms),
1191
+ videos: parseVideos(obj.videos)
1144
1192
  };
1145
1193
  }
1146
1194
  function parseLeadForms(value) {
@@ -1386,6 +1434,21 @@ function automaticThresholdMet(goal, eventType, metadata) {
1386
1434
  case "form_start":
1387
1435
  case "phone_click":
1388
1436
  return true;
1437
+ case "video_play":
1438
+ return trigger.video_id == null || hasStringField(metadata, "video_id") && metadata.video_id === trigger.video_id;
1439
+ case "video_progress": {
1440
+ if (trigger.video_id != null && (!hasStringField(metadata, "video_id") || metadata.video_id !== trigger.video_id)) {
1441
+ return false;
1442
+ }
1443
+ if (typeof trigger.threshold !== "number") return false;
1444
+ if (trigger.metric === "seconds") {
1445
+ return hasNumberField(metadata, "watched_s") && metadata.watched_s >= trigger.threshold;
1446
+ }
1447
+ if (trigger.metric === "percent") {
1448
+ return hasNumberField(metadata, "max_percent") && metadata.max_percent >= trigger.threshold || hasNumberField(metadata, "watched_percent") && metadata.watched_percent >= trigger.threshold;
1449
+ }
1450
+ return false;
1451
+ }
1389
1452
  default:
1390
1453
  return false;
1391
1454
  }
@@ -1422,6 +1485,9 @@ var RETRY_BASE_MS = 5e3;
1422
1485
  var RETRY_MAX_MS = 5 * 6e4;
1423
1486
  var runtimes = /* @__PURE__ */ new Map();
1424
1487
  var configuredIds = /* @__PURE__ */ new Set();
1488
+ var EMPTY_GOALS = [];
1489
+ var EMPTY_STRINGS = [];
1490
+ var EMPTY_VIDEOS = {};
1425
1491
  var scriptLoad = null;
1426
1492
  var jsInitialized = false;
1427
1493
  function cacheKey2(url) {
@@ -1612,11 +1678,11 @@ var TrackingConfigRuntime = class {
1612
1678
  void this.flush();
1613
1679
  }
1614
1680
  listGoals() {
1615
- return this.current?.goals ?? [];
1681
+ return this.current?.goals ?? EMPTY_GOALS;
1616
1682
  }
1617
1683
  /** Sibling hostnames for the linker — read from the cached object even while unconfirmed. */
1618
1684
  linkedHostnames() {
1619
- return this.current?.linked_hostnames ?? [];
1685
+ return this.current?.linked_hostnames ?? EMPTY_STRINGS;
1620
1686
  }
1621
1687
  /**
1622
1688
  * Integration blocks, from the cached object even while unconfirmed: the booking fallback
@@ -1629,6 +1695,14 @@ var TrackingConfigRuntime = class {
1629
1695
  leadForms() {
1630
1696
  return this.current?.lead_forms ?? null;
1631
1697
  }
1698
+ /** Configured Cloudflare Stream tracked videos for this site. */
1699
+ videos() {
1700
+ return this.current?.videos ?? EMPTY_VIDEOS;
1701
+ }
1702
+ /** Look up one configured Cloudflare Stream tracked video by key. */
1703
+ getVideo(key) {
1704
+ return this.current?.videos?.[key] ?? null;
1705
+ }
1632
1706
  async revalidateNow() {
1633
1707
  try {
1634
1708
  const headers = {};
@@ -1785,6 +1859,16 @@ var TrackingConfigRuntime = class {
1785
1859
  this.scheduleRetry();
1786
1860
  return;
1787
1861
  }
1862
+ if (outcome === "fired" && typeof window !== "undefined" && typeof window.fbq === "function") {
1863
+ try {
1864
+ window.fbq("trackCustom", item.key, {
1865
+ ...value != null ? { value } : {},
1866
+ ...currency ? { currency } : {},
1867
+ ...item.transactionId ? { transaction_id: item.transactionId } : {}
1868
+ });
1869
+ } catch {
1870
+ }
1871
+ }
1788
1872
  this.conversionQueue.shift();
1789
1873
  }
1790
1874
  }
@@ -1928,12 +2012,24 @@ function createConversionAutoFire(store) {
1928
2012
  const firing = goal.firing;
1929
2013
  const cents = firing.value_cents ?? null;
1930
2014
  const currency = firing.currency ?? null;
1931
- fireConversionWithConsent({
2015
+ const value = cents != null && currency ? fromMinor(cents, currency) : null;
2016
+ const outcome = fireConversionWithConsent({
1932
2017
  sendTo: firing.send_to,
1933
- value: cents != null && currency ? fromMinor(cents, currency) : null,
2018
+ value,
1934
2019
  currency,
1935
2020
  transactionId
1936
2021
  });
2022
+ if (outcome === "fired" && typeof window !== "undefined" && typeof window.fbq === "function") {
2023
+ try {
2024
+ window.fbq("trackCustom", goal.key, {
2025
+ event_type: eventType,
2026
+ ...value != null ? { value } : {},
2027
+ ...currency ? { currency } : {},
2028
+ transaction_id: transactionId
2029
+ });
2030
+ } catch {
2031
+ }
2032
+ }
1937
2033
  }
1938
2034
  }
1939
2035
  return {
@@ -2030,6 +2126,13 @@ var EVENT_SEMANTICS = {
2030
2126
  outcomeRole: "engagement",
2031
2127
  clientVisibility: "detailed"
2032
2128
  },
2129
+ // Adds to the submit it names; counting it as a lead would double every lead.
2130
+ form_details: {
2131
+ label: "Form details added",
2132
+ category: "lead",
2133
+ outcomeRole: "engagement",
2134
+ clientVisibility: "detailed"
2135
+ },
2033
2136
  form_submit: {
2034
2137
  label: "Form submitted",
2035
2138
  category: "lead",
@@ -2059,6 +2162,18 @@ var EVENT_SEMANTICS = {
2059
2162
  category: "page",
2060
2163
  outcomeRole: "navigation",
2061
2164
  clientVisibility: "simple"
2165
+ },
2166
+ video_play: {
2167
+ label: "Video played",
2168
+ category: "engagement",
2169
+ outcomeRole: "engagement",
2170
+ clientVisibility: "simple"
2171
+ },
2172
+ video_progress: {
2173
+ label: "Video watched",
2174
+ category: "engagement",
2175
+ outcomeRole: "engagement",
2176
+ clientVisibility: "detailed"
2062
2177
  }
2063
2178
  };
2064
2179
  function getEventSemantics(eventName) {
@@ -2646,14 +2761,31 @@ function createTrackingClient(config) {
2646
2761
  metadata: input.metadata ?? null,
2647
2762
  occurred_at: occurredAt
2648
2763
  });
2649
- if (queue.length >= maxQueueSize || getEventSemantics(input.eventType)?.outcomeRole === "lead") {
2764
+ const triggerReason = input.metadata?.trigger;
2765
+ const isUrgentVideoEvent = input.eventType === "video_play" || input.eventType === "video_progress" && (triggerReason === "ended" || triggerReason === "goal" || triggerReason === "checkpoint");
2766
+ if (queue.length >= maxQueueSize || input.eventType === "form_details" || getEventSemantics(input.eventType)?.outcomeRole === "lead" || isUrgentVideoEvent) {
2650
2767
  void flush();
2651
2768
  } else {
2652
2769
  scheduleFlush();
2653
2770
  }
2654
2771
  }
2772
+ const beforeUnloadListeners = /* @__PURE__ */ new Set();
2773
+ let runningBeforeUnload = false;
2655
2774
  function flushOnUnload() {
2656
2775
  if (destroyed) return;
2776
+ if (!runningBeforeUnload && beforeUnloadListeners.size > 0) {
2777
+ runningBeforeUnload = true;
2778
+ try {
2779
+ for (const listener of beforeUnloadListeners) {
2780
+ try {
2781
+ listener();
2782
+ } catch {
2783
+ }
2784
+ }
2785
+ } finally {
2786
+ runningBeforeUnload = false;
2787
+ }
2788
+ }
2657
2789
  bufferQueued();
2658
2790
  clearScheduledFlush();
2659
2791
  if (pending.length === 0) return;
@@ -2674,6 +2806,12 @@ function createTrackingClient(config) {
2674
2806
  trackEvent,
2675
2807
  flush,
2676
2808
  flushBeacon: flushOnUnload,
2809
+ onBeforeUnload: (listener) => {
2810
+ beforeUnloadListeners.add(listener);
2811
+ return () => {
2812
+ beforeUnloadListeners.delete(listener);
2813
+ };
2814
+ },
2677
2815
  getSessionId: () => sessionId,
2678
2816
  getVisitorId: () => visitorId,
2679
2817
  destroy: () => {
@@ -2684,6 +2822,7 @@ function createTrackingClient(config) {
2684
2822
  destroyed = true;
2685
2823
  clearScheduledFlush();
2686
2824
  queue = [];
2825
+ beforeUnloadListeners.clear();
2687
2826
  if (typeof window !== "undefined") {
2688
2827
  window.removeEventListener("pagehide", flushOnUnload);
2689
2828
  window.removeEventListener("visibilitychange", onVisibilityChange);
@@ -2750,110 +2889,133 @@ var formStartConfigSchema = z5.strictObject({
2750
2889
  selector: z5.optional(z5.string())
2751
2890
  });
2752
2891
 
2753
- // ../tracking-core/src/events/form-step.ts
2892
+ // ../tracking-core/src/events/form-details.ts
2893
+ var z7 = __toESM(require("zod/mini"));
2894
+
2895
+ // ../tracking-core/src/events/form-submit.ts
2754
2896
  var z6 = __toESM(require("zod/mini"));
2755
- var formStepMetadataSchema = z6.strictObject({
2897
+ var jsonValueSchema = z6.lazy(
2898
+ () => z6.union([
2899
+ z6.string(),
2900
+ z6.number(),
2901
+ z6.boolean(),
2902
+ z6.null(),
2903
+ z6.array(jsonValueSchema),
2904
+ z6.record(z6.string(), jsonValueSchema)
2905
+ ])
2906
+ ).register(z6.globalRegistry, { id: "JsonSerializableValue" });
2907
+ var contractNameSchema = z6.string().check(z6.regex(/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/));
2908
+ var violationRole = z6.string().check(
2909
+ z6.maxLength(200),
2910
+ z6.regex(
2911
+ /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?(\|[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?)*$/
2912
+ )
2913
+ );
2914
+ var MAX_CONTRACT_VIOLATIONS = 50;
2915
+ var contractViolationSchema = z6.strictObject({
2916
+ role: z6.nullable(violationRole),
2917
+ rule: z6.string().check(z6.regex(/^[a-z][a-z_]{0,47}$/))
2918
+ });
2919
+ var formFieldSchema = z6.strictObject({
2920
+ name: z6.string(),
2921
+ type: z6.string(),
2922
+ label: z6.nullable(z6.string()),
2923
+ value: jsonValueSchema,
2924
+ role: z6.optional(z6.nullable(contractNameSchema))
2925
+ });
2926
+ var formExtraSchema = z6.optional(
2927
+ z6.nullable(
2928
+ z6.array(
2929
+ z6.strictObject({
2930
+ key: z6.string().check(z6.maxLength(64)),
2931
+ label: z6.string().check(z6.maxLength(200)),
2932
+ value: jsonValueSchema
2933
+ })
2934
+ ).check(z6.maxLength(20))
2935
+ )
2936
+ );
2937
+ var contractVersionSchema = z6.optional(z6.nullable(z6.int().check(z6.gte(1))));
2938
+ var contractHashSchema = z6.optional(z6.nullable(z6.string().check(z6.maxLength(64))));
2939
+ var formContractViolationsSchema = z6.optional(
2940
+ z6.nullable(z6.array(contractViolationSchema).check(z6.maxLength(MAX_CONTRACT_VIOLATIONS)))
2941
+ );
2942
+ var formSubmitMetadataSchema = z6.strictObject({
2756
2943
  form: z6.strictObject({
2757
2944
  id: z6.string(),
2758
- action: z6.nullable(z6.string())
2759
- }),
2760
- step: z6.strictObject({
2761
- id: z6.string().check(z6.minLength(1), z6.maxLength(64), z6.regex(/^[A-Za-z0-9_-]+$/)),
2762
- // 1-based position in the flow the visitor is on right now.
2763
- index: z6.int().check(z6.gte(1), z6.lte(50)),
2764
- // Null when the number of steps depends on earlier answers.
2765
- total: z6.nullable(z6.int().check(z6.gte(1), z6.lte(50)))
2945
+ action: z6.nullable(z6.string()),
2946
+ fields: z6.optional(z6.array(formFieldSchema)),
2947
+ kind: z6.optional(z6.nullable(contractNameSchema)),
2948
+ contract_version: contractVersionSchema,
2949
+ contract_hash: contractHashSchema,
2950
+ extra: formExtraSchema,
2951
+ contract_violations: formContractViolationsSchema,
2952
+ // Minted per submit; a later `form_details` names it to add answers to this lead.
2953
+ submission_id: z6.optional(z6.nullable(z6.uuidv4()))
2766
2954
  }),
2767
- direction: z6.enum(["forward", "back"]),
2768
2955
  page: z6.strictObject({
2769
2956
  path: z6.string()
2770
2957
  })
2771
2958
  });
2772
- var formStepConfigSchema = z6.strictObject({});
2959
+ var formSubmitConfigSchema = z6.strictObject({});
2773
2960
 
2774
- // ../tracking-core/src/events/form-submit.ts
2775
- var z7 = __toESM(require("zod/mini"));
2776
- var jsonValueSchema = z7.lazy(
2777
- () => z7.union([
2778
- z7.string(),
2779
- z7.number(),
2780
- z7.boolean(),
2781
- z7.null(),
2782
- z7.array(jsonValueSchema),
2783
- z7.record(z7.string(), jsonValueSchema)
2784
- ])
2785
- ).register(z7.globalRegistry, { id: "JsonSerializableValue" });
2786
- var contractNameSchema = z7.string().check(z7.regex(/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/));
2787
- var contractName = contractNameSchema;
2788
- var violationRole = z7.string().check(
2789
- z7.maxLength(200),
2790
- z7.regex(
2791
- /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?(\|[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?)*$/
2792
- )
2793
- );
2794
- var MAX_CONTRACT_VIOLATIONS = 50;
2795
- var contractViolationSchema = z7.strictObject({
2796
- role: z7.nullable(violationRole),
2797
- rule: z7.string().check(z7.regex(/^[a-z][a-z_]{0,47}$/))
2798
- });
2799
- var formSubmitMetadataSchema = z7.strictObject({
2961
+ // ../tracking-core/src/events/form-details.ts
2962
+ var formDetailsMetadataSchema = z7.strictObject({
2800
2963
  form: z7.strictObject({
2801
2964
  id: z7.string(),
2802
- action: z7.nullable(z7.string()),
2803
- fields: z7.optional(
2804
- z7.array(
2805
- z7.strictObject({
2806
- name: z7.string(),
2807
- type: z7.string(),
2808
- label: z7.nullable(z7.string()),
2809
- value: jsonValueSchema,
2810
- role: z7.optional(z7.nullable(contractName))
2811
- })
2812
- )
2813
- ),
2814
- kind: z7.optional(z7.nullable(contractName)),
2815
- contract_version: z7.optional(z7.nullable(z7.int().check(z7.gte(1)))),
2816
- contract_hash: z7.optional(z7.nullable(z7.string().check(z7.maxLength(64)))),
2817
- extra: z7.optional(
2818
- z7.nullable(
2819
- z7.array(
2820
- z7.strictObject({
2821
- key: z7.string().check(z7.maxLength(64)),
2822
- label: z7.string().check(z7.maxLength(200)),
2823
- value: jsonValueSchema
2824
- })
2825
- ).check(z7.maxLength(20))
2826
- )
2827
- ),
2828
- contract_violations: z7.optional(
2829
- z7.nullable(z7.array(contractViolationSchema).check(z7.maxLength(MAX_CONTRACT_VIOLATIONS)))
2830
- )
2965
+ submission_id: z7.uuidv4(),
2966
+ kind: z7.optional(z7.nullable(contractNameSchema)),
2967
+ fields: z7.array(formFieldSchema),
2968
+ contract_version: contractVersionSchema,
2969
+ contract_hash: contractHashSchema,
2970
+ extra: formExtraSchema,
2971
+ contract_violations: formContractViolationsSchema
2831
2972
  }),
2832
2973
  page: z7.strictObject({
2833
2974
  path: z7.string()
2834
2975
  })
2835
2976
  });
2836
- var formSubmitConfigSchema = z7.strictObject({});
2977
+ var formDetailsConfigSchema = z7.strictObject({});
2837
2978
 
2838
- // ../tracking-core/src/events/multi-page-session.ts
2979
+ // ../tracking-core/src/events/form-step.ts
2839
2980
  var z8 = __toESM(require("zod/mini"));
2840
- var multiPageSessionMetadataSchema = z8.strictObject({
2841
- page_count: z8.int().check(z8.gte(2)),
2981
+ var formStepMetadataSchema = z8.strictObject({
2982
+ form: z8.strictObject({
2983
+ id: z8.string(),
2984
+ action: z8.nullable(z8.string())
2985
+ }),
2986
+ step: z8.strictObject({
2987
+ id: z8.string().check(z8.minLength(1), z8.maxLength(64), z8.regex(/^[A-Za-z0-9_-]+$/)),
2988
+ // 1-based position in the flow the visitor is on right now.
2989
+ index: z8.int().check(z8.gte(1), z8.lte(50)),
2990
+ // Null when the number of steps depends on earlier answers.
2991
+ total: z8.nullable(z8.int().check(z8.gte(1), z8.lte(50)))
2992
+ }),
2993
+ direction: z8.enum(["forward", "back"]),
2842
2994
  page: z8.strictObject({
2843
2995
  path: z8.string()
2844
2996
  })
2845
2997
  });
2846
- var multiPageSessionConfigSchema = z8.strictObject({
2847
- pageThreshold: z8.int().check(z8.gte(2))
2998
+ var formStepConfigSchema = z8.strictObject({});
2999
+
3000
+ // ../tracking-core/src/events/multi-page-session.ts
3001
+ var z9 = __toESM(require("zod/mini"));
3002
+ var multiPageSessionMetadataSchema = z9.strictObject({
3003
+ page_count: z9.int().check(z9.gte(2)),
3004
+ page: z9.strictObject({
3005
+ path: z9.string()
3006
+ })
3007
+ });
3008
+ var multiPageSessionConfigSchema = z9.strictObject({
3009
+ pageThreshold: z9.int().check(z9.gte(2))
2848
3010
  });
2849
3011
 
2850
3012
  // ../tracking-core/src/events/page-exit.ts
2851
- var z9 = __toESM(require("zod/mini"));
2852
- var pageExitMetadataSchema = z9.strictObject({
2853
- dwell_ms: z9.int().check(z9.gte(0)),
3013
+ var z10 = __toESM(require("zod/mini"));
3014
+ var pageExitMetadataSchema = z10.strictObject({
3015
+ dwell_ms: z10.int().check(z10.gte(0)),
2854
3016
  // null = left without any scroll signal; floor is 0 so a valid 0% is never
2855
3017
  // rejected (a single bad field 422s the whole keepalive beacon batch).
2856
- max_scroll_percent: z9.nullable(z9.int().check(z9.gte(0), z9.lte(100))),
3018
+ max_scroll_percent: z10.nullable(z10.int().check(z10.gte(0), z10.lte(100))),
2857
3019
  // The gating baseline: the fraction of the page visible at load with
2858
3020
  // zero scrolling — or, for pages that grew after the post-paint snapshot
2859
3021
  // (skeleton/streaming renders), the first-scroll position that
@@ -2861,106 +3023,186 @@ var pageExitMetadataSchema = z9.strictObject({
2861
3023
  // ended before the snapshot. Optionality is load-bearing: SDK builds
2862
3024
  // predating this field keep POSTing page_exit without the key —
2863
3025
  // requiring it would 422 whole keepalive beacon batches.
2864
- scroll_baseline_percent: z9.optional(z9.nullable(z9.int().check(z9.gte(0), z9.lte(100)))),
2865
- page: z9.strictObject({
2866
- path: z9.string()
3026
+ scroll_baseline_percent: z10.optional(z10.nullable(z10.int().check(z10.gte(0), z10.lte(100)))),
3027
+ page: z10.strictObject({
3028
+ path: z10.string()
2867
3029
  })
2868
3030
  });
2869
- var pageExitConfigSchema = z9.strictObject({});
3031
+ var pageExitConfigSchema = z10.strictObject({});
2870
3032
 
2871
3033
  // ../tracking-core/src/events/phone-click.ts
2872
- var z10 = __toESM(require("zod/mini"));
2873
- var phoneClickMetadataSchema = z10.strictObject({
2874
- phone_number: z10.string(),
2875
- page: z10.strictObject({
2876
- path: z10.string()
3034
+ var z11 = __toESM(require("zod/mini"));
3035
+ var phoneClickMetadataSchema = z11.strictObject({
3036
+ phone_number: z11.string(),
3037
+ page: z11.strictObject({
3038
+ path: z11.string()
2877
3039
  }),
2878
- section: z10.optional(z10.nullable(z10.string()))
3040
+ section: z11.optional(z11.nullable(z11.string()))
2879
3041
  });
2880
- var phoneClickConfigSchema = z10.strictObject({
2881
- autoCapture: z10.optional(
2882
- z10.strictObject({
2883
- selector: z10.optional(z10.string())
3042
+ var phoneClickConfigSchema = z11.strictObject({
3043
+ autoCapture: z11.optional(
3044
+ z11.strictObject({
3045
+ selector: z11.optional(z11.string())
2884
3046
  })
2885
3047
  )
2886
3048
  });
2887
3049
 
2888
3050
  // ../tracking-core/src/events/scroll-depth.ts
2889
- var z11 = __toESM(require("zod/mini"));
2890
- var scrollDepthMetadataSchema = z11.strictObject({
2891
- depth_percent: z11.int().check(z11.gte(1), z11.lte(100)),
2892
- page: z11.strictObject({
2893
- path: z11.string()
3051
+ var z12 = __toESM(require("zod/mini"));
3052
+ var scrollDepthMetadataSchema = z12.strictObject({
3053
+ depth_percent: z12.int().check(z12.gte(1), z12.lte(100)),
3054
+ page: z12.strictObject({
3055
+ path: z12.string()
2894
3056
  })
2895
3057
  });
2896
- var scrollDepthConfigSchema = z11.strictObject({
2897
- thresholds: z11.array(z11.int().check(z11.gte(1), z11.lte(100))).check(z11.minLength(1))
3058
+ var scrollDepthConfigSchema = z12.strictObject({
3059
+ thresholds: z12.array(z12.int().check(z12.gte(1), z12.lte(100))).check(z12.minLength(1))
2898
3060
  });
2899
3061
 
2900
3062
  // ../tracking-core/src/events/specific-page-visit.ts
2901
- var z12 = __toESM(require("zod/mini"));
3063
+ var z13 = __toESM(require("zod/mini"));
2902
3064
  var PAGE_IDENTITY_MAX_LENGTH = 64;
2903
3065
  var PAGE_IDENTITY_PATTERN = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/;
2904
- var pageIdentitySchema = z12.string().check(
2905
- z12.maxLength(PAGE_IDENTITY_MAX_LENGTH),
2906
- z12.regex(PAGE_IDENTITY_PATTERN, {
3066
+ var pageIdentitySchema = z13.string().check(
3067
+ z13.maxLength(PAGE_IDENTITY_MAX_LENGTH),
3068
+ z13.regex(PAGE_IDENTITY_PATTERN, {
2907
3069
  message: "page identity must be lowercase snake_case, start with a letter, and contain at most 64 characters"
2908
3070
  })
2909
3071
  );
2910
3072
  var specificPageNameSchema = pageIdentitySchema;
2911
- var specificPageVisitMetadataSchema = z12.strictObject({
3073
+ var specificPageVisitMetadataSchema = z13.strictObject({
2912
3074
  page_name: specificPageNameSchema,
2913
- page: z12.strictObject({
2914
- path: z12.string()
3075
+ page: z13.strictObject({
3076
+ path: z13.string()
2915
3077
  })
2916
3078
  });
2917
- var specificPageVisitConfigSchema = z12.strictObject({
2918
- pages: z12.array(
2919
- z12.strictObject({
3079
+ var specificPageVisitConfigSchema = z13.strictObject({
3080
+ pages: z13.array(
3081
+ z13.strictObject({
2920
3082
  name: specificPageNameSchema,
2921
- pathPattern: z12.custom((value) => value instanceof RegExp, {
3083
+ pathPattern: z13.custom((value) => value instanceof RegExp, {
2922
3084
  message: "pathPattern must be a RegExp"
2923
3085
  })
2924
3086
  })
2925
- ).check(z12.minLength(1))
3087
+ ).check(z13.minLength(1))
2926
3088
  });
2927
3089
 
2928
3090
  // ../tracking-core/src/events/site-link.ts
2929
- var z13 = __toESM(require("zod/mini"));
2930
- var siteLinkClickMetadataSchema = z13.strictObject({
2931
- destination_host: z13.string(),
2932
- destination_url: z13.string(),
2933
- page: z13.strictObject({
2934
- path: z13.string()
3091
+ var z14 = __toESM(require("zod/mini"));
3092
+ var siteLinkClickMetadataSchema = z14.strictObject({
3093
+ destination_host: z14.string(),
3094
+ destination_url: z14.string(),
3095
+ page: z14.strictObject({
3096
+ path: z14.string()
2935
3097
  })
2936
3098
  });
2937
- var siteLinkClickConfigSchema = z13.strictObject({});
2938
- var siteLinkArrivalMetadataSchema = z13.strictObject({
2939
- outcome: z13.enum(["linked", "stale", "invalid"]),
2940
- origin_session_id: z13.optional(z13.nullable(z13.string())),
2941
- origin_visitor_id: z13.optional(z13.nullable(z13.string())),
3099
+ var siteLinkClickConfigSchema = z14.strictObject({});
3100
+ var siteLinkArrivalMetadataSchema = z14.strictObject({
3101
+ outcome: z14.enum(["linked", "stale", "invalid"]),
3102
+ origin_session_id: z14.optional(z14.nullable(z14.string())),
3103
+ origin_visitor_id: z14.optional(z14.nullable(z14.string())),
2942
3104
  // Milliseconds between the origin click and this page load.
2943
- transit_ms: z13.optional(z13.nullable(z13.number())),
3105
+ transit_ms: z14.optional(z14.nullable(z14.number())),
2944
3106
  // True when this browser had no visitor id and took the origin's.
2945
- adopted_visitor: z13.boolean(),
2946
- page: z13.strictObject({
2947
- path: z13.string()
3107
+ adopted_visitor: z14.boolean(),
3108
+ page: z14.strictObject({
3109
+ path: z14.string()
2948
3110
  })
2949
3111
  });
2950
- var siteLinkArrivalConfigSchema = z13.strictObject({});
3112
+ var siteLinkArrivalConfigSchema = z14.strictObject({});
2951
3113
 
2952
3114
  // ../tracking-core/src/events/time-on-site.ts
2953
- var z14 = __toESM(require("zod/mini"));
2954
- var timeOnSiteMetadataSchema = z14.strictObject({
2955
- duration_ms: z14.int().check(z14.nonnegative()),
2956
- page: z14.strictObject({
2957
- path: z14.string()
3115
+ var z15 = __toESM(require("zod/mini"));
3116
+ var timeOnSiteMetadataSchema = z15.strictObject({
3117
+ duration_ms: z15.int().check(z15.nonnegative()),
3118
+ page: z15.strictObject({
3119
+ path: z15.string()
2958
3120
  })
2959
3121
  });
2960
- var timeOnSiteConfigSchema = z14.strictObject({
2961
- thresholdSeconds: z14.int().check(z14.positive())
3122
+ var timeOnSiteConfigSchema = z15.strictObject({
3123
+ thresholdSeconds: z15.int().check(z15.positive())
2962
3124
  });
2963
3125
 
3126
+ // ../tracking-core/src/events/video.ts
3127
+ var z16 = __toESM(require("zod/mini"));
3128
+
3129
+ // ../tracking-core/src/video-ranges.ts
3130
+ var VIDEO_ID_MAX_LENGTH = 64;
3131
+ var VIDEO_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
3132
+ function roundVideoSeconds(value) {
3133
+ if (!Number.isFinite(value) || value < 0) return 0;
3134
+ return Math.round(value * 100) / 100;
3135
+ }
3136
+ function mergeVideoRanges(ranges, epsilonSeconds = 0.25) {
3137
+ const valid2 = ranges.filter((r) => Number.isFinite(r.start_s) && Number.isFinite(r.end_s) && r.end_s > r.start_s).map((r) => ({
3138
+ start_s: roundVideoSeconds(r.start_s),
3139
+ end_s: roundVideoSeconds(r.end_s)
3140
+ })).filter((r) => r.end_s - r.start_s >= 0.05).sort((a, b) => a.start_s - b.start_s || a.end_s - b.end_s);
3141
+ if (valid2.length === 0) return [];
3142
+ const merged = [{ ...valid2[0] }];
3143
+ for (let i = 1; i < valid2.length; i += 1) {
3144
+ const cur = valid2[i];
3145
+ const last = merged[merged.length - 1];
3146
+ if (cur.start_s <= last.end_s + epsilonSeconds) {
3147
+ last.end_s = roundVideoSeconds(Math.max(last.end_s, cur.end_s));
3148
+ } else {
3149
+ merged.push({ ...cur });
3150
+ }
3151
+ }
3152
+ return merged;
3153
+ }
3154
+ function sumVideoRangesSeconds(ranges, durationSeconds = 0) {
3155
+ const merged = mergeVideoRanges(ranges);
3156
+ const raw = merged.reduce((acc, r) => acc + Math.max(0, r.end_s - r.start_s), 0);
3157
+ const clamped = durationSeconds > 0 ? Math.min(raw, durationSeconds) : raw;
3158
+ return roundVideoSeconds(clamped);
3159
+ }
3160
+
3161
+ // ../tracking-core/src/events/video.ts
3162
+ var videoIdSchema = z16.string().check(z16.minLength(1), z16.maxLength(VIDEO_ID_MAX_LENGTH), z16.regex(VIDEO_ID_PATTERN));
3163
+ var videoVariantSchema = z16.enum(["16x9", "9x16"]);
3164
+ var videoProgressTriggerReasonSchema = z16.enum([
3165
+ "checkpoint",
3166
+ "goal",
3167
+ "pause",
3168
+ "seek",
3169
+ "ended",
3170
+ "page_exit"
3171
+ ]);
3172
+ var videoRangeSchema = z16.strictObject({
3173
+ start_s: z16.number().check(z16.gte(0)),
3174
+ end_s: z16.number().check(z16.gte(0))
3175
+ });
3176
+ var videoPlayMetadataSchema = z16.strictObject({
3177
+ video_id: videoIdSchema,
3178
+ variant: videoVariantSchema,
3179
+ current_s: z16.number().check(z16.gte(0)),
3180
+ duration_s: z16.number().check(z16.gte(0)),
3181
+ play_sequence: z16.int().check(z16.gte(1)),
3182
+ page: z16.strictObject({
3183
+ path: z16.string()
3184
+ })
3185
+ });
3186
+ var videoPlayConfigSchema = z16.strictObject({});
3187
+ var videoProgressMetadataSchema = z16.strictObject({
3188
+ video_id: videoIdSchema,
3189
+ variant: videoVariantSchema,
3190
+ trigger: videoProgressTriggerReasonSchema,
3191
+ current_s: z16.number().check(z16.gte(0)),
3192
+ furthest_s: z16.number().check(z16.gte(0)),
3193
+ watched_s: z16.number().check(z16.gte(0)),
3194
+ duration_s: z16.number().check(z16.gte(0)),
3195
+ max_percent: z16.int().check(z16.gte(0), z16.lte(100)),
3196
+ watched_percent: z16.int().check(z16.gte(0), z16.lte(100)),
3197
+ play_count: z16.int().check(z16.gte(1)),
3198
+ seek_count: z16.int().check(z16.gte(0)),
3199
+ ranges: z16.array(videoRangeSchema),
3200
+ page: z16.strictObject({
3201
+ path: z16.string()
3202
+ })
3203
+ });
3204
+ var videoProgressConfigSchema = z16.strictObject({});
3205
+
2964
3206
  // ../tracking-core/src/events/registry.ts
2965
3207
  var EVENT_REGISTRY = {
2966
3208
  // --- automatic triggers ---
@@ -3027,6 +3269,18 @@ var EVENT_REGISTRY = {
3027
3269
  metadataSchema: siteLinkArrivalMetadataSchema,
3028
3270
  configSchema: siteLinkArrivalConfigSchema
3029
3271
  },
3272
+ video_play: {
3273
+ kind: "automatic",
3274
+ semantics: EVENT_SEMANTICS.video_play,
3275
+ metadataSchema: videoPlayMetadataSchema,
3276
+ configSchema: videoPlayConfigSchema
3277
+ },
3278
+ video_progress: {
3279
+ kind: "automatic",
3280
+ semantics: EVENT_SEMANTICS.video_progress,
3281
+ metadataSchema: videoProgressMetadataSchema,
3282
+ configSchema: videoProgressConfigSchema
3283
+ },
3030
3284
  // --- manual triggers ---
3031
3285
  form_submit: {
3032
3286
  kind: "manual",
@@ -3040,6 +3294,12 @@ var EVENT_REGISTRY = {
3040
3294
  metadataSchema: formStepMetadataSchema,
3041
3295
  configSchema: formStepConfigSchema
3042
3296
  },
3297
+ form_details: {
3298
+ kind: "manual",
3299
+ semantics: EVENT_SEMANTICS.form_details,
3300
+ metadataSchema: formDetailsMetadataSchema,
3301
+ configSchema: formDetailsConfigSchema
3302
+ },
3043
3303
  phone_click: {
3044
3304
  kind: "manual",
3045
3305
  semantics: EVENT_SEMANTICS.phone_click,
@@ -3078,6 +3338,7 @@ function salvageFormSubmit(metadata) {
3078
3338
  ...valid("contract_version", form.contract_version),
3079
3339
  ...valid("contract_hash", form.contract_hash),
3080
3340
  ...valid("extra", form.extra),
3341
+ ...valid("submission_id", form.submission_id),
3081
3342
  contract_violations: [...reported, INVALID_SHAPE].slice(-MAX_CONTRACT_VIOLATIONS)
3082
3343
  },
3083
3344
  page: { path: typeof page.path === "string" ? page.path : currentPath2() }
@@ -3120,6 +3381,17 @@ function currentPath2() {
3120
3381
 
3121
3382
  // ../tracking-core/src/ingest-typed.ts
3122
3383
  var clientSettings = /* @__PURE__ */ new WeakMap();
3384
+ var latestTrackingConfig;
3385
+ var latestRawClient = null;
3386
+ function setDefaultTrackingConfig(ref) {
3387
+ if (ref) latestTrackingConfig = ref;
3388
+ }
3389
+ function getActiveTrackingEnv() {
3390
+ return {
3391
+ rawClient: latestRawClient,
3392
+ trackingConfig: latestTrackingConfig
3393
+ };
3394
+ }
3123
3395
  function typedClientSettings(client) {
3124
3396
  return clientSettings.get(client) ?? {};
3125
3397
  }
@@ -3150,7 +3422,9 @@ function createTypedClient(raw, registry, options = {}) {
3150
3422
  getSessionId: raw.getSessionId.bind(raw),
3151
3423
  getVisitorId: raw.getVisitorId.bind(raw)
3152
3424
  };
3153
- clientSettings.set(client, options);
3425
+ latestRawClient = raw;
3426
+ if (options.trackingConfig) latestTrackingConfig = options.trackingConfig;
3427
+ clientSettings.set(client, { ...options, rawClient: raw });
3154
3428
  return client;
3155
3429
  }
3156
3430
 
@@ -3358,7 +3632,7 @@ function attachScrollDepth(client, config) {
3358
3632
  }
3359
3633
  const thresholds = new Set(config.thresholds);
3360
3634
  let firedForPath = /* @__PURE__ */ new Set();
3361
- let currentPath3 = window.location.pathname;
3635
+ let currentPath4 = window.location.pathname;
3362
3636
  let rafId = null;
3363
3637
  const gate = createBaselineGate();
3364
3638
  function checkThresholds() {
@@ -3374,7 +3648,7 @@ function attachScrollDepth(client, config) {
3374
3648
  eventType: "scroll_depth",
3375
3649
  metadata: {
3376
3650
  depth_percent: threshold,
3377
- page: { path: currentPath3 }
3651
+ page: { path: currentPath4 }
3378
3652
  },
3379
3653
  pageUrl: window.location.href,
3380
3654
  occurredAt: null
@@ -3391,8 +3665,8 @@ function attachScrollDepth(client, config) {
3391
3665
  }
3392
3666
  function resetIfPathChanged() {
3393
3667
  const newPath = window.location.pathname;
3394
- if (newPath === currentPath3) return;
3395
- currentPath3 = newPath;
3668
+ if (newPath === currentPath4) return;
3669
+ currentPath4 = newPath;
3396
3670
  firedForPath = /* @__PURE__ */ new Set();
3397
3671
  gate.rebaseline();
3398
3672
  }
@@ -3455,13 +3729,13 @@ function attachMultiPageSession(client, config) {
3455
3729
  return storage.getItem(FIRED_KEY) === "1";
3456
3730
  }
3457
3731
  function check2() {
3458
- const currentPath3 = window.location.pathname;
3459
- if (currentPath3 === lastCheckedPath) return;
3460
- lastCheckedPath = currentPath3;
3732
+ const currentPath4 = window.location.pathname;
3733
+ if (currentPath4 === lastCheckedPath) return;
3734
+ lastCheckedPath = currentPath4;
3461
3735
  resetIfSessionChanged();
3462
3736
  if (hasFired()) return;
3463
3737
  const paths = getDistinctPaths();
3464
- paths.add(currentPath3);
3738
+ paths.add(currentPath4);
3465
3739
  saveDistinctPaths(paths);
3466
3740
  if (paths.size >= pageThreshold) {
3467
3741
  storage.setItem(FIRED_KEY, "1");
@@ -3506,7 +3780,7 @@ function attachFormStart(client, config) {
3506
3780
  }
3507
3781
  const selector = config.selector ?? "form";
3508
3782
  let firedForms = /* @__PURE__ */ new Set();
3509
- let currentPath3 = window.location.pathname;
3783
+ let currentPath4 = window.location.pathname;
3510
3784
  let pendingTap = null;
3511
3785
  function getFormKey(form) {
3512
3786
  if (form.id) return `id:${form.id}`;
@@ -3557,8 +3831,8 @@ function attachFormStart(client, config) {
3557
3831
  }
3558
3832
  function resetIfPathChanged() {
3559
3833
  const newPath = window.location.pathname;
3560
- if (newPath === currentPath3) return;
3561
- currentPath3 = newPath;
3834
+ if (newPath === currentPath4) return;
3835
+ currentPath4 = newPath;
3562
3836
  firedForms = /* @__PURE__ */ new Set();
3563
3837
  }
3564
3838
  const originalPushState = history.pushState.bind(history);
@@ -3599,7 +3873,7 @@ function attachPageExit(client) {
3599
3873
  return () => {
3600
3874
  };
3601
3875
  }
3602
- let currentPath3 = window.location.pathname;
3876
+ let currentPath4 = window.location.pathname;
3603
3877
  let activeSince = document.visibilityState === "visible" ? Date.now() : null;
3604
3878
  let accumulatedMs = 0;
3605
3879
  let maxScrollPercent = null;
@@ -3649,9 +3923,9 @@ function attachPageExit(client) {
3649
3923
  }
3650
3924
  function onNavigate() {
3651
3925
  const newPath = window.location.pathname;
3652
- if (newPath === currentPath3) return;
3653
- emitSegment(currentPath3, false);
3654
- currentPath3 = newPath;
3926
+ if (newPath === currentPath4) return;
3927
+ emitSegment(currentPath4, false);
3928
+ currentPath4 = newPath;
3655
3929
  maxScrollPercent = null;
3656
3930
  gate.rebaseline();
3657
3931
  accumulatedMs = 0;
@@ -3659,15 +3933,18 @@ function attachPageExit(client) {
3659
3933
  }
3660
3934
  function onVisibilityChange() {
3661
3935
  if (document.visibilityState === "hidden") {
3662
- emitSegment(currentPath3, true);
3936
+ emitSegment(currentPath4, true);
3663
3937
  } else {
3664
3938
  activeSince = Date.now();
3665
3939
  }
3666
3940
  }
3667
3941
  function onPageHide() {
3668
- emitSegment(currentPath3, true);
3942
+ emitSegment(currentPath4, true);
3669
3943
  }
3670
3944
  const unsubscribeNav = onHistoryChange(onNavigate);
3945
+ const unsubscribeBeforeUnload = client.onBeforeUnload?.(() => {
3946
+ emitSegment(currentPath4, false);
3947
+ });
3671
3948
  window.addEventListener("scroll", onScroll, { passive: true });
3672
3949
  document.addEventListener("visibilitychange", onVisibilityChange);
3673
3950
  window.addEventListener("pagehide", onPageHide);
@@ -3676,6 +3953,7 @@ function attachPageExit(client) {
3676
3953
  if (rafId !== null) cancelAnimationFrame(rafId);
3677
3954
  gate.cancel();
3678
3955
  unsubscribeNav();
3956
+ unsubscribeBeforeUnload?.();
3679
3957
  window.removeEventListener("scroll", onScroll);
3680
3958
  document.removeEventListener("visibilitychange", onVisibilityChange);
3681
3959
  window.removeEventListener("pagehide", onPageHide);
@@ -3845,7 +4123,7 @@ function attachPhoneClickCapture(client, config) {
3845
4123
  }
3846
4124
 
3847
4125
  // ../tracking-core/src/lead-forms/lead-forms.gen.ts
3848
- var z15 = __toESM(require("zod/mini"));
4126
+ var z17 = __toESM(require("zod/mini"));
3849
4127
  var LEAD_FORM_REGISTRY_VERSION = 2;
3850
4128
  var LEAD_FORM_ROLES = [
3851
4129
  "given_name",
@@ -3871,28 +4149,28 @@ var LEAD_FORM_ROLES = [
3871
4149
  "attribution"
3872
4150
  ];
3873
4151
  var leadFormPrimitiveSchemas = /* @__PURE__ */ (() => ({
3874
- string: z15.string().check(z15.maxLength(120)),
3875
- text: z15.string().check(z15.maxLength(2e3)),
3876
- enum: z15.string().check(z15.regex(/^[a-z0-9-]{1,48}$/)),
3877
- multi_enum: z15.array(z15.string().check(z15.regex(/^[a-z0-9-]{1,48}$/))).check(z15.maxLength(10)),
3878
- bool: z15.boolean(),
3879
- date: z15.string().check(z15.regex(/^\d{4}-\d{2}-\d{2}$/)),
3880
- time_window: z15.strictObject({
3881
- start: z15.string().check(z15.regex(/^([01]\d|2[0-3]):[0-5]\d$/)),
3882
- end: z15.nullable(z15.string().check(z15.regex(/^([01]\d|2[0-3]):[0-5]\d$/))),
3883
- label: z15.optional(z15.string().check(z15.maxLength(60)))
4152
+ string: z17.string().check(z17.maxLength(120)),
4153
+ text: z17.string().check(z17.maxLength(2e3)),
4154
+ enum: z17.string().check(z17.regex(/^[a-z0-9-]{1,48}$/)),
4155
+ multi_enum: z17.array(z17.string().check(z17.regex(/^[a-z0-9-]{1,48}$/))).check(z17.maxLength(10)),
4156
+ bool: z17.boolean(),
4157
+ date: z17.string().check(z17.regex(/^\d{4}-\d{2}-\d{2}$/)),
4158
+ time_window: z17.strictObject({
4159
+ start: z17.string().check(z17.regex(/^([01]\d|2[0-3]):[0-5]\d$/)),
4160
+ end: z17.nullable(z17.string().check(z17.regex(/^([01]\d|2[0-3]):[0-5]\d$/))),
4161
+ label: z17.optional(z17.string().check(z17.maxLength(60)))
3884
4162
  }),
3885
- phone: z15.string().check(z15.regex(/^\+[1-9]\d{6,14}$/)),
3886
- email: z15.string().check(z15.maxLength(320), z15.regex(/^[^\s@]+@[^\s@]+$/)),
3887
- address: z15.strictObject({
3888
- line1: z15.string().check(z15.maxLength(120)),
3889
- line2: z15.optional(z15.string().check(z15.maxLength(120))),
3890
- city: z15.optional(z15.string().check(z15.maxLength(120))),
3891
- region: z15.optional(z15.string().check(z15.maxLength(120))),
3892
- postal_code: z15.optional(z15.string().check(z15.maxLength(120))),
3893
- country: z15.optional(z15.string().check(z15.maxLength(120)))
4163
+ phone: z17.string().check(z17.regex(/^\+[1-9]\d{6,14}$/)),
4164
+ email: z17.string().check(z17.maxLength(320), z17.regex(/^[^\s@]+@[^\s@]+$/)),
4165
+ address: z17.strictObject({
4166
+ line1: z17.string().check(z17.maxLength(120)),
4167
+ line2: z17.optional(z17.string().check(z17.maxLength(120))),
4168
+ city: z17.optional(z17.string().check(z17.maxLength(120))),
4169
+ region: z17.optional(z17.string().check(z17.maxLength(120))),
4170
+ postal_code: z17.optional(z17.string().check(z17.maxLength(120))),
4171
+ country: z17.optional(z17.string().check(z17.maxLength(120)))
3894
4172
  }),
3895
- number: z15.number()
4173
+ number: z17.number()
3896
4174
  }))();
3897
4175
  var LEAD_FORM_ROLE_SPECS = {
3898
4176
  given_name: { primitive: "string", smsLabel: "", contactIdentity: true },
@@ -3974,34 +4252,70 @@ function createLeadFormSubmit(tracking, contracts, options = {}) {
3974
4252
  contracts,
3975
4253
  settings
3976
4254
  );
4255
+ const submission = { submissionId: uuidV4(), formId: input.formId, kind: input.kind };
3977
4256
  const metadata = {
3978
4257
  form: {
3979
4258
  id: input.formId,
3980
4259
  action: input.action ?? null,
3981
- fields: fields.map(([role, value]) => ({
3982
- name: role,
3983
- type: INPUT_TYPES[primitiveOf(role) ?? "string"],
3984
- label: labelOf(role),
3985
- value,
3986
- role
3987
- })),
4260
+ fields: fields.map(fieldEntry),
3988
4261
  kind: input.kind,
3989
4262
  contract_version: LEAD_FORM_REGISTRY_VERSION,
3990
4263
  ...hash ? { contract_hash: hash } : {},
3991
4264
  ...input.extra?.length ? { extra: input.extra } : {},
3992
- ...violations.length > 0 ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) } : {}
4265
+ ...violations.length > 0 ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) } : {},
4266
+ submission_id: submission.submissionId
3993
4267
  },
3994
- page: { path: typeof window === "undefined" ? "/" : window.location.pathname }
4268
+ page: { path: currentPath3() }
3995
4269
  };
3996
4270
  tracking.trackEvent("form_submit", metadata);
3997
- return metadata;
4271
+ return { ...metadata, submission };
4272
+ },
4273
+ addDetails(submission, input) {
4274
+ const fields = presentFields(input.fields);
4275
+ const extra = (input.extra ?? []).filter((item) => !isBlank(item.value));
4276
+ if (fields.length === 0 && extra.length === 0) return { sent: false, reason: "no_fields" };
4277
+ const { violations, hash } = judgeLeadForm(
4278
+ submission.kind,
4279
+ submission.formId,
4280
+ fields,
4281
+ contracts,
4282
+ settings,
4283
+ { partial: true }
4284
+ );
4285
+ const metadata = {
4286
+ form: {
4287
+ id: submission.formId,
4288
+ submission_id: submission.submissionId,
4289
+ kind: submission.kind,
4290
+ fields: fields.map(fieldEntry),
4291
+ contract_version: LEAD_FORM_REGISTRY_VERSION,
4292
+ ...hash ? { contract_hash: hash } : {},
4293
+ ...extra.length > 0 ? { extra } : {},
4294
+ ...violations.length > 0 ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) } : {}
4295
+ },
4296
+ page: { path: currentPath3() }
4297
+ };
4298
+ tracking.trackEvent("form_details", metadata);
4299
+ return { sent: true };
3998
4300
  }
3999
4301
  };
4000
4302
  }
4001
- function judgeLeadForm(kind, formId, fields, contracts, settings) {
4303
+ function fieldEntry([role, value]) {
4304
+ return {
4305
+ name: role,
4306
+ type: INPUT_TYPES[primitiveOf(role) ?? "string"],
4307
+ label: labelOf(role),
4308
+ value,
4309
+ role
4310
+ };
4311
+ }
4312
+ function currentPath3() {
4313
+ return typeof window === "undefined" ? "/" : window.location.pathname;
4314
+ }
4315
+ function judgeLeadForm(kind, formId, fields, contracts, settings, { partial = false } = {}) {
4002
4316
  const generated = contracts?.contracts[kind];
4003
4317
  const live = liveContract(settings.trackingConfig, kind);
4004
- const violations = check(live === void 0 ? generated : fromLive(live), fields);
4318
+ const violations = check(live === void 0 ? generated : fromLive(live), fields, partial);
4005
4319
  const liveHash = live === void 0 ? void 0 : live?.contract_hash ?? null;
4006
4320
  if (contracts && (liveHash !== void 0 && (generated?.hash ?? null) !== liveHash || contracts.registryVersion !== LEAD_FORM_REGISTRY_VERSION)) {
4007
4321
  violations.push({ role: null, rule: "contract_behind" });
@@ -4047,7 +4361,7 @@ function normalize(role, value) {
4047
4361
  function isBlank(value) {
4048
4362
  return value === void 0 || value === null || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.length === 0;
4049
4363
  }
4050
- function check(contract, fields) {
4364
+ function check(contract, fields, partial) {
4051
4365
  const answered = new Map(fields);
4052
4366
  const violations = [];
4053
4367
  for (const [role, value] of fields) {
@@ -4065,6 +4379,7 @@ function check(contract, fields) {
4065
4379
  violations.push({ role, rule: "unknown_option" });
4066
4380
  }
4067
4381
  }
4382
+ if (partial) return violations;
4068
4383
  for (const { role, requiredWhen } of contract?.requires ?? []) {
4069
4384
  const owing = requiredWhen === null || optionKeys(answered.get(requiredWhen.role)).some((key) => requiredWhen.anyOf.includes(key));
4070
4385
  if (owing && !answered.has(role)) violations.push({ role, rule: "missing_required" });
@@ -4427,7 +4742,7 @@ function GoogleAdsTracking(props) {
4427
4742
  var import_react6 = require("react");
4428
4743
 
4429
4744
  // package.json
4430
- var version = "1.17.0";
4745
+ var version = "1.19.0";
4431
4746
 
4432
4747
  // ../tracking-core/src/phone-react.tsx
4433
4748
  var import_react5 = require("react");
@@ -4538,6 +4853,7 @@ function createTracking(options) {
4538
4853
  trackingConfig
4539
4854
  } = options;
4540
4855
  registerCapability("base_tracking");
4856
+ setDefaultTrackingConfig(trackingConfig);
4541
4857
  if (trackingConfig) registerCapability("conversion_goals_auto");
4542
4858
  if (!apiKey || !endpoint) {
4543
4859
  if (apiKey || endpoint) {
@@ -4805,15 +5121,1422 @@ function useBookingSubmit(bookings, options = {}) {
4805
5121
  const status = submitting ? "submitting" : outcome?.status ?? "idle";
4806
5122
  return { status, outcome, booked, submit, updateBooking, reset };
4807
5123
  }
5124
+
5125
+ // ../tracking-core/src/video-react.tsx
5126
+ var import_react8 = require("react");
5127
+ var import_jsx_runtime5 = require("react/jsx-runtime");
5128
+ var DEFAULT_VIDEO_CHECKPOINTS = [25, 50, 75, 90, 100];
5129
+ var streamManifestUrl = (host, uid) => `https://${host}/${uid}/manifest/video.m3u8`;
5130
+ var streamPosterUrl = (host, uid, time = 4) => `https://${host}/${uid}/thumbnails/thumbnail.jpg?time=${time}s`;
5131
+ var TV_SKIN_STYLE_ATTR = "data-aranova-tv-skin";
5132
+ var TV_SKIN_CSS = `
5133
+ .tv-skin {
5134
+ container-type: inline-size;
5135
+ position: relative;
5136
+ overflow: hidden;
5137
+ border-radius: 18px;
5138
+ border: 1px solid rgba(255, 255, 255, 0.12);
5139
+ background: #0a0e1a;
5140
+ box-shadow: 0 18px 48px rgba(0, 0, 0, 0.32);
5141
+ }
5142
+ .tv-skin-desktop {
5143
+ display: none;
5144
+ aspect-ratio: 16 / 9;
5145
+ width: 100%;
5146
+ }
5147
+ .tv-skin-mobile {
5148
+ display: block;
5149
+ margin-left: auto;
5150
+ margin-right: auto;
5151
+ aspect-ratio: 9 / 16;
5152
+ width: 100%;
5153
+ max-width: 420px;
5154
+ }
5155
+ @media (min-width: 768px) {
5156
+ .tv-skin-desktop {
5157
+ display: block;
5158
+ }
5159
+ .tv-skin-mobile {
5160
+ display: none;
5161
+ }
5162
+ }
5163
+ .tv-skin media-controller {
5164
+ display: block;
5165
+ width: 100%;
5166
+ height: 100%;
5167
+ background: #000;
5168
+ --media-primary-color: #ffffff;
5169
+ --media-text-color: rgba(255, 255, 255, 0.82);
5170
+ --media-icon-color: #ffffff;
5171
+ --media-secondary-color: transparent;
5172
+ --media-control-background: transparent;
5173
+ --media-control-hover-background: transparent;
5174
+ --media-control-padding: 0;
5175
+ --media-control-height: 40px;
5176
+ --media-tooltip-display: none;
5177
+ --media-font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
5178
+ --media-font-size: 11.5px;
5179
+ --media-focus-box-shadow: 0 0 0 2px #74a0ff;
5180
+ --media-button-icon-width: 20px;
5181
+ --media-button-icon-height: 20px;
5182
+ --media-range-padding: 0;
5183
+ --media-range-bar-color: #ffffff;
5184
+ --media-range-track-background: rgba(255, 255, 255, 0.25);
5185
+ --media-range-track-border-radius: 3px;
5186
+ --media-time-range-buffered-color: transparent;
5187
+ --media-time-range-hover-display: none;
5188
+ --media-preview-time-background: transparent;
5189
+ --media-range-thumb-background: #ffffff;
5190
+ --media-range-thumb-width: 12px;
5191
+ --media-range-thumb-height: 12px;
5192
+ --media-range-thumb-opacity: 0;
5193
+ }
5194
+ .tv-skin media-controller::part(vertical-layer),
5195
+ .tv-skin media-controller::part(centered-layer) {
5196
+ background: transparent;
5197
+ }
5198
+ .tv-chrome {
5199
+ position: absolute;
5200
+ inset: auto 0 0 0;
5201
+ padding-top: 56px;
5202
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0));
5203
+ transition: opacity 0.25s ease;
5204
+ }
5205
+ .tv-skin media-controller[userinactive]:not([mediapaused]) .tv-chrome {
5206
+ opacity: 0;
5207
+ pointer-events: none;
5208
+ }
5209
+ .tv-row {
5210
+ display: flex;
5211
+ align-items: center;
5212
+ gap: 2px;
5213
+ padding: 0 4px;
5214
+ }
5215
+ .tv-spacer {
5216
+ flex: 1 1 auto;
5217
+ }
5218
+ .tv-ctl,
5219
+ .tv-btn {
5220
+ display: inline-flex;
5221
+ align-items: center;
5222
+ justify-content: center;
5223
+ width: 40px;
5224
+ height: 40px;
5225
+ padding: 0;
5226
+ border: 0;
5227
+ border-radius: 0;
5228
+ background: transparent;
5229
+ box-shadow: none;
5230
+ color: rgba(255, 255, 255, 0.92);
5231
+ cursor: pointer;
5232
+ transition: color 0.15s;
5233
+ }
5234
+ .tv-ctl:hover,
5235
+ .tv-btn:hover {
5236
+ background: transparent;
5237
+ color: #ffffff;
5238
+ }
5239
+ .tv-ctl:focus-visible,
5240
+ .tv-btn:focus-visible,
5241
+ .tv-menu-item:focus-visible {
5242
+ outline: 2px solid #74a0ff;
5243
+ outline-offset: -4px;
5244
+ box-shadow: none;
5245
+ }
5246
+ .tv-time {
5247
+ width: auto;
5248
+ padding: 0 4px 0 6px;
5249
+ background: transparent;
5250
+ letter-spacing: 0.02em;
5251
+ white-space: nowrap;
5252
+ --media-control-background: transparent;
5253
+ }
5254
+ .tv-time-cur {
5255
+ display: none;
5256
+ }
5257
+ .tv-seek {
5258
+ display: block;
5259
+ width: 100%;
5260
+ height: 16px;
5261
+ margin: 0;
5262
+ padding: 0;
5263
+ background: transparent;
5264
+ box-shadow: none;
5265
+ border: 0;
5266
+ --media-range-track-height: 3px;
5267
+ --media-control-background: transparent;
5268
+ --media-control-hover-background: transparent;
5269
+ }
5270
+ .tv-seek:hover,
5271
+ .tv-seek:focus-visible {
5272
+ --media-range-track-height: 6px;
5273
+ --media-range-thumb-opacity: 1;
5274
+ }
5275
+ .tv-vol {
5276
+ display: flex;
5277
+ align-items: center;
5278
+ }
5279
+ .tv-volrange {
5280
+ width: 64px;
5281
+ height: 40px;
5282
+ margin-right: 4px;
5283
+ padding: 0;
5284
+ opacity: 0;
5285
+ pointer-events: none;
5286
+ transform: scaleX(0.75);
5287
+ transform-origin: right center;
5288
+ background: transparent;
5289
+ transition: transform 0.2s ease, opacity 0.2s ease;
5290
+ --media-control-background: transparent;
5291
+ --media-range-track-height: 3px;
5292
+ }
5293
+ .tv-vol:hover .tv-volrange,
5294
+ .tv-vol:focus-within .tv-volrange {
5295
+ opacity: 1;
5296
+ pointer-events: auto;
5297
+ transform: scaleX(1);
5298
+ }
5299
+ @media (hover: none) {
5300
+ .tv-volrange {
5301
+ display: none;
5302
+ }
5303
+ }
5304
+ @container (max-width: 479px) {
5305
+ .tv-vol,
5306
+ .tv-time-full {
5307
+ display: none;
5308
+ }
5309
+ .tv-time-cur {
5310
+ display: inline-flex;
5311
+ }
5312
+ }
5313
+ @container (max-width: 767px) {
5314
+ .tv-theater {
5315
+ display: none;
5316
+ }
5317
+ }
5318
+ .tv-menu-wrap {
5319
+ position: relative;
5320
+ display: flex;
5321
+ align-items: center;
5322
+ }
5323
+ .tv-menu {
5324
+ position: absolute;
5325
+ right: 0;
5326
+ bottom: calc(100% + 8px);
5327
+ z-index: 5;
5328
+ width: 136px;
5329
+ max-height: 232px;
5330
+ margin: 0;
5331
+ padding: 4px 0;
5332
+ overflow-y: auto;
5333
+ list-style: none;
5334
+ border: 1px solid rgba(255, 255, 255, 0.2);
5335
+ border-radius: 10px;
5336
+ background: linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12)), rgba(10, 14, 26, 0.55);
5337
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 12px 32px rgba(0, 0, 0, 0.35);
5338
+ }
5339
+ @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
5340
+ .tv-menu {
5341
+ -webkit-backdrop-filter: blur(16px) saturate(140%);
5342
+ backdrop-filter: blur(16px) saturate(140%);
5343
+ }
5344
+ }
5345
+ .tv-menu-item {
5346
+ position: relative;
5347
+ display: flex;
5348
+ width: 100%;
5349
+ height: 32px;
5350
+ align-items: center;
5351
+ padding: 0 12px 0 24px;
5352
+ border: 0;
5353
+ background: transparent;
5354
+ color: rgba(255, 255, 255, 0.75);
5355
+ font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
5356
+ font-size: 13px;
5357
+ font-weight: 500;
5358
+ text-align: left;
5359
+ cursor: pointer;
5360
+ }
5361
+ .tv-menu-item:hover,
5362
+ .tv-menu-item:focus-visible {
5363
+ background: rgba(255, 255, 255, 0.1);
5364
+ color: #ffffff;
5365
+ outline: none;
5366
+ }
5367
+ .tv-menu-item[aria-checked='true'] {
5368
+ color: #ffffff;
5369
+ font-weight: 600;
5370
+ }
5371
+ .tv-menu-item[aria-checked='true']::before {
5372
+ content: '';
5373
+ position: absolute;
5374
+ left: 12px;
5375
+ width: 5px;
5376
+ height: 5px;
5377
+ border-radius: 50%;
5378
+ background: #ffffff;
5379
+ }
5380
+ .tv-menu-sub {
5381
+ margin-left: 4px;
5382
+ font-size: 11.5px;
5383
+ font-weight: 400;
5384
+ color: rgba(255, 255, 255, 0.55);
5385
+ }
5386
+ .tv-menu-note {
5387
+ height: 20px;
5388
+ padding: 0 12px 0 24px;
5389
+ color: rgba(255, 255, 255, 0.55);
5390
+ font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
5391
+ font-size: 11.5px;
5392
+ line-height: 20px;
5393
+ }
5394
+ .tv-poster-overlay {
5395
+ position: absolute;
5396
+ inset: 0;
5397
+ }
5398
+ .tv-poster-img {
5399
+ width: 100%;
5400
+ height: 100%;
5401
+ object-fit: cover;
5402
+ display: block;
5403
+ }
5404
+ .tv-play-trigger {
5405
+ position: absolute;
5406
+ inset: 0;
5407
+ width: 100%;
5408
+ height: 100%;
5409
+ border: 0;
5410
+ padding: 0;
5411
+ background: transparent;
5412
+ cursor: pointer;
5413
+ display: flex;
5414
+ align-items: center;
5415
+ justify-content: center;
5416
+ transition: filter 0.2s ease;
5417
+ }
5418
+ .tv-play-trigger:hover {
5419
+ filter: brightness(1.1);
5420
+ }
5421
+ .tv-play-trigger:focus-visible {
5422
+ outline: 2px solid #74a0ff;
5423
+ outline-offset: -4px;
5424
+ }
5425
+ .tv-play-pill {
5426
+ display: flex;
5427
+ width: 64px;
5428
+ height: 64px;
5429
+ align-items: center;
5430
+ justify-content: center;
5431
+ border-radius: 9999px;
5432
+ border: 2px solid rgba(255, 255, 255, 0.72);
5433
+ background: rgba(255, 255, 255, 0.15);
5434
+ color: #ffffff;
5435
+ backdrop-filter: blur(2px);
5436
+ -webkit-backdrop-filter: blur(2px);
5437
+ transition: transform 0.3s ease;
5438
+ }
5439
+ .tv-play-trigger:hover .tv-play-pill {
5440
+ transform: scale(1.05);
5441
+ }
5442
+ `;
5443
+ function ensureTvSkinStyles() {
5444
+ if (typeof document === "undefined") return;
5445
+ if (document.querySelector(`style[${TV_SKIN_STYLE_ATTR}]`)) return;
5446
+ const style = document.createElement("style");
5447
+ style.setAttribute(TV_SKIN_STYLE_ATTR, "1");
5448
+ style.textContent = TV_SKIN_CSS;
5449
+ document.head.append(style);
5450
+ }
5451
+ function IconPlay({
5452
+ slot,
5453
+ size = 20,
5454
+ filled = false
5455
+ }) {
5456
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5457
+ "svg",
5458
+ {
5459
+ slot,
5460
+ width: size,
5461
+ height: size,
5462
+ viewBox: "0 0 24 24",
5463
+ fill: filled ? "currentColor" : "none",
5464
+ stroke: "currentColor",
5465
+ strokeWidth: filled ? 0 : 1.75,
5466
+ strokeLinecap: "round",
5467
+ strokeLinejoin: "round",
5468
+ "aria-hidden": "true",
5469
+ style: filled ? { marginLeft: 3 } : void 0,
5470
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "6 3 20 12 6 21 6 3" })
5471
+ }
5472
+ );
5473
+ }
5474
+ function IconPause({ slot }) {
5475
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5476
+ "svg",
5477
+ {
5478
+ slot,
5479
+ width: 20,
5480
+ height: 20,
5481
+ viewBox: "0 0 24 24",
5482
+ fill: "none",
5483
+ stroke: "currentColor",
5484
+ strokeWidth: 1.75,
5485
+ strokeLinecap: "round",
5486
+ strokeLinejoin: "round",
5487
+ "aria-hidden": "true",
5488
+ children: [
5489
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1" }),
5490
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1" })
5491
+ ]
5492
+ }
5493
+ );
5494
+ }
5495
+ function IconVolumeHigh({ slot }) {
5496
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5497
+ "svg",
5498
+ {
5499
+ slot,
5500
+ width: 20,
5501
+ height: 20,
5502
+ viewBox: "0 0 24 24",
5503
+ fill: "none",
5504
+ stroke: "currentColor",
5505
+ strokeWidth: 1.75,
5506
+ strokeLinecap: "round",
5507
+ strokeLinejoin: "round",
5508
+ "aria-hidden": "true",
5509
+ children: [
5510
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
5511
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" }),
5512
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" })
5513
+ ]
5514
+ }
5515
+ );
5516
+ }
5517
+ function IconVolumeLow({ slot }) {
5518
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5519
+ "svg",
5520
+ {
5521
+ slot,
5522
+ width: 20,
5523
+ height: 20,
5524
+ viewBox: "0 0 24 24",
5525
+ fill: "none",
5526
+ stroke: "currentColor",
5527
+ strokeWidth: 1.75,
5528
+ strokeLinecap: "round",
5529
+ strokeLinejoin: "round",
5530
+ "aria-hidden": "true",
5531
+ children: [
5532
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
5533
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" })
5534
+ ]
5535
+ }
5536
+ );
5537
+ }
5538
+ function IconVolumeOff({ slot }) {
5539
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5540
+ "svg",
5541
+ {
5542
+ slot,
5543
+ width: 20,
5544
+ height: 20,
5545
+ viewBox: "0 0 24 24",
5546
+ fill: "none",
5547
+ stroke: "currentColor",
5548
+ strokeWidth: 1.75,
5549
+ strokeLinecap: "round",
5550
+ strokeLinejoin: "round",
5551
+ "aria-hidden": "true",
5552
+ children: [
5553
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
5554
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "22", y1: "9", x2: "16", y2: "15" }),
5555
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "16", y1: "9", x2: "22", y2: "15" })
5556
+ ]
5557
+ }
5558
+ );
5559
+ }
5560
+ function IconSettings() {
5561
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5562
+ "svg",
5563
+ {
5564
+ width: 20,
5565
+ height: 20,
5566
+ viewBox: "0 0 24 24",
5567
+ fill: "none",
5568
+ stroke: "currentColor",
5569
+ strokeWidth: 1.75,
5570
+ strokeLinecap: "round",
5571
+ strokeLinejoin: "round",
5572
+ "aria-hidden": "true",
5573
+ children: [
5574
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" }),
5575
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "12", cy: "12", r: "3" })
5576
+ ]
5577
+ }
5578
+ );
5579
+ }
5580
+ function IconTheaterExpand() {
5581
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5582
+ "svg",
5583
+ {
5584
+ width: 20,
5585
+ height: 20,
5586
+ viewBox: "0 0 24 24",
5587
+ fill: "none",
5588
+ stroke: "currentColor",
5589
+ strokeWidth: 1.75,
5590
+ strokeLinecap: "round",
5591
+ strokeLinejoin: "round",
5592
+ "aria-hidden": "true",
5593
+ children: [
5594
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "15 3 21 3 21 9" }),
5595
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "9 21 3 21 3 15" }),
5596
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
5597
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
5598
+ ]
5599
+ }
5600
+ );
5601
+ }
5602
+ function IconTheaterShrink() {
5603
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5604
+ "svg",
5605
+ {
5606
+ width: 20,
5607
+ height: 20,
5608
+ viewBox: "0 0 24 24",
5609
+ fill: "none",
5610
+ stroke: "currentColor",
5611
+ strokeWidth: 1.75,
5612
+ strokeLinecap: "round",
5613
+ strokeLinejoin: "round",
5614
+ "aria-hidden": "true",
5615
+ children: [
5616
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "4 14 10 14 10 20" }),
5617
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "20 10 14 10 14 4" }),
5618
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
5619
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
5620
+ ]
5621
+ }
5622
+ );
5623
+ }
5624
+ function IconFullscreenEnter({ slot }) {
5625
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5626
+ "svg",
5627
+ {
5628
+ slot,
5629
+ width: 20,
5630
+ height: 20,
5631
+ viewBox: "0 0 24 24",
5632
+ fill: "none",
5633
+ stroke: "currentColor",
5634
+ strokeWidth: 1.75,
5635
+ strokeLinecap: "round",
5636
+ strokeLinejoin: "round",
5637
+ "aria-hidden": "true",
5638
+ children: [
5639
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
5640
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M21 8V5a2 2 0 0 0-2-2h-3" }),
5641
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M3 16v3a2 2 0 0 0 2 2h3" }),
5642
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
5643
+ ]
5644
+ }
5645
+ );
5646
+ }
5647
+ function IconFullscreenExit({ slot }) {
5648
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5649
+ "svg",
5650
+ {
5651
+ slot,
5652
+ width: 20,
5653
+ height: 20,
5654
+ viewBox: "0 0 24 24",
5655
+ fill: "none",
5656
+ stroke: "currentColor",
5657
+ strokeWidth: 1.75,
5658
+ strokeLinecap: "round",
5659
+ strokeLinejoin: "round",
5660
+ "aria-hidden": "true",
5661
+ children: [
5662
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M8 3v3a2 2 0 0 1-2 2H3" }),
5663
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M21 8h-3a2 2 0 0 1-2-2V3" }),
5664
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M3 16h3a2 2 0 0 1 2 2v3" }),
5665
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M16 21v-3a2 2 0 0 1 2-2h3" })
5666
+ ]
5667
+ }
5668
+ );
5669
+ }
5670
+ var EMPTY_GOALS2 = [];
5671
+ function serverVideoSnapshot() {
5672
+ return null;
5673
+ }
5674
+ function serverGoalsSnapshot() {
5675
+ return EMPTY_GOALS2;
5676
+ }
5677
+ function useTrackedVideoConfig(videoKey, trackingConfig, fallbackConfig) {
5678
+ registerCapability("video_tracking");
5679
+ const effectiveRef = trackingConfig ?? getActiveTrackingEnv().trackingConfig;
5680
+ const subscribe = (0, import_react8.useCallback)(
5681
+ (listener) => effectiveRef ? getTrackingConfigRuntime(effectiveRef).subscribe(listener) : () => {
5682
+ },
5683
+ [effectiveRef]
5684
+ );
5685
+ const live = (0, import_react8.useSyncExternalStore)(
5686
+ subscribe,
5687
+ () => effectiveRef && videoKey ? getTrackingConfigRuntime(effectiveRef).getVideo(videoKey) : null,
5688
+ serverVideoSnapshot
5689
+ );
5690
+ return live ?? fallbackConfig ?? null;
5691
+ }
5692
+ function useActiveVideoGoals(videoId, trackingConfig) {
5693
+ const effectiveRef = trackingConfig ?? getActiveTrackingEnv().trackingConfig;
5694
+ const subscribe = (0, import_react8.useCallback)(
5695
+ (listener) => effectiveRef ? getTrackingConfigRuntime(effectiveRef).subscribe(listener) : () => {
5696
+ },
5697
+ [effectiveRef]
5698
+ );
5699
+ const goals = (0, import_react8.useSyncExternalStore)(
5700
+ subscribe,
5701
+ () => effectiveRef ? getTrackingConfigRuntime(effectiveRef).listGoals() : EMPTY_GOALS2,
5702
+ serverGoalsSnapshot
5703
+ );
5704
+ return (0, import_react8.useMemo)(() => {
5705
+ const out = [];
5706
+ for (const goal of goals) {
5707
+ if (goal.kind !== "event" || !goal.trigger) continue;
5708
+ if (goal.trigger.event_type !== "video_progress") continue;
5709
+ if (goal.trigger.video_id && goal.trigger.video_id !== videoId) continue;
5710
+ const metric = goal.trigger.metric;
5711
+ const threshold = goal.trigger.threshold;
5712
+ if ((metric === "percent" || metric === "seconds") && typeof threshold === "number") {
5713
+ out.push({ key: goal.key, metric, threshold });
5714
+ }
5715
+ }
5716
+ return out;
5717
+ }, [goals, videoId]);
5718
+ }
5719
+ function renditionsOf(h) {
5720
+ const best = /* @__PURE__ */ new Map();
5721
+ h.levels.forEach((l, index) => {
5722
+ const px = Math.min(l.width || Infinity, l.height || Infinity);
5723
+ if (!Number.isFinite(px)) return;
5724
+ const cur = best.get(px);
5725
+ if (!cur || l.bitrate > cur.bitrate) best.set(px, { height: px, index, bitrate: l.bitrate });
5726
+ });
5727
+ return [...best.values()].sort((a, b) => b.height - a.height).map(({ height, index }) => ({ height, index }));
5728
+ }
5729
+ var MEDIA_CHROME_CDN_URL = "https://cdn.jsdelivr.net/npm/media-chrome@4/+esm";
5730
+ var HLS_JS_CDN_URL = "https://cdn.jsdelivr.net/npm/hls.js@1/dist/hls.min.js";
5731
+ var defaultLibsPromise = null;
5732
+ function loadScriptOnce(src, attr, isModule = false) {
5733
+ if (typeof document === "undefined") return Promise.resolve();
5734
+ const existing = document.querySelector(`script[${attr}]`);
5735
+ if (existing) return Promise.resolve();
5736
+ return new Promise((resolve) => {
5737
+ const s = document.createElement("script");
5738
+ if (isModule) s.type = "module";
5739
+ s.async = true;
5740
+ s.src = src;
5741
+ s.setAttribute(attr, "1");
5742
+ s.addEventListener("load", () => resolve(), { once: true });
5743
+ s.addEventListener("error", () => resolve(), { once: true });
5744
+ document.head.append(s);
5745
+ if (typeof navigator !== "undefined" && /jsdom/i.test(navigator.userAgent)) {
5746
+ setTimeout(resolve, 0);
5747
+ }
5748
+ });
5749
+ }
5750
+ function loadDefaultPlayerLibs() {
5751
+ if (typeof window === "undefined") return Promise.resolve({});
5752
+ if (defaultLibsPromise) return defaultLibsPromise;
5753
+ defaultLibsPromise = Promise.all([
5754
+ typeof customElements !== "undefined" && customElements.get("media-controller") ? Promise.resolve() : loadScriptOnce(MEDIA_CHROME_CDN_URL, "data-aranova-media-chrome", true),
5755
+ window.Hls ? Promise.resolve() : loadScriptOnce(HLS_JS_CDN_URL, "data-aranova-hls-js", false)
5756
+ ]).then(() => ({
5757
+ hls: window.Hls
5758
+ }));
5759
+ return defaultLibsPromise;
5760
+ }
5761
+ function Cut({
5762
+ id,
5763
+ variant,
5764
+ uid,
5765
+ host,
5766
+ title,
5767
+ posterTime,
5768
+ posterUrl,
5769
+ renderPoster,
5770
+ theater,
5771
+ onTheaterChange,
5772
+ autoExpandOnPlay,
5773
+ checkpoints,
5774
+ videoGoals,
5775
+ client,
5776
+ loadPlayerLibs,
5777
+ className,
5778
+ ...cb
5779
+ }) {
5780
+ const root = (0, import_react8.useRef)(null);
5781
+ const video = (0, import_react8.useRef)(null);
5782
+ const hls = (0, import_react8.useRef)(null);
5783
+ const lastTime = (0, import_react8.useRef)(0);
5784
+ const seekFrom = (0, import_react8.useRef)(0);
5785
+ const cbRef = (0, import_react8.useRef)(cb);
5786
+ cbRef.current = cb;
5787
+ const clientRef = (0, import_react8.useRef)(client);
5788
+ clientRef.current = client;
5789
+ const goalsRef = (0, import_react8.useRef)(videoGoals);
5790
+ goalsRef.current = videoGoals;
5791
+ const rangesRef = (0, import_react8.useRef)([]);
5792
+ const activeSegmentStartRef = (0, import_react8.useRef)(null);
5793
+ const furthestSecondsRef = (0, import_react8.useRef)(0);
5794
+ const maxPercentRef = (0, import_react8.useRef)(0);
5795
+ const durationRef = (0, import_react8.useRef)(0);
5796
+ const playCountRef = (0, import_react8.useRef)(0);
5797
+ const seekCountRef = (0, import_react8.useRef)(0);
5798
+ const firedCheckpointsRef = (0, import_react8.useRef)(/* @__PURE__ */ new Set());
5799
+ const firedGoalsRef = (0, import_react8.useRef)(/* @__PURE__ */ new Set());
5800
+ const dirtySinceLastEmitRef = (0, import_react8.useRef)(false);
5801
+ const [started, setStarted] = (0, import_react8.useState)(false);
5802
+ const [ready, setReady] = (0, import_react8.useState)(false);
5803
+ const [playing, setPlaying] = (0, import_react8.useState)(false);
5804
+ const [renditions, setRenditions] = (0, import_react8.useState)([]);
5805
+ const [selected, setSelected] = (0, import_react8.useState)("auto");
5806
+ const [live, setLive] = (0, import_react8.useState)(null);
5807
+ const [menu, setMenu] = (0, import_react8.useState)(false);
5808
+ const [pending, setPending] = (0, import_react8.useState)(null);
5809
+ const pendingRef = (0, import_react8.useRef)(null);
5810
+ const wrap = (0, import_react8.useRef)(null);
5811
+ const gear = (0, import_react8.useRef)(null);
5812
+ const selectedRef = (0, import_react8.useRef)("auto");
5813
+ const pendingTimer = (0, import_react8.useRef)(null);
5814
+ const liveRef = (0, import_react8.useRef)(null);
5815
+ const videoId = `${id}-${variant}`;
5816
+ const getCurrentRanges = (0, import_react8.useCallback)(() => {
5817
+ const v = video.current;
5818
+ const cur = v ? v.currentTime : lastTime.current;
5819
+ const list = [...rangesRef.current];
5820
+ if (activeSegmentStartRef.current !== null && cur > activeSegmentStartRef.current) {
5821
+ list.push({
5822
+ start_s: activeSegmentStartRef.current,
5823
+ end_s: cur
5824
+ });
5825
+ }
5826
+ return mergeVideoRanges(list);
5827
+ }, []);
5828
+ const commitActiveSegment = (0, import_react8.useCallback)((endTime) => {
5829
+ const end = endTime ?? video.current?.currentTime ?? lastTime.current;
5830
+ if (activeSegmentStartRef.current !== null && end > activeSegmentStartRef.current) {
5831
+ rangesRef.current = mergeVideoRanges([
5832
+ ...rangesRef.current,
5833
+ { start_s: activeSegmentStartRef.current, end_s: end }
5834
+ ]);
5835
+ }
5836
+ activeSegmentStartRef.current = null;
5837
+ }, []);
5838
+ const snap = (0, import_react8.useCallback)(() => {
5839
+ const v = video.current;
5840
+ const currentTime = roundVideoSeconds(v?.currentTime ?? lastTime.current);
5841
+ const rawDuration = v?.duration;
5842
+ const duration = typeof rawDuration === "number" && Number.isFinite(rawDuration) && rawDuration > 0 ? roundVideoSeconds(rawDuration) : durationRef.current;
5843
+ if (duration > 0) durationRef.current = duration;
5844
+ if (currentTime > furthestSecondsRef.current) {
5845
+ furthestSecondsRef.current = currentTime;
5846
+ }
5847
+ const ranges = getCurrentRanges();
5848
+ const watchedSeconds = sumVideoRangesSeconds(ranges, duration);
5849
+ const watchedPercent = duration > 0 ? Math.min(100, Math.round(watchedSeconds / duration * 100)) : 0;
5850
+ const curPercent = duration > 0 ? Math.min(100, Math.round(furthestSecondsRef.current / duration * 100)) : 0;
5851
+ if (curPercent > maxPercentRef.current) {
5852
+ maxPercentRef.current = curPercent;
5853
+ }
5854
+ return {
5855
+ id,
5856
+ videoId,
5857
+ variant,
5858
+ currentTime,
5859
+ duration,
5860
+ watchedSeconds,
5861
+ watchedPercent,
5862
+ maxPercent: maxPercentRef.current,
5863
+ ranges
5864
+ };
5865
+ }, [getCurrentRanges, id, variant, videoId]);
5866
+ const emitProgress = (0, import_react8.useCallback)(
5867
+ (trigger, snapshotOverride) => {
5868
+ const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;
5869
+ if (!activeClient || playCountRef.current <= 0) return;
5870
+ const s = snapshotOverride ?? snap();
5871
+ if (s.ranges.length === 0 && s.currentTime <= 0 && trigger !== "ended") return;
5872
+ dirtySinceLastEmitRef.current = false;
5873
+ const path = typeof window === "undefined" ? "/" : window.location.pathname;
5874
+ activeClient.trackEvent({
5875
+ eventType: "video_progress",
5876
+ metadata: {
5877
+ video_id: id,
5878
+ variant,
5879
+ trigger,
5880
+ current_s: s.currentTime,
5881
+ furthest_s: roundVideoSeconds(furthestSecondsRef.current),
5882
+ watched_s: s.watchedSeconds,
5883
+ duration_s: s.duration,
5884
+ max_percent: s.maxPercent,
5885
+ watched_percent: s.watchedPercent,
5886
+ play_count: playCountRef.current,
5887
+ seek_count: seekCountRef.current,
5888
+ ranges: s.ranges,
5889
+ page: { path }
5890
+ }
5891
+ });
5892
+ },
5893
+ [id, snap, variant]
5894
+ );
5895
+ const clearPending = () => {
5896
+ if (pendingTimer.current) clearTimeout(pendingTimer.current);
5897
+ pendingTimer.current = null;
5898
+ pendingRef.current = null;
5899
+ setPending(null);
5900
+ };
5901
+ (0, import_react8.useEffect)(() => {
5902
+ ensureTvSkinStyles();
5903
+ registerCapability("video_tracking");
5904
+ return () => {
5905
+ if (pendingTimer.current) clearTimeout(pendingTimer.current);
5906
+ hls.current?.destroy();
5907
+ hls.current = null;
5908
+ };
5909
+ }, []);
5910
+ (0, import_react8.useEffect)(() => {
5911
+ if (!started || typeof window === "undefined") return;
5912
+ const flushExitProgress = (useBeacon) => {
5913
+ if (playCountRef.current <= 0 || !dirtySinceLastEmitRef.current) return;
5914
+ commitActiveSegment();
5915
+ emitProgress("page_exit");
5916
+ if (useBeacon) {
5917
+ const activeClient2 = clientRef.current ?? getActiveTrackingEnv().rawClient;
5918
+ activeClient2?.flushBeacon();
5919
+ }
5920
+ };
5921
+ const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;
5922
+ const unsubscribeBeforeUnload = activeClient?.onBeforeUnload?.(() => {
5923
+ flushExitProgress(false);
5924
+ });
5925
+ const onVisibilityChange = () => {
5926
+ if (document.visibilityState === "hidden") {
5927
+ flushExitProgress(true);
5928
+ }
5929
+ };
5930
+ const onPageHide = () => {
5931
+ flushExitProgress(true);
5932
+ };
5933
+ document.addEventListener("visibilitychange", onVisibilityChange);
5934
+ window.addEventListener("pagehide", onPageHide);
5935
+ return () => {
5936
+ unsubscribeBeforeUnload?.();
5937
+ document.removeEventListener("visibilitychange", onVisibilityChange);
5938
+ window.removeEventListener("pagehide", onPageHide);
5939
+ };
5940
+ }, [started, commitActiveSegment, emitProgress]);
5941
+ (0, import_react8.useEffect)(() => {
5942
+ if (!started) return;
5943
+ let was = false;
5944
+ const report = (fullscreen) => {
5945
+ if (fullscreen === was) return;
5946
+ was = fullscreen;
5947
+ cbRef.current.onFullscreenChange?.({ ...snap(), fullscreen });
5948
+ };
5949
+ const onDoc = () => {
5950
+ const el = root.current;
5951
+ const fe = document.fullscreenElement ?? document.webkitFullscreenElement;
5952
+ report(Boolean(el && fe && el.contains(fe)));
5953
+ };
5954
+ const v = video.current;
5955
+ const begin = () => report(true);
5956
+ const end = () => report(false);
5957
+ document.addEventListener("fullscreenchange", onDoc);
5958
+ document.addEventListener("webkitfullscreenchange", onDoc);
5959
+ v?.addEventListener("webkitbeginfullscreen", begin);
5960
+ v?.addEventListener("webkitendfullscreen", end);
5961
+ return () => {
5962
+ document.removeEventListener("fullscreenchange", onDoc);
5963
+ document.removeEventListener("webkitfullscreenchange", onDoc);
5964
+ v?.removeEventListener("webkitbeginfullscreen", begin);
5965
+ v?.removeEventListener("webkitendfullscreen", end);
5966
+ };
5967
+ }, [started, snap]);
5968
+ (0, import_react8.useEffect)(() => {
5969
+ if (!menu) return;
5970
+ const outside = (e) => {
5971
+ if (wrap.current && !wrap.current.contains(e.target)) setMenu(false);
5972
+ };
5973
+ document.addEventListener("pointerdown", outside);
5974
+ return () => document.removeEventListener("pointerdown", outside);
5975
+ }, [menu]);
5976
+ const closeMenu = (refocus) => {
5977
+ setMenu(false);
5978
+ if (refocus) gear.current?.focus();
5979
+ };
5980
+ const onMenuKey = (e) => {
5981
+ if (e.key === "Escape") {
5982
+ e.preventDefault();
5983
+ e.stopPropagation();
5984
+ closeMenu(true);
5985
+ } else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
5986
+ e.preventDefault();
5987
+ const items = [
5988
+ ...wrap.current?.querySelectorAll('[role="menuitemradio"]') ?? []
5989
+ ];
5990
+ const i = items.indexOf(document.activeElement);
5991
+ items[(i + (e.key === "ArrowDown" ? 1 : items.length - 1)) % items.length]?.focus();
5992
+ }
5993
+ };
5994
+ const emitQuality = (from, to, source) => cbRef.current.onQualityChange?.({ ...snap(), from, to, source });
5995
+ const start = async () => {
5996
+ const v = video.current;
5997
+ if (!v || started) return;
5998
+ setStarted(true);
5999
+ if (variant === "16x9" && autoExpandOnPlay) {
6000
+ onTheaterChange?.(true);
6001
+ }
6002
+ const src = streamManifestUrl(host, uid);
6003
+ try {
6004
+ const loaded = await (loadPlayerLibs ? loadPlayerLibs() : loadDefaultPlayerLibs());
6005
+ setReady(true);
6006
+ const rawHls = (loaded && typeof loaded === "object" && "hls" in loaded ? loaded.hls : null) ?? (typeof window !== "undefined" ? window.Hls : void 0);
6007
+ const HlsLib = rawHls && typeof rawHls === "object" && "default" in rawHls ? rawHls.default : rawHls;
6008
+ if (!HlsLib || !HlsLib.isSupported()) {
6009
+ v.src = src;
6010
+ } else {
6011
+ const h = new HlsLib({ capLevelToPlayerSize: true });
6012
+ hls.current = h;
6013
+ h.on(HlsLib.Events.MANIFEST_PARSED, () => setRenditions(renditionsOf(h)));
6014
+ h.on(HlsLib.Events.LEVEL_SWITCHED, (_e, data) => {
6015
+ const lv = h.levels[data.level];
6016
+ const height = lv ? Math.min(lv.width || Infinity, lv.height || Infinity) : null;
6017
+ const from = liveRef.current;
6018
+ liveRef.current = height;
6019
+ setLive(height);
6020
+ const want = pendingRef.current;
6021
+ if (want !== null && want === height) clearPending();
6022
+ if (selectedRef.current === "auto" && height && from && from !== height) {
6023
+ emitQuality(from, height, "auto");
6024
+ }
6025
+ });
6026
+ h.on(HlsLib.Events.ERROR, () => clearPending());
6027
+ h.loadSource(src);
6028
+ h.attachMedia(v);
6029
+ }
6030
+ } catch {
6031
+ setReady(true);
6032
+ v.src = src;
6033
+ }
6034
+ try {
6035
+ await v.play();
6036
+ } catch {
6037
+ }
6038
+ };
6039
+ const pick = (q) => {
6040
+ const h = hls.current;
6041
+ const from = selectedRef.current;
6042
+ setMenu(false);
6043
+ if (!h || q === from) return;
6044
+ if (q === "auto") h.nextLevel = -1;
6045
+ else {
6046
+ const r = renditions.find((x) => x.height === q);
6047
+ if (r) h.nextLevel = r.index;
6048
+ }
6049
+ selectedRef.current = q;
6050
+ setSelected(q);
6051
+ clearPending();
6052
+ if (typeof q === "number" && q !== liveRef.current) {
6053
+ pendingRef.current = q;
6054
+ setPending(q);
6055
+ pendingTimer.current = setTimeout(clearPending, 4e3);
6056
+ }
6057
+ emitQuality(from, q, "manual");
6058
+ };
6059
+ const qualityLabel = selected === "auto" ? live ? `Auto (${live}p)` : "Auto" : `${selected}p`;
6060
+ const resolvedPosterUrl = posterUrl ?? streamPosterUrl(host, uid, posterTime);
6061
+ const posterWidth = variant === "16x9" ? 1280 : 720;
6062
+ const posterHeight = variant === "16x9" ? 720 : 1280;
6063
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
6064
+ "div",
6065
+ {
6066
+ ref: root,
6067
+ className: `tv-skin ${variant === "16x9" ? "tv-skin-desktop" : "tv-skin-mobile"} ${className ?? ""}`,
6068
+ "data-video-id": videoId,
6069
+ children: [
6070
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("media-controller", { class: "absolute inset-0 block", autohide: menu ? "-1" : "2", children: [
6071
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6072
+ "video",
6073
+ {
6074
+ ref: video,
6075
+ slot: "media",
6076
+ style: { width: "100%", height: "100%", backgroundColor: "#000", objectFit: "contain" },
6077
+ preload: "none",
6078
+ playsInline: true,
6079
+ title,
6080
+ "aria-label": title,
6081
+ "data-video-id": videoId,
6082
+ onPlay: () => {
6083
+ const v = video.current;
6084
+ const cur = roundVideoSeconds(v?.currentTime ?? 0);
6085
+ activeSegmentStartRef.current = cur;
6086
+ playCountRef.current += 1;
6087
+ dirtySinceLastEmitRef.current = true;
6088
+ const s = snap();
6089
+ const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;
6090
+ if (activeClient) {
6091
+ const path = typeof window === "undefined" ? "/" : window.location.pathname;
6092
+ activeClient.trackEvent({
6093
+ eventType: "video_play",
6094
+ metadata: {
6095
+ video_id: id,
6096
+ variant,
6097
+ current_s: s.currentTime,
6098
+ duration_s: s.duration,
6099
+ play_sequence: playCountRef.current,
6100
+ page: { path }
6101
+ }
6102
+ });
6103
+ }
6104
+ cbRef.current.onPlay?.(s);
6105
+ },
6106
+ onPlaying: () => setPlaying(true),
6107
+ onPause: () => {
6108
+ const v = video.current;
6109
+ if (v?.ended) return;
6110
+ commitActiveSegment(v?.currentTime);
6111
+ const s = snap();
6112
+ emitProgress("pause", s);
6113
+ cbRef.current.onPause?.(s);
6114
+ },
6115
+ onEnded: () => {
6116
+ const v = video.current;
6117
+ const dur = typeof v?.duration === "number" && Number.isFinite(v.duration) ? v.duration : durationRef.current;
6118
+ commitActiveSegment(dur > 0 ? dur : v?.currentTime);
6119
+ maxPercentRef.current = 100;
6120
+ const s = snap();
6121
+ if (!firedCheckpointsRef.current.has(100) && checkpoints.includes(100)) {
6122
+ firedCheckpointsRef.current.add(100);
6123
+ cbRef.current.onCheckpoint?.({ ...s, checkpoint: 100 });
6124
+ }
6125
+ emitProgress("ended", s);
6126
+ cbRef.current.onEnded?.(s);
6127
+ },
6128
+ onWaiting: () => cbRef.current.onWaiting?.(snap()),
6129
+ onTimeUpdate: () => {
6130
+ const v = video.current;
6131
+ if (v && !v.seeking) {
6132
+ if (activeSegmentStartRef.current === null && !v.paused) {
6133
+ activeSegmentStartRef.current = lastTime.current;
6134
+ }
6135
+ lastTime.current = v.currentTime;
6136
+ dirtySinceLastEmitRef.current = true;
6137
+ }
6138
+ const s = snap();
6139
+ let hitCheckpoint = false;
6140
+ let hitGoal = false;
6141
+ for (const goal of goalsRef.current) {
6142
+ if (firedGoalsRef.current.has(goal.key)) continue;
6143
+ const met = goal.metric === "seconds" ? s.watchedSeconds >= goal.threshold : s.watchedPercent >= goal.threshold;
6144
+ if (met) {
6145
+ firedGoalsRef.current.add(goal.key);
6146
+ hitGoal = true;
6147
+ }
6148
+ }
6149
+ const newlyFiredCheckpoints = [];
6150
+ for (const cp of checkpoints) {
6151
+ if (firedCheckpointsRef.current.has(cp)) continue;
6152
+ if (s.watchedPercent >= cp || s.maxPercent >= cp) {
6153
+ firedCheckpointsRef.current.add(cp);
6154
+ newlyFiredCheckpoints.push(cp);
6155
+ hitCheckpoint = true;
6156
+ }
6157
+ }
6158
+ if (hitCheckpoint || hitGoal) {
6159
+ commitActiveSegment(v?.currentTime);
6160
+ if (v && !v.paused) activeSegmentStartRef.current = v.currentTime;
6161
+ const emittedSnap = snap();
6162
+ emitProgress(hitCheckpoint ? "checkpoint" : "goal", emittedSnap);
6163
+ for (const cp of newlyFiredCheckpoints) {
6164
+ cbRef.current.onCheckpoint?.({ ...emittedSnap, checkpoint: cp });
6165
+ }
6166
+ }
6167
+ cbRef.current.onTimeUpdate?.(s);
6168
+ },
6169
+ onSeeking: () => {
6170
+ seekFrom.current = lastTime.current;
6171
+ commitActiveSegment(seekFrom.current);
6172
+ },
6173
+ onSeeked: () => {
6174
+ const v = video.current;
6175
+ const to = roundVideoSeconds(v?.currentTime ?? 0);
6176
+ const from = roundVideoSeconds(seekFrom.current);
6177
+ lastTime.current = to;
6178
+ if (v && !v.paused) {
6179
+ activeSegmentStartRef.current = to;
6180
+ }
6181
+ if (Math.abs(to - from) >= 1) {
6182
+ seekCountRef.current += 1;
6183
+ const s = snap();
6184
+ emitProgress("seek", s);
6185
+ cbRef.current.onSeek?.({ ...s, from, to });
6186
+ }
6187
+ },
6188
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("track", { kind: "captions" })
6189
+ }
6190
+ ),
6191
+ ready && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
6192
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("media-loading-indicator", { slot: "centered-chrome" }),
6193
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "tv-chrome", children: [
6194
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "tv-row", children: [
6195
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("media-play-button", { class: "tv-ctl", children: [
6196
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconPlay, { slot: "play" }),
6197
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconPause, { slot: "pause" })
6198
+ ] }),
6199
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("media-time-display", { class: "tv-time tv-time-full", showduration: true }),
6200
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("media-time-display", { class: "tv-time tv-time-cur" }),
6201
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "tv-spacer" }),
6202
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "tv-vol", children: [
6203
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("media-mute-button", { class: "tv-ctl", children: [
6204
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconVolumeHigh, { slot: "high" }),
6205
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconVolumeLow, { slot: "medium" }),
6206
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconVolumeLow, { slot: "low" }),
6207
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconVolumeOff, { slot: "off" })
6208
+ ] }),
6209
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("media-volume-range", { class: "tv-volrange" })
6210
+ ] }),
6211
+ renditions.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "tv-menu-wrap", ref: wrap, children: [
6212
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6213
+ "button",
6214
+ {
6215
+ ref: gear,
6216
+ type: "button",
6217
+ className: "tv-btn",
6218
+ "aria-haspopup": "menu",
6219
+ "aria-expanded": menu,
6220
+ "aria-label": `Settings, quality ${qualityLabel}`,
6221
+ title: "Settings",
6222
+ onClick: () => setMenu((m) => !m),
6223
+ onKeyDown: onMenuKey,
6224
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconSettings, {})
6225
+ }
6226
+ ),
6227
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "status", className: "sr-only", children: pending !== null ? `Switching to ${pending}p` : "" }),
6228
+ menu && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
6229
+ "ul",
6230
+ {
6231
+ role: "menu",
6232
+ "aria-label": "Quality",
6233
+ className: "tv-menu",
6234
+ onKeyDown: onMenuKey,
6235
+ children: [
6236
+ ["auto", ...renditions.map((r) => r.height)].map(
6237
+ (q) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { role: "none", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6238
+ "button",
6239
+ {
6240
+ type: "button",
6241
+ role: "menuitemradio",
6242
+ "aria-checked": selected === q,
6243
+ className: "tv-menu-item",
6244
+ onClick: () => pick(q),
6245
+ children: q === "auto" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
6246
+ "Auto",
6247
+ live ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "tv-menu-sub", children: [
6248
+ " ",
6249
+ live,
6250
+ "p"
6251
+ ] }) : null
6252
+ ] }) : `${q}p`
6253
+ }
6254
+ ) }, String(q))
6255
+ ),
6256
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { "aria-hidden": "true", className: "tv-menu-note", children: pending !== null ? `Switching to ${pending}p\u2026` : "" })
6257
+ ]
6258
+ }
6259
+ )
6260
+ ] }),
6261
+ variant === "16x9" && onTheaterChange && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6262
+ "button",
6263
+ {
6264
+ type: "button",
6265
+ className: "tv-btn tv-theater",
6266
+ "aria-pressed": theater,
6267
+ "aria-label": theater ? "Shrink the player" : "Expand the player",
6268
+ title: theater ? "Shrink" : "Expand",
6269
+ onClick: () => onTheaterChange(!theater),
6270
+ children: theater ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconTheaterShrink, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconTheaterExpand, {})
6271
+ }
6272
+ ),
6273
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("media-fullscreen-button", { class: "tv-ctl", children: [
6274
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconFullscreenEnter, { slot: "enter" }),
6275
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconFullscreenExit, { slot: "exit" })
6276
+ ] })
6277
+ ] }),
6278
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("media-time-range", { class: "tv-seek" })
6279
+ ] })
6280
+ ] })
6281
+ ] }),
6282
+ !playing && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "tv-poster-overlay", children: [
6283
+ renderPoster ? renderPoster({
6284
+ src: resolvedPosterUrl,
6285
+ variant,
6286
+ title,
6287
+ width: posterWidth,
6288
+ height: posterHeight
6289
+ }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6290
+ "img",
6291
+ {
6292
+ src: resolvedPosterUrl,
6293
+ alt: "",
6294
+ width: posterWidth,
6295
+ height: posterHeight,
6296
+ loading: "lazy",
6297
+ decoding: "async",
6298
+ className: "tv-poster-img"
6299
+ }
6300
+ ),
6301
+ !started && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6302
+ "button",
6303
+ {
6304
+ type: "button",
6305
+ onClick: start,
6306
+ "aria-label": `Play video: ${title}`,
6307
+ className: "tv-play-trigger",
6308
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "tv-play-pill", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconPlay, { size: 26, filled: true }) })
6309
+ }
6310
+ )
6311
+ ] })
6312
+ ]
6313
+ }
6314
+ );
6315
+ }
6316
+ function TrackedVideo(props) {
6317
+ const {
6318
+ id,
6319
+ video,
6320
+ host,
6321
+ sources,
6322
+ title,
6323
+ posterTime,
6324
+ posterUrl,
6325
+ renderPoster,
6326
+ theater: controlledTheater,
6327
+ defaultTheater = false,
6328
+ onTheaterChange,
6329
+ expandMode = "inline",
6330
+ autoExpandOnPlay = true,
6331
+ expandedMaxWidth = 1100,
6332
+ checkpoints = DEFAULT_VIDEO_CHECKPOINTS,
6333
+ trackingConfig,
6334
+ fallbackConfig,
6335
+ client = null,
6336
+ loadPlayerLibs,
6337
+ className = "",
6338
+ style,
6339
+ ...callbacks
6340
+ } = props;
6341
+ const videoKey = id ?? video ?? fallbackConfig?.key ?? "";
6342
+ const r2Video = useTrackedVideoConfig(videoKey || void 0, trackingConfig, fallbackConfig);
6343
+ const videoGoals = useActiveVideoGoals(videoKey, trackingConfig);
6344
+ const [internalTheater, setInternalTheater] = (0, import_react8.useState)(defaultTheater);
6345
+ const isTheaterControlled = controlledTheater !== void 0;
6346
+ const activeTheater = isTheaterControlled ? controlledTheater : internalTheater;
6347
+ const handleTheaterChange = (0, import_react8.useCallback)(
6348
+ (next) => {
6349
+ if (!isTheaterControlled) {
6350
+ setInternalTheater(next);
6351
+ }
6352
+ onTheaterChange?.(next);
6353
+ },
6354
+ [isTheaterControlled, onTheaterChange]
6355
+ );
6356
+ const resolvedHost = host ?? r2Video?.host ?? "";
6357
+ const desktopUid = sources?.desktop ?? r2Video?.sources.desktop ?? "";
6358
+ const mobileUid = sources?.mobile ?? r2Video?.sources.mobile ?? desktopUid;
6359
+ const resolvedTitle = title ?? r2Video?.title ?? videoKey;
6360
+ const resolvedPosterTime = posterTime ?? r2Video?.poster_time ?? r2Video?.poster_time_s ?? 4;
6361
+ if (!videoKey || !resolvedHost || !desktopUid) {
6362
+ return null;
6363
+ }
6364
+ const showTheaterControl = expandMode !== "none" && (expandMode === "theater" || Boolean(onTheaterChange));
6365
+ const wrapperStyle = !isTheaterControlled && expandMode === "theater" && activeTheater ? {
6366
+ maxWidth: typeof expandedMaxWidth === "number" ? `${expandedMaxWidth}px` : expandedMaxWidth,
6367
+ marginLeft: "auto",
6368
+ marginRight: "auto",
6369
+ width: "100%",
6370
+ transition: "max-width 360ms cubic-bezier(0.2, 0.7, 0.2, 1)",
6371
+ ...style
6372
+ } : style;
6373
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className, style: wrapperStyle, "data-tracked-video": videoKey, children: [
6374
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6375
+ Cut,
6376
+ {
6377
+ ...callbacks,
6378
+ id: videoKey,
6379
+ variant: "16x9",
6380
+ uid: desktopUid,
6381
+ host: resolvedHost,
6382
+ title: resolvedTitle,
6383
+ posterTime: resolvedPosterTime,
6384
+ posterUrl,
6385
+ renderPoster,
6386
+ theater: activeTheater,
6387
+ onTheaterChange: showTheaterControl ? handleTheaterChange : void 0,
6388
+ autoExpandOnPlay,
6389
+ checkpoints,
6390
+ videoGoals,
6391
+ client,
6392
+ loadPlayerLibs
6393
+ }
6394
+ ),
6395
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6396
+ Cut,
6397
+ {
6398
+ ...callbacks,
6399
+ id: videoKey,
6400
+ variant: "9x16",
6401
+ uid: mobileUid || desktopUid,
6402
+ host: resolvedHost,
6403
+ title: resolvedTitle,
6404
+ posterTime: resolvedPosterTime,
6405
+ posterUrl,
6406
+ renderPoster,
6407
+ theater: false,
6408
+ onTheaterChange: void 0,
6409
+ autoExpandOnPlay: false,
6410
+ checkpoints,
6411
+ videoGoals,
6412
+ client,
6413
+ loadPlayerLibs
6414
+ }
6415
+ )
6416
+ ] });
6417
+ }
6418
+ var STAGE_DURATION_MS = 360;
6419
+ var STAGE_EASE = "cubic-bezier(0.2, 0.7, 0.2, 1)";
6420
+ function TrackedVideoStage({
6421
+ video,
6422
+ children,
6423
+ expandedMaxWidth = 1100,
6424
+ collapsedTextMaxWidth = 760,
6425
+ className = ""
6426
+ }) {
6427
+ const [theater, setTheater] = (0, import_react8.useState)(video.defaultTheater ?? false);
6428
+ const videoBox = (0, import_react8.useRef)(null);
6429
+ const textBox = (0, import_react8.useRef)(null);
6430
+ const first = (0, import_react8.useRef)(null);
6431
+ const activeTheater = video.theater !== void 0 ? video.theater : theater;
6432
+ const change = (0, import_react8.useCallback)(
6433
+ (next) => {
6434
+ if (next === activeTheater) return;
6435
+ if (next && typeof window !== "undefined" && !window.matchMedia("(min-width: 768px)").matches) {
6436
+ return;
6437
+ }
6438
+ if (videoBox.current && textBox.current) {
6439
+ first.current = {
6440
+ v: videoBox.current.getBoundingClientRect(),
6441
+ t: textBox.current.getBoundingClientRect()
6442
+ };
6443
+ }
6444
+ if (video.theater === void 0) {
6445
+ setTheater(next);
6446
+ }
6447
+ video.onTheaterChange?.(next);
6448
+ },
6449
+ [activeTheater, video]
6450
+ );
6451
+ (0, import_react8.useLayoutEffect)(() => {
6452
+ if (typeof window === "undefined") return;
6453
+ const f = first.current;
6454
+ const vb = videoBox.current;
6455
+ const tb = textBox.current;
6456
+ first.current = null;
6457
+ if (!f || !vb || !tb) return;
6458
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
6459
+ const lv = vb.getBoundingClientRect();
6460
+ const lt = tb.getBoundingClientRect();
6461
+ if (!reduce && typeof vb.animate === "function" && lv.width > 0) {
6462
+ const s = f.v.width / lv.width;
6463
+ vb.animate(
6464
+ [
6465
+ {
6466
+ transformOrigin: "top left",
6467
+ transform: `translate(${f.v.left - lv.left}px, ${f.v.top - lv.top}px) scale(${s})`
6468
+ },
6469
+ { transformOrigin: "top left", transform: "none" }
6470
+ ],
6471
+ { duration: STAGE_DURATION_MS, easing: STAGE_EASE }
6472
+ );
6473
+ tb.animate(
6474
+ [
6475
+ {
6476
+ transform: `translate(${f.t.left - lt.left}px, ${f.t.top - lt.top}px)`,
6477
+ opacity: 0.4
6478
+ },
6479
+ { transform: "none", opacity: 1 }
6480
+ ],
6481
+ { duration: STAGE_DURATION_MS, easing: STAGE_EASE }
6482
+ );
6483
+ }
6484
+ if (lv.top < 80 || lv.bottom > window.innerHeight - 24) {
6485
+ const top = window.scrollY + lv.top - Math.max(96, (window.innerHeight - lv.height) / 2);
6486
+ window.scrollTo({ top, behavior: reduce ? "auto" : "smooth" });
6487
+ }
6488
+ }, [activeTheater]);
6489
+ const maxVideoWidthCss = typeof expandedMaxWidth === "number" ? `${expandedMaxWidth}px` : expandedMaxWidth;
6490
+ const maxTextWidthCss = typeof collapsedTextMaxWidth === "number" ? `${collapsedTextMaxWidth}px` : collapsedTextMaxWidth;
6491
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
6492
+ "div",
6493
+ {
6494
+ className: activeTheater ? `grid gap-10 md:gap-12 ${className}` : `grid items-start gap-10 lg:grid-cols-[1fr_1fr] lg:gap-16 ${className}`,
6495
+ children: [
6496
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6497
+ "div",
6498
+ {
6499
+ ref: textBox,
6500
+ className: activeTheater ? "md:order-2" : "",
6501
+ style: activeTheater ? { maxWidth: maxTextWidthCss } : void 0,
6502
+ children
6503
+ }
6504
+ ),
6505
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6506
+ "div",
6507
+ {
6508
+ ref: videoBox,
6509
+ className: activeTheater ? "md:order-1 md:mx-auto md:w-full" : "",
6510
+ style: activeTheater ? { maxWidth: maxVideoWidthCss, width: "100%" } : void 0,
6511
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6512
+ TrackedVideo,
6513
+ {
6514
+ ...video,
6515
+ theater: activeTheater,
6516
+ onTheaterChange: change,
6517
+ className: video.className ?? "w-full"
6518
+ }
6519
+ )
6520
+ }
6521
+ )
6522
+ ]
6523
+ }
6524
+ );
6525
+ }
4808
6526
  // Annotate the CommonJS export names for ESM import in node:
4809
6527
  0 && (module.exports = {
4810
6528
  AdPlatformTracking,
4811
6529
  BookingHandoffLink,
4812
6530
  ConsentBanner,
6531
+ DEFAULT_VIDEO_CHECKPOINTS,
4813
6532
  GoogleAdsTracking,
4814
6533
  PhoneField,
6534
+ TrackedVideo,
6535
+ TrackedVideoStage,
4815
6536
  createLeadFormSubmit,
4816
6537
  createTracking,
6538
+ streamManifestUrl,
6539
+ streamPosterUrl,
4817
6540
  useBookingHandoffUrl,
4818
6541
  useBookingIntegration,
4819
6542
  useBookingSlots,
@@ -4824,6 +6547,7 @@ function useBookingSubmit(bookings, options = {}) {
4824
6547
  useGclid,
4825
6548
  usePhoneConfig,
4826
6549
  usePhoneField,
6550
+ useTrackedVideoConfig,
4827
6551
  useTrackingParams
4828
6552
  });
4829
6553
  //# sourceMappingURL=client.js.map