@aranova/tracking-react 1.18.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.mjs CHANGED
@@ -255,7 +255,8 @@ var TRACKING_CAPABILITIES = [
255
255
  "form_capture",
256
256
  "phone_click_capture",
257
257
  "phone_fields",
258
- "typed_form_submit"
258
+ "typed_form_submit",
259
+ "video_tracking"
259
260
  ];
260
261
  var CAPABILITY_DOM_ATTRIBUTE = "data-aranova-capability";
261
262
  var registered = /* @__PURE__ */ new Set();
@@ -1036,8 +1037,48 @@ function parseTrigger(value) {
1036
1037
  if (typeof t.threshold_seconds === "number") spec.threshold_seconds = t.threshold_seconds;
1037
1038
  if (typeof t.page_threshold === "number") spec.page_threshold = t.page_threshold;
1038
1039
  if (typeof t.page_name === "string") spec.page_name = t.page_name;
1040
+ if (t.metric === "percent" || t.metric === "seconds") spec.metric = t.metric;
1041
+ if (typeof t.threshold === "number") spec.threshold = t.threshold;
1042
+ if (typeof t.video_id === "string" || t.video_id === null) spec.video_id = t.video_id;
1039
1043
  return spec;
1040
1044
  }
1045
+ function parseVideoEntry(fallbackKey2, entry) {
1046
+ const key = typeof entry.key === "string" && entry.key ? entry.key : fallbackKey2;
1047
+ if (!key) return null;
1048
+ const host = typeof entry.host === "string" ? entry.host : "";
1049
+ const title = typeof entry.title === "string" && entry.title ? entry.title : key;
1050
+ const sourcesRaw = isRecord(entry.sources) ? entry.sources : null;
1051
+ const desktop = sourcesRaw && typeof sourcesRaw.desktop === "string" ? sourcesRaw.desktop : typeof entry.desktop_uid === "string" ? entry.desktop_uid : "";
1052
+ if (!host || !desktop) return null;
1053
+ const mobile = sourcesRaw && typeof sourcesRaw.mobile === "string" ? sourcesRaw.mobile : typeof entry.mobile_uid === "string" ? entry.mobile_uid : null;
1054
+ const posterTime = typeof entry.poster_time === "number" ? entry.poster_time : typeof entry.poster_time_s === "number" ? entry.poster_time_s : 4;
1055
+ return {
1056
+ key,
1057
+ title,
1058
+ host,
1059
+ sources: { desktop, mobile },
1060
+ poster_time: posterTime,
1061
+ duration_s: typeof entry.duration_s === "number" ? entry.duration_s : null
1062
+ };
1063
+ }
1064
+ function parseVideos(value) {
1065
+ const out = {};
1066
+ if (Array.isArray(value)) {
1067
+ for (const item of value) {
1068
+ if (!isRecord(item)) continue;
1069
+ const parsed = parseVideoEntry("", item);
1070
+ if (parsed) out[parsed.key] = parsed;
1071
+ }
1072
+ return out;
1073
+ }
1074
+ if (!isRecord(value)) return out;
1075
+ for (const [key, entry] of Object.entries(value)) {
1076
+ if (!isRecord(entry)) continue;
1077
+ const parsed = parseVideoEntry(key, entry);
1078
+ if (parsed) out[parsed.key] = parsed;
1079
+ }
1080
+ return out;
1081
+ }
1041
1082
  function parseConversionConfig(raw) {
1042
1083
  if (!raw || typeof raw !== "object") return null;
1043
1084
  const obj = raw;
@@ -1088,7 +1129,8 @@ function parseConversionConfig(raw) {
1088
1129
  goals,
1089
1130
  linked_hostnames: Array.isArray(obj.linked_hostnames) ? obj.linked_hostnames.filter((h) => typeof h === "string" && h.length > 0) : [],
1090
1131
  integrations: parseIntegrations(obj.integrations),
1091
- lead_forms: parseLeadForms(obj.lead_forms)
1132
+ lead_forms: parseLeadForms(obj.lead_forms),
1133
+ videos: parseVideos(obj.videos)
1092
1134
  };
1093
1135
  }
1094
1136
  function parseLeadForms(value) {
@@ -1334,6 +1376,21 @@ function automaticThresholdMet(goal, eventType, metadata) {
1334
1376
  case "form_start":
1335
1377
  case "phone_click":
1336
1378
  return true;
1379
+ case "video_play":
1380
+ return trigger.video_id == null || hasStringField(metadata, "video_id") && metadata.video_id === trigger.video_id;
1381
+ case "video_progress": {
1382
+ if (trigger.video_id != null && (!hasStringField(metadata, "video_id") || metadata.video_id !== trigger.video_id)) {
1383
+ return false;
1384
+ }
1385
+ if (typeof trigger.threshold !== "number") return false;
1386
+ if (trigger.metric === "seconds") {
1387
+ return hasNumberField(metadata, "watched_s") && metadata.watched_s >= trigger.threshold;
1388
+ }
1389
+ if (trigger.metric === "percent") {
1390
+ return hasNumberField(metadata, "max_percent") && metadata.max_percent >= trigger.threshold || hasNumberField(metadata, "watched_percent") && metadata.watched_percent >= trigger.threshold;
1391
+ }
1392
+ return false;
1393
+ }
1337
1394
  default:
1338
1395
  return false;
1339
1396
  }
@@ -1370,6 +1427,9 @@ var RETRY_BASE_MS = 5e3;
1370
1427
  var RETRY_MAX_MS = 5 * 6e4;
1371
1428
  var runtimes = /* @__PURE__ */ new Map();
1372
1429
  var configuredIds = /* @__PURE__ */ new Set();
1430
+ var EMPTY_GOALS = [];
1431
+ var EMPTY_STRINGS = [];
1432
+ var EMPTY_VIDEOS = {};
1373
1433
  var scriptLoad = null;
1374
1434
  var jsInitialized = false;
1375
1435
  function cacheKey2(url) {
@@ -1560,11 +1620,11 @@ var TrackingConfigRuntime = class {
1560
1620
  void this.flush();
1561
1621
  }
1562
1622
  listGoals() {
1563
- return this.current?.goals ?? [];
1623
+ return this.current?.goals ?? EMPTY_GOALS;
1564
1624
  }
1565
1625
  /** Sibling hostnames for the linker — read from the cached object even while unconfirmed. */
1566
1626
  linkedHostnames() {
1567
- return this.current?.linked_hostnames ?? [];
1627
+ return this.current?.linked_hostnames ?? EMPTY_STRINGS;
1568
1628
  }
1569
1629
  /**
1570
1630
  * Integration blocks, from the cached object even while unconfirmed: the booking fallback
@@ -1577,6 +1637,14 @@ var TrackingConfigRuntime = class {
1577
1637
  leadForms() {
1578
1638
  return this.current?.lead_forms ?? null;
1579
1639
  }
1640
+ /** Configured Cloudflare Stream tracked videos for this site. */
1641
+ videos() {
1642
+ return this.current?.videos ?? EMPTY_VIDEOS;
1643
+ }
1644
+ /** Look up one configured Cloudflare Stream tracked video by key. */
1645
+ getVideo(key) {
1646
+ return this.current?.videos?.[key] ?? null;
1647
+ }
1580
1648
  async revalidateNow() {
1581
1649
  try {
1582
1650
  const headers = {};
@@ -1733,6 +1801,16 @@ var TrackingConfigRuntime = class {
1733
1801
  this.scheduleRetry();
1734
1802
  return;
1735
1803
  }
1804
+ if (outcome === "fired" && typeof window !== "undefined" && typeof window.fbq === "function") {
1805
+ try {
1806
+ window.fbq("trackCustom", item.key, {
1807
+ ...value != null ? { value } : {},
1808
+ ...currency ? { currency } : {},
1809
+ ...item.transactionId ? { transaction_id: item.transactionId } : {}
1810
+ });
1811
+ } catch {
1812
+ }
1813
+ }
1736
1814
  this.conversionQueue.shift();
1737
1815
  }
1738
1816
  }
@@ -1876,12 +1954,24 @@ function createConversionAutoFire(store) {
1876
1954
  const firing = goal.firing;
1877
1955
  const cents = firing.value_cents ?? null;
1878
1956
  const currency = firing.currency ?? null;
1879
- fireConversionWithConsent({
1957
+ const value = cents != null && currency ? fromMinor(cents, currency) : null;
1958
+ const outcome = fireConversionWithConsent({
1880
1959
  sendTo: firing.send_to,
1881
- value: cents != null && currency ? fromMinor(cents, currency) : null,
1960
+ value,
1882
1961
  currency,
1883
1962
  transactionId
1884
1963
  });
1964
+ if (outcome === "fired" && typeof window !== "undefined" && typeof window.fbq === "function") {
1965
+ try {
1966
+ window.fbq("trackCustom", goal.key, {
1967
+ event_type: eventType,
1968
+ ...value != null ? { value } : {},
1969
+ ...currency ? { currency } : {},
1970
+ transaction_id: transactionId
1971
+ });
1972
+ } catch {
1973
+ }
1974
+ }
1885
1975
  }
1886
1976
  }
1887
1977
  return {
@@ -2014,6 +2104,18 @@ var EVENT_SEMANTICS = {
2014
2104
  category: "page",
2015
2105
  outcomeRole: "navigation",
2016
2106
  clientVisibility: "simple"
2107
+ },
2108
+ video_play: {
2109
+ label: "Video played",
2110
+ category: "engagement",
2111
+ outcomeRole: "engagement",
2112
+ clientVisibility: "simple"
2113
+ },
2114
+ video_progress: {
2115
+ label: "Video watched",
2116
+ category: "engagement",
2117
+ outcomeRole: "engagement",
2118
+ clientVisibility: "detailed"
2017
2119
  }
2018
2120
  };
2019
2121
  function getEventSemantics(eventName) {
@@ -2601,14 +2703,31 @@ function createTrackingClient(config) {
2601
2703
  metadata: input.metadata ?? null,
2602
2704
  occurred_at: occurredAt
2603
2705
  });
2604
- if (queue.length >= maxQueueSize || input.eventType === "form_details" || getEventSemantics(input.eventType)?.outcomeRole === "lead") {
2706
+ const triggerReason = input.metadata?.trigger;
2707
+ const isUrgentVideoEvent = input.eventType === "video_play" || input.eventType === "video_progress" && (triggerReason === "ended" || triggerReason === "goal" || triggerReason === "checkpoint");
2708
+ if (queue.length >= maxQueueSize || input.eventType === "form_details" || getEventSemantics(input.eventType)?.outcomeRole === "lead" || isUrgentVideoEvent) {
2605
2709
  void flush();
2606
2710
  } else {
2607
2711
  scheduleFlush();
2608
2712
  }
2609
2713
  }
2714
+ const beforeUnloadListeners = /* @__PURE__ */ new Set();
2715
+ let runningBeforeUnload = false;
2610
2716
  function flushOnUnload() {
2611
2717
  if (destroyed) return;
2718
+ if (!runningBeforeUnload && beforeUnloadListeners.size > 0) {
2719
+ runningBeforeUnload = true;
2720
+ try {
2721
+ for (const listener of beforeUnloadListeners) {
2722
+ try {
2723
+ listener();
2724
+ } catch {
2725
+ }
2726
+ }
2727
+ } finally {
2728
+ runningBeforeUnload = false;
2729
+ }
2730
+ }
2612
2731
  bufferQueued();
2613
2732
  clearScheduledFlush();
2614
2733
  if (pending.length === 0) return;
@@ -2629,6 +2748,12 @@ function createTrackingClient(config) {
2629
2748
  trackEvent,
2630
2749
  flush,
2631
2750
  flushBeacon: flushOnUnload,
2751
+ onBeforeUnload: (listener) => {
2752
+ beforeUnloadListeners.add(listener);
2753
+ return () => {
2754
+ beforeUnloadListeners.delete(listener);
2755
+ };
2756
+ },
2632
2757
  getSessionId: () => sessionId,
2633
2758
  getVisitorId: () => visitorId,
2634
2759
  destroy: () => {
@@ -2639,6 +2764,7 @@ function createTrackingClient(config) {
2639
2764
  destroyed = true;
2640
2765
  clearScheduledFlush();
2641
2766
  queue = [];
2767
+ beforeUnloadListeners.clear();
2642
2768
  if (typeof window !== "undefined") {
2643
2769
  window.removeEventListener("pagehide", flushOnUnload);
2644
2770
  window.removeEventListener("visibilitychange", onVisibilityChange);
@@ -2939,6 +3065,86 @@ var timeOnSiteConfigSchema = z15.strictObject({
2939
3065
  thresholdSeconds: z15.int().check(z15.positive())
2940
3066
  });
2941
3067
 
3068
+ // ../tracking-core/src/events/video.ts
3069
+ import * as z16 from "zod/mini";
3070
+
3071
+ // ../tracking-core/src/video-ranges.ts
3072
+ var VIDEO_ID_MAX_LENGTH = 64;
3073
+ var VIDEO_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
3074
+ function roundVideoSeconds(value) {
3075
+ if (!Number.isFinite(value) || value < 0) return 0;
3076
+ return Math.round(value * 100) / 100;
3077
+ }
3078
+ function mergeVideoRanges(ranges, epsilonSeconds = 0.25) {
3079
+ const valid2 = ranges.filter((r) => Number.isFinite(r.start_s) && Number.isFinite(r.end_s) && r.end_s > r.start_s).map((r) => ({
3080
+ start_s: roundVideoSeconds(r.start_s),
3081
+ end_s: roundVideoSeconds(r.end_s)
3082
+ })).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);
3083
+ if (valid2.length === 0) return [];
3084
+ const merged = [{ ...valid2[0] }];
3085
+ for (let i = 1; i < valid2.length; i += 1) {
3086
+ const cur = valid2[i];
3087
+ const last = merged[merged.length - 1];
3088
+ if (cur.start_s <= last.end_s + epsilonSeconds) {
3089
+ last.end_s = roundVideoSeconds(Math.max(last.end_s, cur.end_s));
3090
+ } else {
3091
+ merged.push({ ...cur });
3092
+ }
3093
+ }
3094
+ return merged;
3095
+ }
3096
+ function sumVideoRangesSeconds(ranges, durationSeconds = 0) {
3097
+ const merged = mergeVideoRanges(ranges);
3098
+ const raw = merged.reduce((acc, r) => acc + Math.max(0, r.end_s - r.start_s), 0);
3099
+ const clamped = durationSeconds > 0 ? Math.min(raw, durationSeconds) : raw;
3100
+ return roundVideoSeconds(clamped);
3101
+ }
3102
+
3103
+ // ../tracking-core/src/events/video.ts
3104
+ var videoIdSchema = z16.string().check(z16.minLength(1), z16.maxLength(VIDEO_ID_MAX_LENGTH), z16.regex(VIDEO_ID_PATTERN));
3105
+ var videoVariantSchema = z16.enum(["16x9", "9x16"]);
3106
+ var videoProgressTriggerReasonSchema = z16.enum([
3107
+ "checkpoint",
3108
+ "goal",
3109
+ "pause",
3110
+ "seek",
3111
+ "ended",
3112
+ "page_exit"
3113
+ ]);
3114
+ var videoRangeSchema = z16.strictObject({
3115
+ start_s: z16.number().check(z16.gte(0)),
3116
+ end_s: z16.number().check(z16.gte(0))
3117
+ });
3118
+ var videoPlayMetadataSchema = z16.strictObject({
3119
+ video_id: videoIdSchema,
3120
+ variant: videoVariantSchema,
3121
+ current_s: z16.number().check(z16.gte(0)),
3122
+ duration_s: z16.number().check(z16.gte(0)),
3123
+ play_sequence: z16.int().check(z16.gte(1)),
3124
+ page: z16.strictObject({
3125
+ path: z16.string()
3126
+ })
3127
+ });
3128
+ var videoPlayConfigSchema = z16.strictObject({});
3129
+ var videoProgressMetadataSchema = z16.strictObject({
3130
+ video_id: videoIdSchema,
3131
+ variant: videoVariantSchema,
3132
+ trigger: videoProgressTriggerReasonSchema,
3133
+ current_s: z16.number().check(z16.gte(0)),
3134
+ furthest_s: z16.number().check(z16.gte(0)),
3135
+ watched_s: z16.number().check(z16.gte(0)),
3136
+ duration_s: z16.number().check(z16.gte(0)),
3137
+ max_percent: z16.int().check(z16.gte(0), z16.lte(100)),
3138
+ watched_percent: z16.int().check(z16.gte(0), z16.lte(100)),
3139
+ play_count: z16.int().check(z16.gte(1)),
3140
+ seek_count: z16.int().check(z16.gte(0)),
3141
+ ranges: z16.array(videoRangeSchema),
3142
+ page: z16.strictObject({
3143
+ path: z16.string()
3144
+ })
3145
+ });
3146
+ var videoProgressConfigSchema = z16.strictObject({});
3147
+
2942
3148
  // ../tracking-core/src/events/registry.ts
2943
3149
  var EVENT_REGISTRY = {
2944
3150
  // --- automatic triggers ---
@@ -3005,6 +3211,18 @@ var EVENT_REGISTRY = {
3005
3211
  metadataSchema: siteLinkArrivalMetadataSchema,
3006
3212
  configSchema: siteLinkArrivalConfigSchema
3007
3213
  },
3214
+ video_play: {
3215
+ kind: "automatic",
3216
+ semantics: EVENT_SEMANTICS.video_play,
3217
+ metadataSchema: videoPlayMetadataSchema,
3218
+ configSchema: videoPlayConfigSchema
3219
+ },
3220
+ video_progress: {
3221
+ kind: "automatic",
3222
+ semantics: EVENT_SEMANTICS.video_progress,
3223
+ metadataSchema: videoProgressMetadataSchema,
3224
+ configSchema: videoProgressConfigSchema
3225
+ },
3008
3226
  // --- manual triggers ---
3009
3227
  form_submit: {
3010
3228
  kind: "manual",
@@ -3105,6 +3323,17 @@ function currentPath2() {
3105
3323
 
3106
3324
  // ../tracking-core/src/ingest-typed.ts
3107
3325
  var clientSettings = /* @__PURE__ */ new WeakMap();
3326
+ var latestTrackingConfig;
3327
+ var latestRawClient = null;
3328
+ function setDefaultTrackingConfig(ref) {
3329
+ if (ref) latestTrackingConfig = ref;
3330
+ }
3331
+ function getActiveTrackingEnv() {
3332
+ return {
3333
+ rawClient: latestRawClient,
3334
+ trackingConfig: latestTrackingConfig
3335
+ };
3336
+ }
3108
3337
  function typedClientSettings(client) {
3109
3338
  return clientSettings.get(client) ?? {};
3110
3339
  }
@@ -3135,7 +3364,9 @@ function createTypedClient(raw, registry, options = {}) {
3135
3364
  getSessionId: raw.getSessionId.bind(raw),
3136
3365
  getVisitorId: raw.getVisitorId.bind(raw)
3137
3366
  };
3138
- clientSettings.set(client, options);
3367
+ latestRawClient = raw;
3368
+ if (options.trackingConfig) latestTrackingConfig = options.trackingConfig;
3369
+ clientSettings.set(client, { ...options, rawClient: raw });
3139
3370
  return client;
3140
3371
  }
3141
3372
 
@@ -3653,6 +3884,9 @@ function attachPageExit(client) {
3653
3884
  emitSegment(currentPath4, true);
3654
3885
  }
3655
3886
  const unsubscribeNav = onHistoryChange(onNavigate);
3887
+ const unsubscribeBeforeUnload = client.onBeforeUnload?.(() => {
3888
+ emitSegment(currentPath4, false);
3889
+ });
3656
3890
  window.addEventListener("scroll", onScroll, { passive: true });
3657
3891
  document.addEventListener("visibilitychange", onVisibilityChange);
3658
3892
  window.addEventListener("pagehide", onPageHide);
@@ -3661,6 +3895,7 @@ function attachPageExit(client) {
3661
3895
  if (rafId !== null) cancelAnimationFrame(rafId);
3662
3896
  gate.cancel();
3663
3897
  unsubscribeNav();
3898
+ unsubscribeBeforeUnload?.();
3664
3899
  window.removeEventListener("scroll", onScroll);
3665
3900
  document.removeEventListener("visibilitychange", onVisibilityChange);
3666
3901
  window.removeEventListener("pagehide", onPageHide);
@@ -3830,7 +4065,7 @@ function attachPhoneClickCapture(client, config) {
3830
4065
  }
3831
4066
 
3832
4067
  // ../tracking-core/src/lead-forms/lead-forms.gen.ts
3833
- import * as z16 from "zod/mini";
4068
+ import * as z17 from "zod/mini";
3834
4069
  var LEAD_FORM_REGISTRY_VERSION = 2;
3835
4070
  var LEAD_FORM_ROLES = [
3836
4071
  "given_name",
@@ -3856,28 +4091,28 @@ var LEAD_FORM_ROLES = [
3856
4091
  "attribution"
3857
4092
  ];
3858
4093
  var leadFormPrimitiveSchemas = /* @__PURE__ */ (() => ({
3859
- string: z16.string().check(z16.maxLength(120)),
3860
- text: z16.string().check(z16.maxLength(2e3)),
3861
- enum: z16.string().check(z16.regex(/^[a-z0-9-]{1,48}$/)),
3862
- multi_enum: z16.array(z16.string().check(z16.regex(/^[a-z0-9-]{1,48}$/))).check(z16.maxLength(10)),
3863
- bool: z16.boolean(),
3864
- date: z16.string().check(z16.regex(/^\d{4}-\d{2}-\d{2}$/)),
3865
- time_window: z16.strictObject({
3866
- start: z16.string().check(z16.regex(/^([01]\d|2[0-3]):[0-5]\d$/)),
3867
- end: z16.nullable(z16.string().check(z16.regex(/^([01]\d|2[0-3]):[0-5]\d$/))),
3868
- label: z16.optional(z16.string().check(z16.maxLength(60)))
4094
+ string: z17.string().check(z17.maxLength(120)),
4095
+ text: z17.string().check(z17.maxLength(2e3)),
4096
+ enum: z17.string().check(z17.regex(/^[a-z0-9-]{1,48}$/)),
4097
+ multi_enum: z17.array(z17.string().check(z17.regex(/^[a-z0-9-]{1,48}$/))).check(z17.maxLength(10)),
4098
+ bool: z17.boolean(),
4099
+ date: z17.string().check(z17.regex(/^\d{4}-\d{2}-\d{2}$/)),
4100
+ time_window: z17.strictObject({
4101
+ start: z17.string().check(z17.regex(/^([01]\d|2[0-3]):[0-5]\d$/)),
4102
+ end: z17.nullable(z17.string().check(z17.regex(/^([01]\d|2[0-3]):[0-5]\d$/))),
4103
+ label: z17.optional(z17.string().check(z17.maxLength(60)))
3869
4104
  }),
3870
- phone: z16.string().check(z16.regex(/^\+[1-9]\d{6,14}$/)),
3871
- email: z16.string().check(z16.maxLength(320), z16.regex(/^[^\s@]+@[^\s@]+$/)),
3872
- address: z16.strictObject({
3873
- line1: z16.string().check(z16.maxLength(120)),
3874
- line2: z16.optional(z16.string().check(z16.maxLength(120))),
3875
- city: z16.optional(z16.string().check(z16.maxLength(120))),
3876
- region: z16.optional(z16.string().check(z16.maxLength(120))),
3877
- postal_code: z16.optional(z16.string().check(z16.maxLength(120))),
3878
- country: z16.optional(z16.string().check(z16.maxLength(120)))
4105
+ phone: z17.string().check(z17.regex(/^\+[1-9]\d{6,14}$/)),
4106
+ email: z17.string().check(z17.maxLength(320), z17.regex(/^[^\s@]+@[^\s@]+$/)),
4107
+ address: z17.strictObject({
4108
+ line1: z17.string().check(z17.maxLength(120)),
4109
+ line2: z17.optional(z17.string().check(z17.maxLength(120))),
4110
+ city: z17.optional(z17.string().check(z17.maxLength(120))),
4111
+ region: z17.optional(z17.string().check(z17.maxLength(120))),
4112
+ postal_code: z17.optional(z17.string().check(z17.maxLength(120))),
4113
+ country: z17.optional(z17.string().check(z17.maxLength(120)))
3879
4114
  }),
3880
- number: z16.number()
4115
+ number: z17.number()
3881
4116
  }))();
3882
4117
  var LEAD_FORM_ROLE_SPECS = {
3883
4118
  given_name: { primitive: "string", smsLabel: "", contactIdentity: true },
@@ -4449,7 +4684,7 @@ function GoogleAdsTracking(props) {
4449
4684
  import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
4450
4685
 
4451
4686
  // package.json
4452
- var version = "1.18.0";
4687
+ var version = "1.19.0";
4453
4688
 
4454
4689
  // ../tracking-core/src/phone-react.tsx
4455
4690
  import {
@@ -4567,6 +4802,7 @@ function createTracking(options) {
4567
4802
  trackingConfig
4568
4803
  } = options;
4569
4804
  registerCapability("base_tracking");
4805
+ setDefaultTrackingConfig(trackingConfig);
4570
4806
  if (trackingConfig) registerCapability("conversion_goals_auto");
4571
4807
  if (!apiKey || !endpoint) {
4572
4808
  if (apiKey || endpoint) {
@@ -4834,14 +5070,1429 @@ function useBookingSubmit(bookings, options = {}) {
4834
5070
  const status = submitting ? "submitting" : outcome?.status ?? "idle";
4835
5071
  return { status, outcome, booked, submit, updateBooking, reset };
4836
5072
  }
5073
+
5074
+ // ../tracking-core/src/video-react.tsx
5075
+ import {
5076
+ useCallback as useCallback4,
5077
+ useEffect as useEffect6,
5078
+ useLayoutEffect,
5079
+ useMemo as useMemo6,
5080
+ useRef as useRef2,
5081
+ useState as useState5,
5082
+ useSyncExternalStore as useSyncExternalStore2
5083
+ } from "react";
5084
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
5085
+ var DEFAULT_VIDEO_CHECKPOINTS = [25, 50, 75, 90, 100];
5086
+ var streamManifestUrl = (host, uid) => `https://${host}/${uid}/manifest/video.m3u8`;
5087
+ var streamPosterUrl = (host, uid, time = 4) => `https://${host}/${uid}/thumbnails/thumbnail.jpg?time=${time}s`;
5088
+ var TV_SKIN_STYLE_ATTR = "data-aranova-tv-skin";
5089
+ var TV_SKIN_CSS = `
5090
+ .tv-skin {
5091
+ container-type: inline-size;
5092
+ position: relative;
5093
+ overflow: hidden;
5094
+ border-radius: 18px;
5095
+ border: 1px solid rgba(255, 255, 255, 0.12);
5096
+ background: #0a0e1a;
5097
+ box-shadow: 0 18px 48px rgba(0, 0, 0, 0.32);
5098
+ }
5099
+ .tv-skin-desktop {
5100
+ display: none;
5101
+ aspect-ratio: 16 / 9;
5102
+ width: 100%;
5103
+ }
5104
+ .tv-skin-mobile {
5105
+ display: block;
5106
+ margin-left: auto;
5107
+ margin-right: auto;
5108
+ aspect-ratio: 9 / 16;
5109
+ width: 100%;
5110
+ max-width: 420px;
5111
+ }
5112
+ @media (min-width: 768px) {
5113
+ .tv-skin-desktop {
5114
+ display: block;
5115
+ }
5116
+ .tv-skin-mobile {
5117
+ display: none;
5118
+ }
5119
+ }
5120
+ .tv-skin media-controller {
5121
+ display: block;
5122
+ width: 100%;
5123
+ height: 100%;
5124
+ background: #000;
5125
+ --media-primary-color: #ffffff;
5126
+ --media-text-color: rgba(255, 255, 255, 0.82);
5127
+ --media-icon-color: #ffffff;
5128
+ --media-secondary-color: transparent;
5129
+ --media-control-background: transparent;
5130
+ --media-control-hover-background: transparent;
5131
+ --media-control-padding: 0;
5132
+ --media-control-height: 40px;
5133
+ --media-tooltip-display: none;
5134
+ --media-font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
5135
+ --media-font-size: 11.5px;
5136
+ --media-focus-box-shadow: 0 0 0 2px #74a0ff;
5137
+ --media-button-icon-width: 20px;
5138
+ --media-button-icon-height: 20px;
5139
+ --media-range-padding: 0;
5140
+ --media-range-bar-color: #ffffff;
5141
+ --media-range-track-background: rgba(255, 255, 255, 0.25);
5142
+ --media-range-track-border-radius: 3px;
5143
+ --media-time-range-buffered-color: transparent;
5144
+ --media-time-range-hover-display: none;
5145
+ --media-preview-time-background: transparent;
5146
+ --media-range-thumb-background: #ffffff;
5147
+ --media-range-thumb-width: 12px;
5148
+ --media-range-thumb-height: 12px;
5149
+ --media-range-thumb-opacity: 0;
5150
+ }
5151
+ .tv-skin media-controller::part(vertical-layer),
5152
+ .tv-skin media-controller::part(centered-layer) {
5153
+ background: transparent;
5154
+ }
5155
+ .tv-chrome {
5156
+ position: absolute;
5157
+ inset: auto 0 0 0;
5158
+ padding-top: 56px;
5159
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0));
5160
+ transition: opacity 0.25s ease;
5161
+ }
5162
+ .tv-skin media-controller[userinactive]:not([mediapaused]) .tv-chrome {
5163
+ opacity: 0;
5164
+ pointer-events: none;
5165
+ }
5166
+ .tv-row {
5167
+ display: flex;
5168
+ align-items: center;
5169
+ gap: 2px;
5170
+ padding: 0 4px;
5171
+ }
5172
+ .tv-spacer {
5173
+ flex: 1 1 auto;
5174
+ }
5175
+ .tv-ctl,
5176
+ .tv-btn {
5177
+ display: inline-flex;
5178
+ align-items: center;
5179
+ justify-content: center;
5180
+ width: 40px;
5181
+ height: 40px;
5182
+ padding: 0;
5183
+ border: 0;
5184
+ border-radius: 0;
5185
+ background: transparent;
5186
+ box-shadow: none;
5187
+ color: rgba(255, 255, 255, 0.92);
5188
+ cursor: pointer;
5189
+ transition: color 0.15s;
5190
+ }
5191
+ .tv-ctl:hover,
5192
+ .tv-btn:hover {
5193
+ background: transparent;
5194
+ color: #ffffff;
5195
+ }
5196
+ .tv-ctl:focus-visible,
5197
+ .tv-btn:focus-visible,
5198
+ .tv-menu-item:focus-visible {
5199
+ outline: 2px solid #74a0ff;
5200
+ outline-offset: -4px;
5201
+ box-shadow: none;
5202
+ }
5203
+ .tv-time {
5204
+ width: auto;
5205
+ padding: 0 4px 0 6px;
5206
+ background: transparent;
5207
+ letter-spacing: 0.02em;
5208
+ white-space: nowrap;
5209
+ --media-control-background: transparent;
5210
+ }
5211
+ .tv-time-cur {
5212
+ display: none;
5213
+ }
5214
+ .tv-seek {
5215
+ display: block;
5216
+ width: 100%;
5217
+ height: 16px;
5218
+ margin: 0;
5219
+ padding: 0;
5220
+ background: transparent;
5221
+ box-shadow: none;
5222
+ border: 0;
5223
+ --media-range-track-height: 3px;
5224
+ --media-control-background: transparent;
5225
+ --media-control-hover-background: transparent;
5226
+ }
5227
+ .tv-seek:hover,
5228
+ .tv-seek:focus-visible {
5229
+ --media-range-track-height: 6px;
5230
+ --media-range-thumb-opacity: 1;
5231
+ }
5232
+ .tv-vol {
5233
+ display: flex;
5234
+ align-items: center;
5235
+ }
5236
+ .tv-volrange {
5237
+ width: 64px;
5238
+ height: 40px;
5239
+ margin-right: 4px;
5240
+ padding: 0;
5241
+ opacity: 0;
5242
+ pointer-events: none;
5243
+ transform: scaleX(0.75);
5244
+ transform-origin: right center;
5245
+ background: transparent;
5246
+ transition: transform 0.2s ease, opacity 0.2s ease;
5247
+ --media-control-background: transparent;
5248
+ --media-range-track-height: 3px;
5249
+ }
5250
+ .tv-vol:hover .tv-volrange,
5251
+ .tv-vol:focus-within .tv-volrange {
5252
+ opacity: 1;
5253
+ pointer-events: auto;
5254
+ transform: scaleX(1);
5255
+ }
5256
+ @media (hover: none) {
5257
+ .tv-volrange {
5258
+ display: none;
5259
+ }
5260
+ }
5261
+ @container (max-width: 479px) {
5262
+ .tv-vol,
5263
+ .tv-time-full {
5264
+ display: none;
5265
+ }
5266
+ .tv-time-cur {
5267
+ display: inline-flex;
5268
+ }
5269
+ }
5270
+ @container (max-width: 767px) {
5271
+ .tv-theater {
5272
+ display: none;
5273
+ }
5274
+ }
5275
+ .tv-menu-wrap {
5276
+ position: relative;
5277
+ display: flex;
5278
+ align-items: center;
5279
+ }
5280
+ .tv-menu {
5281
+ position: absolute;
5282
+ right: 0;
5283
+ bottom: calc(100% + 8px);
5284
+ z-index: 5;
5285
+ width: 136px;
5286
+ max-height: 232px;
5287
+ margin: 0;
5288
+ padding: 4px 0;
5289
+ overflow-y: auto;
5290
+ list-style: none;
5291
+ border: 1px solid rgba(255, 255, 255, 0.2);
5292
+ border-radius: 10px;
5293
+ background: linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12)), rgba(10, 14, 26, 0.55);
5294
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 12px 32px rgba(0, 0, 0, 0.35);
5295
+ }
5296
+ @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
5297
+ .tv-menu {
5298
+ -webkit-backdrop-filter: blur(16px) saturate(140%);
5299
+ backdrop-filter: blur(16px) saturate(140%);
5300
+ }
5301
+ }
5302
+ .tv-menu-item {
5303
+ position: relative;
5304
+ display: flex;
5305
+ width: 100%;
5306
+ height: 32px;
5307
+ align-items: center;
5308
+ padding: 0 12px 0 24px;
5309
+ border: 0;
5310
+ background: transparent;
5311
+ color: rgba(255, 255, 255, 0.75);
5312
+ font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
5313
+ font-size: 13px;
5314
+ font-weight: 500;
5315
+ text-align: left;
5316
+ cursor: pointer;
5317
+ }
5318
+ .tv-menu-item:hover,
5319
+ .tv-menu-item:focus-visible {
5320
+ background: rgba(255, 255, 255, 0.1);
5321
+ color: #ffffff;
5322
+ outline: none;
5323
+ }
5324
+ .tv-menu-item[aria-checked='true'] {
5325
+ color: #ffffff;
5326
+ font-weight: 600;
5327
+ }
5328
+ .tv-menu-item[aria-checked='true']::before {
5329
+ content: '';
5330
+ position: absolute;
5331
+ left: 12px;
5332
+ width: 5px;
5333
+ height: 5px;
5334
+ border-radius: 50%;
5335
+ background: #ffffff;
5336
+ }
5337
+ .tv-menu-sub {
5338
+ margin-left: 4px;
5339
+ font-size: 11.5px;
5340
+ font-weight: 400;
5341
+ color: rgba(255, 255, 255, 0.55);
5342
+ }
5343
+ .tv-menu-note {
5344
+ height: 20px;
5345
+ padding: 0 12px 0 24px;
5346
+ color: rgba(255, 255, 255, 0.55);
5347
+ font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
5348
+ font-size: 11.5px;
5349
+ line-height: 20px;
5350
+ }
5351
+ .tv-poster-overlay {
5352
+ position: absolute;
5353
+ inset: 0;
5354
+ }
5355
+ .tv-poster-img {
5356
+ width: 100%;
5357
+ height: 100%;
5358
+ object-fit: cover;
5359
+ display: block;
5360
+ }
5361
+ .tv-play-trigger {
5362
+ position: absolute;
5363
+ inset: 0;
5364
+ width: 100%;
5365
+ height: 100%;
5366
+ border: 0;
5367
+ padding: 0;
5368
+ background: transparent;
5369
+ cursor: pointer;
5370
+ display: flex;
5371
+ align-items: center;
5372
+ justify-content: center;
5373
+ transition: filter 0.2s ease;
5374
+ }
5375
+ .tv-play-trigger:hover {
5376
+ filter: brightness(1.1);
5377
+ }
5378
+ .tv-play-trigger:focus-visible {
5379
+ outline: 2px solid #74a0ff;
5380
+ outline-offset: -4px;
5381
+ }
5382
+ .tv-play-pill {
5383
+ display: flex;
5384
+ width: 64px;
5385
+ height: 64px;
5386
+ align-items: center;
5387
+ justify-content: center;
5388
+ border-radius: 9999px;
5389
+ border: 2px solid rgba(255, 255, 255, 0.72);
5390
+ background: rgba(255, 255, 255, 0.15);
5391
+ color: #ffffff;
5392
+ backdrop-filter: blur(2px);
5393
+ -webkit-backdrop-filter: blur(2px);
5394
+ transition: transform 0.3s ease;
5395
+ }
5396
+ .tv-play-trigger:hover .tv-play-pill {
5397
+ transform: scale(1.05);
5398
+ }
5399
+ `;
5400
+ function ensureTvSkinStyles() {
5401
+ if (typeof document === "undefined") return;
5402
+ if (document.querySelector(`style[${TV_SKIN_STYLE_ATTR}]`)) return;
5403
+ const style = document.createElement("style");
5404
+ style.setAttribute(TV_SKIN_STYLE_ATTR, "1");
5405
+ style.textContent = TV_SKIN_CSS;
5406
+ document.head.append(style);
5407
+ }
5408
+ function IconPlay({
5409
+ slot,
5410
+ size = 20,
5411
+ filled = false
5412
+ }) {
5413
+ return /* @__PURE__ */ jsx5(
5414
+ "svg",
5415
+ {
5416
+ slot,
5417
+ width: size,
5418
+ height: size,
5419
+ viewBox: "0 0 24 24",
5420
+ fill: filled ? "currentColor" : "none",
5421
+ stroke: "currentColor",
5422
+ strokeWidth: filled ? 0 : 1.75,
5423
+ strokeLinecap: "round",
5424
+ strokeLinejoin: "round",
5425
+ "aria-hidden": "true",
5426
+ style: filled ? { marginLeft: 3 } : void 0,
5427
+ children: /* @__PURE__ */ jsx5("polygon", { points: "6 3 20 12 6 21 6 3" })
5428
+ }
5429
+ );
5430
+ }
5431
+ function IconPause({ slot }) {
5432
+ return /* @__PURE__ */ jsxs2(
5433
+ "svg",
5434
+ {
5435
+ slot,
5436
+ width: 20,
5437
+ height: 20,
5438
+ viewBox: "0 0 24 24",
5439
+ fill: "none",
5440
+ stroke: "currentColor",
5441
+ strokeWidth: 1.75,
5442
+ strokeLinecap: "round",
5443
+ strokeLinejoin: "round",
5444
+ "aria-hidden": "true",
5445
+ children: [
5446
+ /* @__PURE__ */ jsx5("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1" }),
5447
+ /* @__PURE__ */ jsx5("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1" })
5448
+ ]
5449
+ }
5450
+ );
5451
+ }
5452
+ function IconVolumeHigh({ slot }) {
5453
+ return /* @__PURE__ */ jsxs2(
5454
+ "svg",
5455
+ {
5456
+ slot,
5457
+ width: 20,
5458
+ height: 20,
5459
+ viewBox: "0 0 24 24",
5460
+ fill: "none",
5461
+ stroke: "currentColor",
5462
+ strokeWidth: 1.75,
5463
+ strokeLinecap: "round",
5464
+ strokeLinejoin: "round",
5465
+ "aria-hidden": "true",
5466
+ children: [
5467
+ /* @__PURE__ */ jsx5("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
5468
+ /* @__PURE__ */ jsx5("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" }),
5469
+ /* @__PURE__ */ jsx5("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" })
5470
+ ]
5471
+ }
5472
+ );
5473
+ }
5474
+ function IconVolumeLow({ slot }) {
5475
+ return /* @__PURE__ */ jsxs2(
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__ */ jsx5("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
5490
+ /* @__PURE__ */ jsx5("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" })
5491
+ ]
5492
+ }
5493
+ );
5494
+ }
5495
+ function IconVolumeOff({ slot }) {
5496
+ return /* @__PURE__ */ jsxs2(
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__ */ jsx5("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
5511
+ /* @__PURE__ */ jsx5("line", { x1: "22", y1: "9", x2: "16", y2: "15" }),
5512
+ /* @__PURE__ */ jsx5("line", { x1: "16", y1: "9", x2: "22", y2: "15" })
5513
+ ]
5514
+ }
5515
+ );
5516
+ }
5517
+ function IconSettings() {
5518
+ return /* @__PURE__ */ jsxs2(
5519
+ "svg",
5520
+ {
5521
+ width: 20,
5522
+ height: 20,
5523
+ viewBox: "0 0 24 24",
5524
+ fill: "none",
5525
+ stroke: "currentColor",
5526
+ strokeWidth: 1.75,
5527
+ strokeLinecap: "round",
5528
+ strokeLinejoin: "round",
5529
+ "aria-hidden": "true",
5530
+ children: [
5531
+ /* @__PURE__ */ jsx5("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" }),
5532
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "3" })
5533
+ ]
5534
+ }
5535
+ );
5536
+ }
5537
+ function IconTheaterExpand() {
5538
+ return /* @__PURE__ */ jsxs2(
5539
+ "svg",
5540
+ {
5541
+ width: 20,
5542
+ height: 20,
5543
+ viewBox: "0 0 24 24",
5544
+ fill: "none",
5545
+ stroke: "currentColor",
5546
+ strokeWidth: 1.75,
5547
+ strokeLinecap: "round",
5548
+ strokeLinejoin: "round",
5549
+ "aria-hidden": "true",
5550
+ children: [
5551
+ /* @__PURE__ */ jsx5("polyline", { points: "15 3 21 3 21 9" }),
5552
+ /* @__PURE__ */ jsx5("polyline", { points: "9 21 3 21 3 15" }),
5553
+ /* @__PURE__ */ jsx5("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
5554
+ /* @__PURE__ */ jsx5("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
5555
+ ]
5556
+ }
5557
+ );
5558
+ }
5559
+ function IconTheaterShrink() {
5560
+ return /* @__PURE__ */ jsxs2(
5561
+ "svg",
5562
+ {
5563
+ width: 20,
5564
+ height: 20,
5565
+ viewBox: "0 0 24 24",
5566
+ fill: "none",
5567
+ stroke: "currentColor",
5568
+ strokeWidth: 1.75,
5569
+ strokeLinecap: "round",
5570
+ strokeLinejoin: "round",
5571
+ "aria-hidden": "true",
5572
+ children: [
5573
+ /* @__PURE__ */ jsx5("polyline", { points: "4 14 10 14 10 20" }),
5574
+ /* @__PURE__ */ jsx5("polyline", { points: "20 10 14 10 14 4" }),
5575
+ /* @__PURE__ */ jsx5("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
5576
+ /* @__PURE__ */ jsx5("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
5577
+ ]
5578
+ }
5579
+ );
5580
+ }
5581
+ function IconFullscreenEnter({ slot }) {
5582
+ return /* @__PURE__ */ jsxs2(
5583
+ "svg",
5584
+ {
5585
+ slot,
5586
+ width: 20,
5587
+ height: 20,
5588
+ viewBox: "0 0 24 24",
5589
+ fill: "none",
5590
+ stroke: "currentColor",
5591
+ strokeWidth: 1.75,
5592
+ strokeLinecap: "round",
5593
+ strokeLinejoin: "round",
5594
+ "aria-hidden": "true",
5595
+ children: [
5596
+ /* @__PURE__ */ jsx5("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
5597
+ /* @__PURE__ */ jsx5("path", { d: "M21 8V5a2 2 0 0 0-2-2h-3" }),
5598
+ /* @__PURE__ */ jsx5("path", { d: "M3 16v3a2 2 0 0 0 2 2h3" }),
5599
+ /* @__PURE__ */ jsx5("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
5600
+ ]
5601
+ }
5602
+ );
5603
+ }
5604
+ function IconFullscreenExit({ slot }) {
5605
+ return /* @__PURE__ */ jsxs2(
5606
+ "svg",
5607
+ {
5608
+ slot,
5609
+ width: 20,
5610
+ height: 20,
5611
+ viewBox: "0 0 24 24",
5612
+ fill: "none",
5613
+ stroke: "currentColor",
5614
+ strokeWidth: 1.75,
5615
+ strokeLinecap: "round",
5616
+ strokeLinejoin: "round",
5617
+ "aria-hidden": "true",
5618
+ children: [
5619
+ /* @__PURE__ */ jsx5("path", { d: "M8 3v3a2 2 0 0 1-2 2H3" }),
5620
+ /* @__PURE__ */ jsx5("path", { d: "M21 8h-3a2 2 0 0 1-2-2V3" }),
5621
+ /* @__PURE__ */ jsx5("path", { d: "M3 16h3a2 2 0 0 1 2 2v3" }),
5622
+ /* @__PURE__ */ jsx5("path", { d: "M16 21v-3a2 2 0 0 1 2-2h3" })
5623
+ ]
5624
+ }
5625
+ );
5626
+ }
5627
+ var EMPTY_GOALS2 = [];
5628
+ function serverVideoSnapshot() {
5629
+ return null;
5630
+ }
5631
+ function serverGoalsSnapshot() {
5632
+ return EMPTY_GOALS2;
5633
+ }
5634
+ function useTrackedVideoConfig(videoKey, trackingConfig, fallbackConfig) {
5635
+ registerCapability("video_tracking");
5636
+ const effectiveRef = trackingConfig ?? getActiveTrackingEnv().trackingConfig;
5637
+ const subscribe = useCallback4(
5638
+ (listener) => effectiveRef ? getTrackingConfigRuntime(effectiveRef).subscribe(listener) : () => {
5639
+ },
5640
+ [effectiveRef]
5641
+ );
5642
+ const live = useSyncExternalStore2(
5643
+ subscribe,
5644
+ () => effectiveRef && videoKey ? getTrackingConfigRuntime(effectiveRef).getVideo(videoKey) : null,
5645
+ serverVideoSnapshot
5646
+ );
5647
+ return live ?? fallbackConfig ?? null;
5648
+ }
5649
+ function useActiveVideoGoals(videoId, trackingConfig) {
5650
+ const effectiveRef = trackingConfig ?? getActiveTrackingEnv().trackingConfig;
5651
+ const subscribe = useCallback4(
5652
+ (listener) => effectiveRef ? getTrackingConfigRuntime(effectiveRef).subscribe(listener) : () => {
5653
+ },
5654
+ [effectiveRef]
5655
+ );
5656
+ const goals = useSyncExternalStore2(
5657
+ subscribe,
5658
+ () => effectiveRef ? getTrackingConfigRuntime(effectiveRef).listGoals() : EMPTY_GOALS2,
5659
+ serverGoalsSnapshot
5660
+ );
5661
+ return useMemo6(() => {
5662
+ const out = [];
5663
+ for (const goal of goals) {
5664
+ if (goal.kind !== "event" || !goal.trigger) continue;
5665
+ if (goal.trigger.event_type !== "video_progress") continue;
5666
+ if (goal.trigger.video_id && goal.trigger.video_id !== videoId) continue;
5667
+ const metric = goal.trigger.metric;
5668
+ const threshold = goal.trigger.threshold;
5669
+ if ((metric === "percent" || metric === "seconds") && typeof threshold === "number") {
5670
+ out.push({ key: goal.key, metric, threshold });
5671
+ }
5672
+ }
5673
+ return out;
5674
+ }, [goals, videoId]);
5675
+ }
5676
+ function renditionsOf(h) {
5677
+ const best = /* @__PURE__ */ new Map();
5678
+ h.levels.forEach((l, index) => {
5679
+ const px = Math.min(l.width || Infinity, l.height || Infinity);
5680
+ if (!Number.isFinite(px)) return;
5681
+ const cur = best.get(px);
5682
+ if (!cur || l.bitrate > cur.bitrate) best.set(px, { height: px, index, bitrate: l.bitrate });
5683
+ });
5684
+ return [...best.values()].sort((a, b) => b.height - a.height).map(({ height, index }) => ({ height, index }));
5685
+ }
5686
+ var MEDIA_CHROME_CDN_URL = "https://cdn.jsdelivr.net/npm/media-chrome@4/+esm";
5687
+ var HLS_JS_CDN_URL = "https://cdn.jsdelivr.net/npm/hls.js@1/dist/hls.min.js";
5688
+ var defaultLibsPromise = null;
5689
+ function loadScriptOnce(src, attr, isModule = false) {
5690
+ if (typeof document === "undefined") return Promise.resolve();
5691
+ const existing = document.querySelector(`script[${attr}]`);
5692
+ if (existing) return Promise.resolve();
5693
+ return new Promise((resolve) => {
5694
+ const s = document.createElement("script");
5695
+ if (isModule) s.type = "module";
5696
+ s.async = true;
5697
+ s.src = src;
5698
+ s.setAttribute(attr, "1");
5699
+ s.addEventListener("load", () => resolve(), { once: true });
5700
+ s.addEventListener("error", () => resolve(), { once: true });
5701
+ document.head.append(s);
5702
+ if (typeof navigator !== "undefined" && /jsdom/i.test(navigator.userAgent)) {
5703
+ setTimeout(resolve, 0);
5704
+ }
5705
+ });
5706
+ }
5707
+ function loadDefaultPlayerLibs() {
5708
+ if (typeof window === "undefined") return Promise.resolve({});
5709
+ if (defaultLibsPromise) return defaultLibsPromise;
5710
+ defaultLibsPromise = Promise.all([
5711
+ typeof customElements !== "undefined" && customElements.get("media-controller") ? Promise.resolve() : loadScriptOnce(MEDIA_CHROME_CDN_URL, "data-aranova-media-chrome", true),
5712
+ window.Hls ? Promise.resolve() : loadScriptOnce(HLS_JS_CDN_URL, "data-aranova-hls-js", false)
5713
+ ]).then(() => ({
5714
+ hls: window.Hls
5715
+ }));
5716
+ return defaultLibsPromise;
5717
+ }
5718
+ function Cut({
5719
+ id,
5720
+ variant,
5721
+ uid,
5722
+ host,
5723
+ title,
5724
+ posterTime,
5725
+ posterUrl,
5726
+ renderPoster,
5727
+ theater,
5728
+ onTheaterChange,
5729
+ autoExpandOnPlay,
5730
+ checkpoints,
5731
+ videoGoals,
5732
+ client,
5733
+ loadPlayerLibs,
5734
+ className,
5735
+ ...cb
5736
+ }) {
5737
+ const root = useRef2(null);
5738
+ const video = useRef2(null);
5739
+ const hls = useRef2(null);
5740
+ const lastTime = useRef2(0);
5741
+ const seekFrom = useRef2(0);
5742
+ const cbRef = useRef2(cb);
5743
+ cbRef.current = cb;
5744
+ const clientRef = useRef2(client);
5745
+ clientRef.current = client;
5746
+ const goalsRef = useRef2(videoGoals);
5747
+ goalsRef.current = videoGoals;
5748
+ const rangesRef = useRef2([]);
5749
+ const activeSegmentStartRef = useRef2(null);
5750
+ const furthestSecondsRef = useRef2(0);
5751
+ const maxPercentRef = useRef2(0);
5752
+ const durationRef = useRef2(0);
5753
+ const playCountRef = useRef2(0);
5754
+ const seekCountRef = useRef2(0);
5755
+ const firedCheckpointsRef = useRef2(/* @__PURE__ */ new Set());
5756
+ const firedGoalsRef = useRef2(/* @__PURE__ */ new Set());
5757
+ const dirtySinceLastEmitRef = useRef2(false);
5758
+ const [started, setStarted] = useState5(false);
5759
+ const [ready, setReady] = useState5(false);
5760
+ const [playing, setPlaying] = useState5(false);
5761
+ const [renditions, setRenditions] = useState5([]);
5762
+ const [selected, setSelected] = useState5("auto");
5763
+ const [live, setLive] = useState5(null);
5764
+ const [menu, setMenu] = useState5(false);
5765
+ const [pending, setPending] = useState5(null);
5766
+ const pendingRef = useRef2(null);
5767
+ const wrap = useRef2(null);
5768
+ const gear = useRef2(null);
5769
+ const selectedRef = useRef2("auto");
5770
+ const pendingTimer = useRef2(null);
5771
+ const liveRef = useRef2(null);
5772
+ const videoId = `${id}-${variant}`;
5773
+ const getCurrentRanges = useCallback4(() => {
5774
+ const v = video.current;
5775
+ const cur = v ? v.currentTime : lastTime.current;
5776
+ const list = [...rangesRef.current];
5777
+ if (activeSegmentStartRef.current !== null && cur > activeSegmentStartRef.current) {
5778
+ list.push({
5779
+ start_s: activeSegmentStartRef.current,
5780
+ end_s: cur
5781
+ });
5782
+ }
5783
+ return mergeVideoRanges(list);
5784
+ }, []);
5785
+ const commitActiveSegment = useCallback4((endTime) => {
5786
+ const end = endTime ?? video.current?.currentTime ?? lastTime.current;
5787
+ if (activeSegmentStartRef.current !== null && end > activeSegmentStartRef.current) {
5788
+ rangesRef.current = mergeVideoRanges([
5789
+ ...rangesRef.current,
5790
+ { start_s: activeSegmentStartRef.current, end_s: end }
5791
+ ]);
5792
+ }
5793
+ activeSegmentStartRef.current = null;
5794
+ }, []);
5795
+ const snap = useCallback4(() => {
5796
+ const v = video.current;
5797
+ const currentTime = roundVideoSeconds(v?.currentTime ?? lastTime.current);
5798
+ const rawDuration = v?.duration;
5799
+ const duration = typeof rawDuration === "number" && Number.isFinite(rawDuration) && rawDuration > 0 ? roundVideoSeconds(rawDuration) : durationRef.current;
5800
+ if (duration > 0) durationRef.current = duration;
5801
+ if (currentTime > furthestSecondsRef.current) {
5802
+ furthestSecondsRef.current = currentTime;
5803
+ }
5804
+ const ranges = getCurrentRanges();
5805
+ const watchedSeconds = sumVideoRangesSeconds(ranges, duration);
5806
+ const watchedPercent = duration > 0 ? Math.min(100, Math.round(watchedSeconds / duration * 100)) : 0;
5807
+ const curPercent = duration > 0 ? Math.min(100, Math.round(furthestSecondsRef.current / duration * 100)) : 0;
5808
+ if (curPercent > maxPercentRef.current) {
5809
+ maxPercentRef.current = curPercent;
5810
+ }
5811
+ return {
5812
+ id,
5813
+ videoId,
5814
+ variant,
5815
+ currentTime,
5816
+ duration,
5817
+ watchedSeconds,
5818
+ watchedPercent,
5819
+ maxPercent: maxPercentRef.current,
5820
+ ranges
5821
+ };
5822
+ }, [getCurrentRanges, id, variant, videoId]);
5823
+ const emitProgress = useCallback4(
5824
+ (trigger, snapshotOverride) => {
5825
+ const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;
5826
+ if (!activeClient || playCountRef.current <= 0) return;
5827
+ const s = snapshotOverride ?? snap();
5828
+ if (s.ranges.length === 0 && s.currentTime <= 0 && trigger !== "ended") return;
5829
+ dirtySinceLastEmitRef.current = false;
5830
+ const path = typeof window === "undefined" ? "/" : window.location.pathname;
5831
+ activeClient.trackEvent({
5832
+ eventType: "video_progress",
5833
+ metadata: {
5834
+ video_id: id,
5835
+ variant,
5836
+ trigger,
5837
+ current_s: s.currentTime,
5838
+ furthest_s: roundVideoSeconds(furthestSecondsRef.current),
5839
+ watched_s: s.watchedSeconds,
5840
+ duration_s: s.duration,
5841
+ max_percent: s.maxPercent,
5842
+ watched_percent: s.watchedPercent,
5843
+ play_count: playCountRef.current,
5844
+ seek_count: seekCountRef.current,
5845
+ ranges: s.ranges,
5846
+ page: { path }
5847
+ }
5848
+ });
5849
+ },
5850
+ [id, snap, variant]
5851
+ );
5852
+ const clearPending = () => {
5853
+ if (pendingTimer.current) clearTimeout(pendingTimer.current);
5854
+ pendingTimer.current = null;
5855
+ pendingRef.current = null;
5856
+ setPending(null);
5857
+ };
5858
+ useEffect6(() => {
5859
+ ensureTvSkinStyles();
5860
+ registerCapability("video_tracking");
5861
+ return () => {
5862
+ if (pendingTimer.current) clearTimeout(pendingTimer.current);
5863
+ hls.current?.destroy();
5864
+ hls.current = null;
5865
+ };
5866
+ }, []);
5867
+ useEffect6(() => {
5868
+ if (!started || typeof window === "undefined") return;
5869
+ const flushExitProgress = (useBeacon) => {
5870
+ if (playCountRef.current <= 0 || !dirtySinceLastEmitRef.current) return;
5871
+ commitActiveSegment();
5872
+ emitProgress("page_exit");
5873
+ if (useBeacon) {
5874
+ const activeClient2 = clientRef.current ?? getActiveTrackingEnv().rawClient;
5875
+ activeClient2?.flushBeacon();
5876
+ }
5877
+ };
5878
+ const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;
5879
+ const unsubscribeBeforeUnload = activeClient?.onBeforeUnload?.(() => {
5880
+ flushExitProgress(false);
5881
+ });
5882
+ const onVisibilityChange = () => {
5883
+ if (document.visibilityState === "hidden") {
5884
+ flushExitProgress(true);
5885
+ }
5886
+ };
5887
+ const onPageHide = () => {
5888
+ flushExitProgress(true);
5889
+ };
5890
+ document.addEventListener("visibilitychange", onVisibilityChange);
5891
+ window.addEventListener("pagehide", onPageHide);
5892
+ return () => {
5893
+ unsubscribeBeforeUnload?.();
5894
+ document.removeEventListener("visibilitychange", onVisibilityChange);
5895
+ window.removeEventListener("pagehide", onPageHide);
5896
+ };
5897
+ }, [started, commitActiveSegment, emitProgress]);
5898
+ useEffect6(() => {
5899
+ if (!started) return;
5900
+ let was = false;
5901
+ const report = (fullscreen) => {
5902
+ if (fullscreen === was) return;
5903
+ was = fullscreen;
5904
+ cbRef.current.onFullscreenChange?.({ ...snap(), fullscreen });
5905
+ };
5906
+ const onDoc = () => {
5907
+ const el = root.current;
5908
+ const fe = document.fullscreenElement ?? document.webkitFullscreenElement;
5909
+ report(Boolean(el && fe && el.contains(fe)));
5910
+ };
5911
+ const v = video.current;
5912
+ const begin = () => report(true);
5913
+ const end = () => report(false);
5914
+ document.addEventListener("fullscreenchange", onDoc);
5915
+ document.addEventListener("webkitfullscreenchange", onDoc);
5916
+ v?.addEventListener("webkitbeginfullscreen", begin);
5917
+ v?.addEventListener("webkitendfullscreen", end);
5918
+ return () => {
5919
+ document.removeEventListener("fullscreenchange", onDoc);
5920
+ document.removeEventListener("webkitfullscreenchange", onDoc);
5921
+ v?.removeEventListener("webkitbeginfullscreen", begin);
5922
+ v?.removeEventListener("webkitendfullscreen", end);
5923
+ };
5924
+ }, [started, snap]);
5925
+ useEffect6(() => {
5926
+ if (!menu) return;
5927
+ const outside = (e) => {
5928
+ if (wrap.current && !wrap.current.contains(e.target)) setMenu(false);
5929
+ };
5930
+ document.addEventListener("pointerdown", outside);
5931
+ return () => document.removeEventListener("pointerdown", outside);
5932
+ }, [menu]);
5933
+ const closeMenu = (refocus) => {
5934
+ setMenu(false);
5935
+ if (refocus) gear.current?.focus();
5936
+ };
5937
+ const onMenuKey = (e) => {
5938
+ if (e.key === "Escape") {
5939
+ e.preventDefault();
5940
+ e.stopPropagation();
5941
+ closeMenu(true);
5942
+ } else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
5943
+ e.preventDefault();
5944
+ const items = [
5945
+ ...wrap.current?.querySelectorAll('[role="menuitemradio"]') ?? []
5946
+ ];
5947
+ const i = items.indexOf(document.activeElement);
5948
+ items[(i + (e.key === "ArrowDown" ? 1 : items.length - 1)) % items.length]?.focus();
5949
+ }
5950
+ };
5951
+ const emitQuality = (from, to, source) => cbRef.current.onQualityChange?.({ ...snap(), from, to, source });
5952
+ const start = async () => {
5953
+ const v = video.current;
5954
+ if (!v || started) return;
5955
+ setStarted(true);
5956
+ if (variant === "16x9" && autoExpandOnPlay) {
5957
+ onTheaterChange?.(true);
5958
+ }
5959
+ const src = streamManifestUrl(host, uid);
5960
+ try {
5961
+ const loaded = await (loadPlayerLibs ? loadPlayerLibs() : loadDefaultPlayerLibs());
5962
+ setReady(true);
5963
+ const rawHls = (loaded && typeof loaded === "object" && "hls" in loaded ? loaded.hls : null) ?? (typeof window !== "undefined" ? window.Hls : void 0);
5964
+ const HlsLib = rawHls && typeof rawHls === "object" && "default" in rawHls ? rawHls.default : rawHls;
5965
+ if (!HlsLib || !HlsLib.isSupported()) {
5966
+ v.src = src;
5967
+ } else {
5968
+ const h = new HlsLib({ capLevelToPlayerSize: true });
5969
+ hls.current = h;
5970
+ h.on(HlsLib.Events.MANIFEST_PARSED, () => setRenditions(renditionsOf(h)));
5971
+ h.on(HlsLib.Events.LEVEL_SWITCHED, (_e, data) => {
5972
+ const lv = h.levels[data.level];
5973
+ const height = lv ? Math.min(lv.width || Infinity, lv.height || Infinity) : null;
5974
+ const from = liveRef.current;
5975
+ liveRef.current = height;
5976
+ setLive(height);
5977
+ const want = pendingRef.current;
5978
+ if (want !== null && want === height) clearPending();
5979
+ if (selectedRef.current === "auto" && height && from && from !== height) {
5980
+ emitQuality(from, height, "auto");
5981
+ }
5982
+ });
5983
+ h.on(HlsLib.Events.ERROR, () => clearPending());
5984
+ h.loadSource(src);
5985
+ h.attachMedia(v);
5986
+ }
5987
+ } catch {
5988
+ setReady(true);
5989
+ v.src = src;
5990
+ }
5991
+ try {
5992
+ await v.play();
5993
+ } catch {
5994
+ }
5995
+ };
5996
+ const pick = (q) => {
5997
+ const h = hls.current;
5998
+ const from = selectedRef.current;
5999
+ setMenu(false);
6000
+ if (!h || q === from) return;
6001
+ if (q === "auto") h.nextLevel = -1;
6002
+ else {
6003
+ const r = renditions.find((x) => x.height === q);
6004
+ if (r) h.nextLevel = r.index;
6005
+ }
6006
+ selectedRef.current = q;
6007
+ setSelected(q);
6008
+ clearPending();
6009
+ if (typeof q === "number" && q !== liveRef.current) {
6010
+ pendingRef.current = q;
6011
+ setPending(q);
6012
+ pendingTimer.current = setTimeout(clearPending, 4e3);
6013
+ }
6014
+ emitQuality(from, q, "manual");
6015
+ };
6016
+ const qualityLabel = selected === "auto" ? live ? `Auto (${live}p)` : "Auto" : `${selected}p`;
6017
+ const resolvedPosterUrl = posterUrl ?? streamPosterUrl(host, uid, posterTime);
6018
+ const posterWidth = variant === "16x9" ? 1280 : 720;
6019
+ const posterHeight = variant === "16x9" ? 720 : 1280;
6020
+ return /* @__PURE__ */ jsxs2(
6021
+ "div",
6022
+ {
6023
+ ref: root,
6024
+ className: `tv-skin ${variant === "16x9" ? "tv-skin-desktop" : "tv-skin-mobile"} ${className ?? ""}`,
6025
+ "data-video-id": videoId,
6026
+ children: [
6027
+ /* @__PURE__ */ jsxs2("media-controller", { class: "absolute inset-0 block", autohide: menu ? "-1" : "2", children: [
6028
+ /* @__PURE__ */ jsx5(
6029
+ "video",
6030
+ {
6031
+ ref: video,
6032
+ slot: "media",
6033
+ style: { width: "100%", height: "100%", backgroundColor: "#000", objectFit: "contain" },
6034
+ preload: "none",
6035
+ playsInline: true,
6036
+ title,
6037
+ "aria-label": title,
6038
+ "data-video-id": videoId,
6039
+ onPlay: () => {
6040
+ const v = video.current;
6041
+ const cur = roundVideoSeconds(v?.currentTime ?? 0);
6042
+ activeSegmentStartRef.current = cur;
6043
+ playCountRef.current += 1;
6044
+ dirtySinceLastEmitRef.current = true;
6045
+ const s = snap();
6046
+ const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;
6047
+ if (activeClient) {
6048
+ const path = typeof window === "undefined" ? "/" : window.location.pathname;
6049
+ activeClient.trackEvent({
6050
+ eventType: "video_play",
6051
+ metadata: {
6052
+ video_id: id,
6053
+ variant,
6054
+ current_s: s.currentTime,
6055
+ duration_s: s.duration,
6056
+ play_sequence: playCountRef.current,
6057
+ page: { path }
6058
+ }
6059
+ });
6060
+ }
6061
+ cbRef.current.onPlay?.(s);
6062
+ },
6063
+ onPlaying: () => setPlaying(true),
6064
+ onPause: () => {
6065
+ const v = video.current;
6066
+ if (v?.ended) return;
6067
+ commitActiveSegment(v?.currentTime);
6068
+ const s = snap();
6069
+ emitProgress("pause", s);
6070
+ cbRef.current.onPause?.(s);
6071
+ },
6072
+ onEnded: () => {
6073
+ const v = video.current;
6074
+ const dur = typeof v?.duration === "number" && Number.isFinite(v.duration) ? v.duration : durationRef.current;
6075
+ commitActiveSegment(dur > 0 ? dur : v?.currentTime);
6076
+ maxPercentRef.current = 100;
6077
+ const s = snap();
6078
+ if (!firedCheckpointsRef.current.has(100) && checkpoints.includes(100)) {
6079
+ firedCheckpointsRef.current.add(100);
6080
+ cbRef.current.onCheckpoint?.({ ...s, checkpoint: 100 });
6081
+ }
6082
+ emitProgress("ended", s);
6083
+ cbRef.current.onEnded?.(s);
6084
+ },
6085
+ onWaiting: () => cbRef.current.onWaiting?.(snap()),
6086
+ onTimeUpdate: () => {
6087
+ const v = video.current;
6088
+ if (v && !v.seeking) {
6089
+ if (activeSegmentStartRef.current === null && !v.paused) {
6090
+ activeSegmentStartRef.current = lastTime.current;
6091
+ }
6092
+ lastTime.current = v.currentTime;
6093
+ dirtySinceLastEmitRef.current = true;
6094
+ }
6095
+ const s = snap();
6096
+ let hitCheckpoint = false;
6097
+ let hitGoal = false;
6098
+ for (const goal of goalsRef.current) {
6099
+ if (firedGoalsRef.current.has(goal.key)) continue;
6100
+ const met = goal.metric === "seconds" ? s.watchedSeconds >= goal.threshold : s.watchedPercent >= goal.threshold;
6101
+ if (met) {
6102
+ firedGoalsRef.current.add(goal.key);
6103
+ hitGoal = true;
6104
+ }
6105
+ }
6106
+ const newlyFiredCheckpoints = [];
6107
+ for (const cp of checkpoints) {
6108
+ if (firedCheckpointsRef.current.has(cp)) continue;
6109
+ if (s.watchedPercent >= cp || s.maxPercent >= cp) {
6110
+ firedCheckpointsRef.current.add(cp);
6111
+ newlyFiredCheckpoints.push(cp);
6112
+ hitCheckpoint = true;
6113
+ }
6114
+ }
6115
+ if (hitCheckpoint || hitGoal) {
6116
+ commitActiveSegment(v?.currentTime);
6117
+ if (v && !v.paused) activeSegmentStartRef.current = v.currentTime;
6118
+ const emittedSnap = snap();
6119
+ emitProgress(hitCheckpoint ? "checkpoint" : "goal", emittedSnap);
6120
+ for (const cp of newlyFiredCheckpoints) {
6121
+ cbRef.current.onCheckpoint?.({ ...emittedSnap, checkpoint: cp });
6122
+ }
6123
+ }
6124
+ cbRef.current.onTimeUpdate?.(s);
6125
+ },
6126
+ onSeeking: () => {
6127
+ seekFrom.current = lastTime.current;
6128
+ commitActiveSegment(seekFrom.current);
6129
+ },
6130
+ onSeeked: () => {
6131
+ const v = video.current;
6132
+ const to = roundVideoSeconds(v?.currentTime ?? 0);
6133
+ const from = roundVideoSeconds(seekFrom.current);
6134
+ lastTime.current = to;
6135
+ if (v && !v.paused) {
6136
+ activeSegmentStartRef.current = to;
6137
+ }
6138
+ if (Math.abs(to - from) >= 1) {
6139
+ seekCountRef.current += 1;
6140
+ const s = snap();
6141
+ emitProgress("seek", s);
6142
+ cbRef.current.onSeek?.({ ...s, from, to });
6143
+ }
6144
+ },
6145
+ children: /* @__PURE__ */ jsx5("track", { kind: "captions" })
6146
+ }
6147
+ ),
6148
+ ready && /* @__PURE__ */ jsxs2(Fragment2, { children: [
6149
+ /* @__PURE__ */ jsx5("media-loading-indicator", { slot: "centered-chrome" }),
6150
+ /* @__PURE__ */ jsxs2("div", { className: "tv-chrome", children: [
6151
+ /* @__PURE__ */ jsxs2("div", { className: "tv-row", children: [
6152
+ /* @__PURE__ */ jsxs2("media-play-button", { class: "tv-ctl", children: [
6153
+ /* @__PURE__ */ jsx5(IconPlay, { slot: "play" }),
6154
+ /* @__PURE__ */ jsx5(IconPause, { slot: "pause" })
6155
+ ] }),
6156
+ /* @__PURE__ */ jsx5("media-time-display", { class: "tv-time tv-time-full", showduration: true }),
6157
+ /* @__PURE__ */ jsx5("media-time-display", { class: "tv-time tv-time-cur" }),
6158
+ /* @__PURE__ */ jsx5("span", { className: "tv-spacer" }),
6159
+ /* @__PURE__ */ jsxs2("div", { className: "tv-vol", children: [
6160
+ /* @__PURE__ */ jsxs2("media-mute-button", { class: "tv-ctl", children: [
6161
+ /* @__PURE__ */ jsx5(IconVolumeHigh, { slot: "high" }),
6162
+ /* @__PURE__ */ jsx5(IconVolumeLow, { slot: "medium" }),
6163
+ /* @__PURE__ */ jsx5(IconVolumeLow, { slot: "low" }),
6164
+ /* @__PURE__ */ jsx5(IconVolumeOff, { slot: "off" })
6165
+ ] }),
6166
+ /* @__PURE__ */ jsx5("media-volume-range", { class: "tv-volrange" })
6167
+ ] }),
6168
+ renditions.length > 1 && /* @__PURE__ */ jsxs2("div", { className: "tv-menu-wrap", ref: wrap, children: [
6169
+ /* @__PURE__ */ jsx5(
6170
+ "button",
6171
+ {
6172
+ ref: gear,
6173
+ type: "button",
6174
+ className: "tv-btn",
6175
+ "aria-haspopup": "menu",
6176
+ "aria-expanded": menu,
6177
+ "aria-label": `Settings, quality ${qualityLabel}`,
6178
+ title: "Settings",
6179
+ onClick: () => setMenu((m) => !m),
6180
+ onKeyDown: onMenuKey,
6181
+ children: /* @__PURE__ */ jsx5(IconSettings, {})
6182
+ }
6183
+ ),
6184
+ /* @__PURE__ */ jsx5("span", { role: "status", className: "sr-only", children: pending !== null ? `Switching to ${pending}p` : "" }),
6185
+ menu && /* @__PURE__ */ jsxs2(
6186
+ "ul",
6187
+ {
6188
+ role: "menu",
6189
+ "aria-label": "Quality",
6190
+ className: "tv-menu",
6191
+ onKeyDown: onMenuKey,
6192
+ children: [
6193
+ ["auto", ...renditions.map((r) => r.height)].map(
6194
+ (q) => /* @__PURE__ */ jsx5("li", { role: "none", children: /* @__PURE__ */ jsx5(
6195
+ "button",
6196
+ {
6197
+ type: "button",
6198
+ role: "menuitemradio",
6199
+ "aria-checked": selected === q,
6200
+ className: "tv-menu-item",
6201
+ onClick: () => pick(q),
6202
+ children: q === "auto" ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
6203
+ "Auto",
6204
+ live ? /* @__PURE__ */ jsxs2("span", { className: "tv-menu-sub", children: [
6205
+ " ",
6206
+ live,
6207
+ "p"
6208
+ ] }) : null
6209
+ ] }) : `${q}p`
6210
+ }
6211
+ ) }, String(q))
6212
+ ),
6213
+ /* @__PURE__ */ jsx5("li", { "aria-hidden": "true", className: "tv-menu-note", children: pending !== null ? `Switching to ${pending}p\u2026` : "" })
6214
+ ]
6215
+ }
6216
+ )
6217
+ ] }),
6218
+ variant === "16x9" && onTheaterChange && /* @__PURE__ */ jsx5(
6219
+ "button",
6220
+ {
6221
+ type: "button",
6222
+ className: "tv-btn tv-theater",
6223
+ "aria-pressed": theater,
6224
+ "aria-label": theater ? "Shrink the player" : "Expand the player",
6225
+ title: theater ? "Shrink" : "Expand",
6226
+ onClick: () => onTheaterChange(!theater),
6227
+ children: theater ? /* @__PURE__ */ jsx5(IconTheaterShrink, {}) : /* @__PURE__ */ jsx5(IconTheaterExpand, {})
6228
+ }
6229
+ ),
6230
+ /* @__PURE__ */ jsxs2("media-fullscreen-button", { class: "tv-ctl", children: [
6231
+ /* @__PURE__ */ jsx5(IconFullscreenEnter, { slot: "enter" }),
6232
+ /* @__PURE__ */ jsx5(IconFullscreenExit, { slot: "exit" })
6233
+ ] })
6234
+ ] }),
6235
+ /* @__PURE__ */ jsx5("media-time-range", { class: "tv-seek" })
6236
+ ] })
6237
+ ] })
6238
+ ] }),
6239
+ !playing && /* @__PURE__ */ jsxs2("div", { className: "tv-poster-overlay", children: [
6240
+ renderPoster ? renderPoster({
6241
+ src: resolvedPosterUrl,
6242
+ variant,
6243
+ title,
6244
+ width: posterWidth,
6245
+ height: posterHeight
6246
+ }) : /* @__PURE__ */ jsx5(
6247
+ "img",
6248
+ {
6249
+ src: resolvedPosterUrl,
6250
+ alt: "",
6251
+ width: posterWidth,
6252
+ height: posterHeight,
6253
+ loading: "lazy",
6254
+ decoding: "async",
6255
+ className: "tv-poster-img"
6256
+ }
6257
+ ),
6258
+ !started && /* @__PURE__ */ jsx5(
6259
+ "button",
6260
+ {
6261
+ type: "button",
6262
+ onClick: start,
6263
+ "aria-label": `Play video: ${title}`,
6264
+ className: "tv-play-trigger",
6265
+ children: /* @__PURE__ */ jsx5("span", { className: "tv-play-pill", children: /* @__PURE__ */ jsx5(IconPlay, { size: 26, filled: true }) })
6266
+ }
6267
+ )
6268
+ ] })
6269
+ ]
6270
+ }
6271
+ );
6272
+ }
6273
+ function TrackedVideo(props) {
6274
+ const {
6275
+ id,
6276
+ video,
6277
+ host,
6278
+ sources,
6279
+ title,
6280
+ posterTime,
6281
+ posterUrl,
6282
+ renderPoster,
6283
+ theater: controlledTheater,
6284
+ defaultTheater = false,
6285
+ onTheaterChange,
6286
+ expandMode = "inline",
6287
+ autoExpandOnPlay = true,
6288
+ expandedMaxWidth = 1100,
6289
+ checkpoints = DEFAULT_VIDEO_CHECKPOINTS,
6290
+ trackingConfig,
6291
+ fallbackConfig,
6292
+ client = null,
6293
+ loadPlayerLibs,
6294
+ className = "",
6295
+ style,
6296
+ ...callbacks
6297
+ } = props;
6298
+ const videoKey = id ?? video ?? fallbackConfig?.key ?? "";
6299
+ const r2Video = useTrackedVideoConfig(videoKey || void 0, trackingConfig, fallbackConfig);
6300
+ const videoGoals = useActiveVideoGoals(videoKey, trackingConfig);
6301
+ const [internalTheater, setInternalTheater] = useState5(defaultTheater);
6302
+ const isTheaterControlled = controlledTheater !== void 0;
6303
+ const activeTheater = isTheaterControlled ? controlledTheater : internalTheater;
6304
+ const handleTheaterChange = useCallback4(
6305
+ (next) => {
6306
+ if (!isTheaterControlled) {
6307
+ setInternalTheater(next);
6308
+ }
6309
+ onTheaterChange?.(next);
6310
+ },
6311
+ [isTheaterControlled, onTheaterChange]
6312
+ );
6313
+ const resolvedHost = host ?? r2Video?.host ?? "";
6314
+ const desktopUid = sources?.desktop ?? r2Video?.sources.desktop ?? "";
6315
+ const mobileUid = sources?.mobile ?? r2Video?.sources.mobile ?? desktopUid;
6316
+ const resolvedTitle = title ?? r2Video?.title ?? videoKey;
6317
+ const resolvedPosterTime = posterTime ?? r2Video?.poster_time ?? r2Video?.poster_time_s ?? 4;
6318
+ if (!videoKey || !resolvedHost || !desktopUid) {
6319
+ return null;
6320
+ }
6321
+ const showTheaterControl = expandMode !== "none" && (expandMode === "theater" || Boolean(onTheaterChange));
6322
+ const wrapperStyle = !isTheaterControlled && expandMode === "theater" && activeTheater ? {
6323
+ maxWidth: typeof expandedMaxWidth === "number" ? `${expandedMaxWidth}px` : expandedMaxWidth,
6324
+ marginLeft: "auto",
6325
+ marginRight: "auto",
6326
+ width: "100%",
6327
+ transition: "max-width 360ms cubic-bezier(0.2, 0.7, 0.2, 1)",
6328
+ ...style
6329
+ } : style;
6330
+ return /* @__PURE__ */ jsxs2("div", { className, style: wrapperStyle, "data-tracked-video": videoKey, children: [
6331
+ /* @__PURE__ */ jsx5(
6332
+ Cut,
6333
+ {
6334
+ ...callbacks,
6335
+ id: videoKey,
6336
+ variant: "16x9",
6337
+ uid: desktopUid,
6338
+ host: resolvedHost,
6339
+ title: resolvedTitle,
6340
+ posterTime: resolvedPosterTime,
6341
+ posterUrl,
6342
+ renderPoster,
6343
+ theater: activeTheater,
6344
+ onTheaterChange: showTheaterControl ? handleTheaterChange : void 0,
6345
+ autoExpandOnPlay,
6346
+ checkpoints,
6347
+ videoGoals,
6348
+ client,
6349
+ loadPlayerLibs
6350
+ }
6351
+ ),
6352
+ /* @__PURE__ */ jsx5(
6353
+ Cut,
6354
+ {
6355
+ ...callbacks,
6356
+ id: videoKey,
6357
+ variant: "9x16",
6358
+ uid: mobileUid || desktopUid,
6359
+ host: resolvedHost,
6360
+ title: resolvedTitle,
6361
+ posterTime: resolvedPosterTime,
6362
+ posterUrl,
6363
+ renderPoster,
6364
+ theater: false,
6365
+ onTheaterChange: void 0,
6366
+ autoExpandOnPlay: false,
6367
+ checkpoints,
6368
+ videoGoals,
6369
+ client,
6370
+ loadPlayerLibs
6371
+ }
6372
+ )
6373
+ ] });
6374
+ }
6375
+ var STAGE_DURATION_MS = 360;
6376
+ var STAGE_EASE = "cubic-bezier(0.2, 0.7, 0.2, 1)";
6377
+ function TrackedVideoStage({
6378
+ video,
6379
+ children,
6380
+ expandedMaxWidth = 1100,
6381
+ collapsedTextMaxWidth = 760,
6382
+ className = ""
6383
+ }) {
6384
+ const [theater, setTheater] = useState5(video.defaultTheater ?? false);
6385
+ const videoBox = useRef2(null);
6386
+ const textBox = useRef2(null);
6387
+ const first = useRef2(null);
6388
+ const activeTheater = video.theater !== void 0 ? video.theater : theater;
6389
+ const change = useCallback4(
6390
+ (next) => {
6391
+ if (next === activeTheater) return;
6392
+ if (next && typeof window !== "undefined" && !window.matchMedia("(min-width: 768px)").matches) {
6393
+ return;
6394
+ }
6395
+ if (videoBox.current && textBox.current) {
6396
+ first.current = {
6397
+ v: videoBox.current.getBoundingClientRect(),
6398
+ t: textBox.current.getBoundingClientRect()
6399
+ };
6400
+ }
6401
+ if (video.theater === void 0) {
6402
+ setTheater(next);
6403
+ }
6404
+ video.onTheaterChange?.(next);
6405
+ },
6406
+ [activeTheater, video]
6407
+ );
6408
+ useLayoutEffect(() => {
6409
+ if (typeof window === "undefined") return;
6410
+ const f = first.current;
6411
+ const vb = videoBox.current;
6412
+ const tb = textBox.current;
6413
+ first.current = null;
6414
+ if (!f || !vb || !tb) return;
6415
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
6416
+ const lv = vb.getBoundingClientRect();
6417
+ const lt = tb.getBoundingClientRect();
6418
+ if (!reduce && typeof vb.animate === "function" && lv.width > 0) {
6419
+ const s = f.v.width / lv.width;
6420
+ vb.animate(
6421
+ [
6422
+ {
6423
+ transformOrigin: "top left",
6424
+ transform: `translate(${f.v.left - lv.left}px, ${f.v.top - lv.top}px) scale(${s})`
6425
+ },
6426
+ { transformOrigin: "top left", transform: "none" }
6427
+ ],
6428
+ { duration: STAGE_DURATION_MS, easing: STAGE_EASE }
6429
+ );
6430
+ tb.animate(
6431
+ [
6432
+ {
6433
+ transform: `translate(${f.t.left - lt.left}px, ${f.t.top - lt.top}px)`,
6434
+ opacity: 0.4
6435
+ },
6436
+ { transform: "none", opacity: 1 }
6437
+ ],
6438
+ { duration: STAGE_DURATION_MS, easing: STAGE_EASE }
6439
+ );
6440
+ }
6441
+ if (lv.top < 80 || lv.bottom > window.innerHeight - 24) {
6442
+ const top = window.scrollY + lv.top - Math.max(96, (window.innerHeight - lv.height) / 2);
6443
+ window.scrollTo({ top, behavior: reduce ? "auto" : "smooth" });
6444
+ }
6445
+ }, [activeTheater]);
6446
+ const maxVideoWidthCss = typeof expandedMaxWidth === "number" ? `${expandedMaxWidth}px` : expandedMaxWidth;
6447
+ const maxTextWidthCss = typeof collapsedTextMaxWidth === "number" ? `${collapsedTextMaxWidth}px` : collapsedTextMaxWidth;
6448
+ return /* @__PURE__ */ jsxs2(
6449
+ "div",
6450
+ {
6451
+ className: activeTheater ? `grid gap-10 md:gap-12 ${className}` : `grid items-start gap-10 lg:grid-cols-[1fr_1fr] lg:gap-16 ${className}`,
6452
+ children: [
6453
+ /* @__PURE__ */ jsx5(
6454
+ "div",
6455
+ {
6456
+ ref: textBox,
6457
+ className: activeTheater ? "md:order-2" : "",
6458
+ style: activeTheater ? { maxWidth: maxTextWidthCss } : void 0,
6459
+ children
6460
+ }
6461
+ ),
6462
+ /* @__PURE__ */ jsx5(
6463
+ "div",
6464
+ {
6465
+ ref: videoBox,
6466
+ className: activeTheater ? "md:order-1 md:mx-auto md:w-full" : "",
6467
+ style: activeTheater ? { maxWidth: maxVideoWidthCss, width: "100%" } : void 0,
6468
+ children: /* @__PURE__ */ jsx5(
6469
+ TrackedVideo,
6470
+ {
6471
+ ...video,
6472
+ theater: activeTheater,
6473
+ onTheaterChange: change,
6474
+ className: video.className ?? "w-full"
6475
+ }
6476
+ )
6477
+ }
6478
+ )
6479
+ ]
6480
+ }
6481
+ );
6482
+ }
4837
6483
  export {
4838
6484
  AdPlatformTracking,
4839
6485
  BookingHandoffLink,
4840
6486
  ConsentBanner,
6487
+ DEFAULT_VIDEO_CHECKPOINTS,
4841
6488
  GoogleAdsTracking,
4842
6489
  PhoneField,
6490
+ TrackedVideo,
6491
+ TrackedVideoStage,
4843
6492
  createLeadFormSubmit,
4844
6493
  createTracking,
6494
+ streamManifestUrl,
6495
+ streamPosterUrl,
4845
6496
  useBookingHandoffUrl,
4846
6497
  useBookingIntegration,
4847
6498
  useBookingSlots,
@@ -4852,6 +6503,7 @@ export {
4852
6503
  useGclid,
4853
6504
  usePhoneConfig,
4854
6505
  usePhoneField,
6506
+ useTrackedVideoConfig,
4855
6507
  useTrackingParams
4856
6508
  };
4857
6509
  //# sourceMappingURL=client.mjs.map