(function () {
  "use strict";

  function isDebugEnabled() {
    return !!(window.modeloraViewerData && window.modeloraViewerData.debug);
  }

  function createTraceId(prefix) {
    return [
      "modelora",
      prefix,
      Date.now().toString(36),
      Math.random().toString(36).slice(2, 8),
    ].join("-");
  }

  function logInfo(stage, payload) {
    if (!isDebugEnabled()) {
      return;
    }

    console.info("[MODELORA][VIEWER][" + stage + "]", payload || {});
  }

  function logWarn(stage, payload) {
    if (!isDebugEnabled()) {
      return;
    }

    console.warn("[MODELORA][VIEWER][" + stage + "]", payload || {});
  }

  function logError(stage, payload) {
    if (!isDebugEnabled()) {
      return;
    }

    console.error("[MODELORA][VIEWER][" + stage + "]", payload || {});
  }

  function hasModelViewerDefined() {
    return !!(window.customElements && window.customElements.get("model-viewer"));
  }

  function waitForModelViewerDefined(timeoutMs) {
    if (hasModelViewerDefined()) {
      return Promise.resolve(true);
    }

    if (!window.customElements || typeof window.customElements.whenDefined !== "function") {
      return Promise.resolve(false);
    }

    return new Promise(function (resolve) {
      var done = false;
      var timer = window.setTimeout(function () {
        if (!done) {
          done = true;
          resolve(false);
        }
      }, timeoutMs);

      window.customElements
        .whenDefined("model-viewer")
        .then(function () {
          if (!done) {
            done = true;
            window.clearTimeout(timer);
            resolve(true);
          }
        })
        .catch(function () {
          if (!done) {
            done = true;
            window.clearTimeout(timer);
            resolve(false);
          }
        });
    });
  }

  function injectModelViewerScript(url, scriptId) {
    return new Promise(function (resolve) {
      if (!url) {
        resolve(false);
        return;
      }

      var existing = document.getElementById(scriptId);
      if (existing) {
        resolve(true);
        return;
      }

      var script = document.createElement("script");
      script.id = scriptId;
      script.type = "module";
      script.src = url;
      script.onload = function () {
        resolve(true);
      };
      script.onerror = function () {
        resolve(false);
      };
      document.head.appendChild(script);
    });
  }

  function ensureModelViewerDefined(traceId) {
    if (hasModelViewerDefined()) {
      logInfo("model_viewer_defined", {
        traceId: traceId,
        issueCode: "OK",
      });
      return Promise.resolve(true);
    }

    logWarn("model_viewer_not_defined_initial", {
      traceId: traceId,
      issueCode: "MODEL_VIEWER_UNDEFINED",
    });

    // Wait for the model-viewer custom element to be defined
    // It should be loaded by the enqueued script, we just need to wait for it
    return waitForModelViewerDefined(10000)
      .then(function (defined) {
        if (defined) {
          logInfo("model_viewer_defined_after_wait", {
            traceId: traceId,
            issueCode: "OK",
          });
          return true;
        }

        logError("model_viewer_timeout", {
          traceId: traceId,
          issueCode: "MODEL_VIEWER_DEFINE_TIMEOUT",
        });
        return false;
      });
  }

  function probeModelSource(src, traceId, modelId) {
    if (!src || !window.fetch) {
      return;
    }

    fetch(src, { method: "HEAD" })
      .then(function (res) {
        logInfo("model_source_probe", {
          traceId: traceId,
          modelId: Number(modelId),
          src: src,
          httpStatus: res.status,
          ok: !!res.ok,
          contentType: res.headers.get("content-type") || "",
          issueCode: res.ok ? "OK" : "MODEL_SOURCE_HTTP_ERROR",
        });
      })
      .catch(function (err) {
        logError("model_source_probe_failed", {
          traceId: traceId,
          modelId: Number(modelId),
          src: src,
          message: err && err.message ? err.message : "Model source probe failed.",
          issueCode: "MODEL_SOURCE_PROBE_ERROR",
        });
      });

    fetch(src, {
      method: "GET",
      headers: {
        Range: "bytes=0-1",
      },
      cache: "no-store",
    })
      .then(function (res) {
        logInfo("model_source_range_probe", {
          traceId: traceId,
          modelId: Number(modelId),
          src: src,
          httpStatus: res.status,
          ok: !!res.ok,
          acceptRanges: res.headers.get("accept-ranges") || "",
          contentRange: res.headers.get("content-range") || "",
          issueCode: res.ok ? "OK" : "MODEL_SOURCE_RANGE_ERROR",
        });
      })
      .catch(function (err) {
        logError("model_source_range_probe_failed", {
          traceId: traceId,
          modelId: Number(modelId),
          src: src,
          message: err && err.message ? err.message : "Range probe failed.",
          issueCode: "MODEL_SOURCE_RANGE_PROBE_ERROR",
        });
      });
  }

  function forceSafeFraming(viewer, traceId, modelId, reason) {
    try {
      viewer.setAttribute("camera-target", "auto auto auto");
      viewer.setAttribute("camera-orbit", "0deg 75deg auto");
      viewer.setAttribute("field-of-view", "auto");
      if (typeof viewer.jumpCameraToGoal === "function") {
        viewer.jumpCameraToGoal();
      }
      if (typeof viewer.updateFraming === "function") {
        viewer.updateFraming();
      }

      logWarn("viewer_safe_framing_applied", {
        traceId: traceId,
        modelId: Number(modelId),
        reason: reason,
        issueCode: "VIEWER_SAFE_FRAMING",
      });
    } catch (err) {
      logError("viewer_safe_framing_failed", {
        traceId: traceId,
        modelId: Number(modelId),
        reason: reason,
        message: err && err.message ? err.message : "Safe framing failed.",
      });
    }
  }

  function reloadModelSource(viewer, traceId, modelId, src, reason) {
    if (!src) {
      return;
    }

    try {
      viewer.setAttribute("reveal", "auto");
      viewer.setAttribute("loading", "eager");
      viewer.setAttribute("src", "");

      window.requestAnimationFrame(function () {
        viewer.setAttribute("src", src);
        if (typeof viewer.load === "function") {
          viewer.load();
        }
      });

      logWarn("viewer_reload_attempted", {
        traceId: traceId,
        modelId: Number(modelId),
        src: src,
        reason: reason,
        issueCode: "VIEWER_RELOAD",
      });
    } catch (err) {
      logError("viewer_reload_failed", {
        traceId: traceId,
        modelId: Number(modelId),
        src: src,
        reason: reason,
        message: err && err.message ? err.message : "Reload failed.",
      });
    }
  }

  function applyPreset(el, preset) {
    if (preset === "dark") {
      el.setAttribute("environment-image", "legacy");
      el.style.setProperty("--modelora-bg", "#111827");
      return;
    }

    if (preset === "outdoor") {
      el.setAttribute("environment-image", "neutral");
      el.style.setProperty("--modelora-bg", "#f6fbff");
      return;
    }

    el.setAttribute("environment-image", "neutral");
    el.style.setProperty("--modelora-bg", "#f8fafc");
  }

  function clampNumber(value, fallback) {
    var n = Number(value);
    return Number.isFinite(n) ? n : fallback;
  }

  function lerp(a, b, t) {
    return a + (b - a) * t;
  }

  function lerpAngle(a, b, t) {
    var delta = b - a;
    while (delta > Math.PI) {
      delta -= Math.PI * 2;
    }
    while (delta < -Math.PI) {
      delta += Math.PI * 2;
    }
    return a + delta * t;
  }

  function easeInOutCubic(t) {
    if (t < 0.5) {
      return 4 * t * t * t;
    }
    return 1 - Math.pow(-2 * t + 2, 3) / 2;
  }

  function applyCustomBackground(viewer, shell) {
    if (!viewer || !shell) {
      return;
    }

    if (shell.classList.contains("modelora-no-bg")) {
      viewer.style.background = "transparent";
      viewer.style.backgroundImage = "none";
      return;
    }

    var bgColor = shell.getAttribute("data-bg-color") || "";
    var bgImage = shell.getAttribute("data-bg-image") || "";

    if (bgImage) {
      viewer.style.backgroundColor = bgColor || "#f8fafc";
      viewer.style.backgroundImage = 'url("' + bgImage.replace(/"/g, "%22") + '")';
      viewer.style.backgroundSize = "cover";
      viewer.style.backgroundPosition = "center";
      viewer.style.backgroundRepeat = "no-repeat";
      return;
    }

    if (bgColor) {
      viewer.style.background = bgColor;
      viewer.style.backgroundImage = "none";
      return;
    }

    viewer.style.background = "radial-gradient(circle at 22% 18%, #ffffff, #eaf2fb 65%)";
    viewer.style.backgroundImage = "none";
  }

  function setupViewer(shell) {
    var viewer = shell.querySelector("model-viewer");
    if (!viewer) {
      return;
    }

    var modelId = viewer.getAttribute("data-model-id") || "0";
    var traceId = viewer.getAttribute("data-trace-id") || createTraceId("viewer");
    var renderSource = viewer.getAttribute("data-render-source") || "shortcode";
    var src = viewer.getAttribute("src") || "";
    var start = Date.now();
    var loadedOrErrored = false;
    var loadedEventSeen = false;
    var rotateCount = 0;
    var zoomCount = 0;
    var preset = viewer.getAttribute("data-preset") || "studio";
    var defaultViewState = null;
    var resetAnimationFrame = 0;

    function captureDefaultViewState() {
      if (typeof viewer.getCameraOrbit !== "function" || typeof viewer.getCameraTarget !== "function") {
        return;
      }

      try {
        var orbit = viewer.getCameraOrbit();
        var target = viewer.getCameraTarget();
        defaultViewState = {
          theta: clampNumber(orbit.theta, 0),
          phi: clampNumber(orbit.phi, 1.3089969389957472),
          radius: clampNumber(orbit.radius, 2),
          x: clampNumber(target.x, 0),
          y: clampNumber(target.y, 0),
          z: clampNumber(target.z, 0),
        };
      } catch (err) {
        defaultViewState = null;
      }
    }

    function animateResetToDefault() {
      if (!defaultViewState || typeof viewer.getCameraOrbit !== "function" || typeof viewer.getCameraTarget !== "function") {
        viewer.setAttribute("camera-target", "auto auto auto");
        viewer.setAttribute("camera-orbit", "0deg 75deg auto");
        viewer.setAttribute("field-of-view", "auto");
        return;
      }

      var startOrbit;
      var startTarget;
      try {
        startOrbit = viewer.getCameraOrbit();
        startTarget = viewer.getCameraTarget();
      } catch (err) {
        viewer.setAttribute("camera-target", "auto auto auto");
        viewer.setAttribute("camera-orbit", "0deg 75deg auto");
        viewer.setAttribute("field-of-view", "auto");
        return;
      }

      var start = {
        theta: clampNumber(startOrbit.theta, defaultViewState.theta),
        phi: clampNumber(startOrbit.phi, defaultViewState.phi),
        radius: clampNumber(startOrbit.radius, defaultViewState.radius),
        x: clampNumber(startTarget.x, defaultViewState.x),
        y: clampNumber(startTarget.y, defaultViewState.y),
        z: clampNumber(startTarget.z, defaultViewState.z),
      };

      var duration = 760;
      var startTs = null;

      if (resetAnimationFrame) {
        cancelAnimationFrame(resetAnimationFrame);
        resetAnimationFrame = 0;
      }

      function step(ts) {
        if (startTs === null) {
          startTs = ts;
        }

        var progress = Math.min(1, (ts - startTs) / duration);
        var eased = easeInOutCubic(progress);

        var theta = lerpAngle(start.theta, defaultViewState.theta, eased);
        var phi = lerp(start.phi, defaultViewState.phi, eased);
        var radius = lerp(start.radius, defaultViewState.radius, eased);

        var x = lerp(start.x, defaultViewState.x, eased);
        var y = lerp(start.y, defaultViewState.y, eased);
        var z = lerp(start.z, defaultViewState.z, eased);

        viewer.setAttribute("camera-orbit", theta + "rad " + phi + "rad " + radius + "m");
        viewer.setAttribute("camera-target", x + "m " + y + "m " + z + "m");
        viewer.setAttribute("field-of-view", "auto");

        if (progress < 1) {
          resetAnimationFrame = requestAnimationFrame(step);
          return;
        }

        resetAnimationFrame = 0;
        viewer.setAttribute("camera-orbit", defaultViewState.theta + "rad " + defaultViewState.phi + "rad " + defaultViewState.radius + "m");
        viewer.setAttribute("camera-target", defaultViewState.x + "m " + defaultViewState.y + "m " + defaultViewState.z + "m");
      }

      resetAnimationFrame = requestAnimationFrame(step);
    }

    logInfo("viewer_setup_start", {
      traceId: traceId,
      modelId: Number(modelId),
      renderSource: renderSource,
      src: src,
      preset: preset,
      arEnabled: viewer.getAttribute("data-ar-enabled") === "1",
      pluginVersion: (window.modeloraViewerData && window.modeloraViewerData.version) || "unknown",
    });

    viewer.addEventListener("load", function () {
      loadedOrErrored = true;
      loadedEventSeen = true;
      shell.classList.add("is-ready");
      logInfo("viewer_loaded", {
        traceId: traceId,
        modelId: Number(modelId),
        issueCode: "OK",
      });

      forceSafeFraming(viewer, traceId, modelId, "after_load");
      window.setTimeout(captureDefaultViewState, 120);
    });

    viewer.addEventListener("progress", function (event) {
      var detail = event && event.detail ? event.detail : {};
      var totalProgress = typeof detail.totalProgress === "number" ? detail.totalProgress : null;
      logInfo("viewer_progress", {
        traceId: traceId,
        modelId: Number(modelId),
        progress: totalProgress,
      });
    });

    viewer.addEventListener("model-visibility", function () {
      logInfo("viewer_model_visibility", {
        traceId: traceId,
        modelId: Number(modelId),
        visible: !!viewer.modelIsVisible,
      });
    });

    viewer.addEventListener("error", function (event) {
      loadedOrErrored = true;
      shell.classList.add("has-error");
      shell.insertAdjacentHTML(
        "beforeend",
        '<p class="modelora-friendly-error">Your model could not be loaded. Try optimizing it and re-uploading as GLB.</p>'
      );
      logError("viewer_error", {
        traceId: traceId,
        modelId: Number(modelId),
        src: viewer.getAttribute("src") || "",
        detail: event && event.detail ? event.detail : null,
        issueCode: "VIEWER_LOAD_ERROR",
      });
    });

    ensureModelViewerDefined(traceId).then(function (defined) {
      if (!defined) {
        shell.classList.add("has-error");
        shell.insertAdjacentHTML(
          "beforeend",
          '<p class="modelora-friendly-error">3D runtime could not load from CDN. Check network/cors and try again.</p>'
        );
        return;
      }

      viewer.setAttribute("reveal", "auto");
      viewer.setAttribute("loading", "eager");
      viewer.setAttribute("src", src);
      if (typeof viewer.load === "function") {
        viewer.load();
      }

      applyPreset(viewer, preset);
      applyCustomBackground(viewer, shell);
      logInfo("viewer_preset_applied", {
        traceId: traceId,
        modelId: Number(modelId),
        preset: preset,
      });

      window.setTimeout(function () {
        logInfo("viewer_model_state", {
          traceId: traceId,
          modelId: Number(modelId),
          modelIsVisible: !!viewer.modelIsVisible,
          hasModel: !!viewer.model,
        });
      }, 1500);
    });

    window.setTimeout(function () {
      if (loadedOrErrored) {
        return;
      }

      var src = viewer.getAttribute("src") || "";
      logWarn("viewer_load_timeout", {
        traceId: traceId,
        modelId: Number(modelId),
        src: src,
        loadedEventSeen: loadedEventSeen,
        modelIsVisible: !!viewer.modelIsVisible,
        issueCode: "VIEWER_LOAD_TIMEOUT",
      });
      probeModelSource(src, traceId, modelId);
      forceSafeFraming(viewer, traceId, modelId, "timeout");
      reloadModelSource(viewer, traceId, modelId, src, "timeout");
    }, 10000);

    viewer.addEventListener("camera-change", function () {
      rotateCount += 1;
      if (rotateCount === 4) {
        logInfo("viewer_rotate_threshold_reached", {
          traceId: traceId,
          modelId: Number(modelId),
          rotations: rotateCount,
        });
      }
    });

    viewer.addEventListener("wheel", function () {
      zoomCount += 1;
      if (zoomCount === 2) {
        logInfo("viewer_zoom_threshold_reached", {
          traceId: traceId,
          modelId: Number(modelId),
          zooms: zoomCount,
        });
      }
    });

    var buttons = shell.querySelectorAll(".modelora-btn");
    buttons.forEach(function (btn) {
      btn.addEventListener("click", function () {
        var action = btn.getAttribute("data-modelora-action");

        if (action === "reset-view") {
          animateResetToDefault();

          logInfo("toolbar_reset_view", {
            traceId: traceId,
            modelId: Number(modelId),
            animated: true,
            mode: defaultViewState ? "smooth_return" : "fallback_default",
          });
        }

        if (action === "ar" && typeof viewer.activateAR === "function") {
          logInfo("toolbar_ar_requested", {
            traceId: traceId,
            modelId: Number(modelId),
          });
          viewer.activateAR();
        } else if (action === "ar") {
          logWarn("toolbar_ar_unavailable", {
            traceId: traceId,
            modelId: Number(modelId),
            issueCode: "AR_NOT_SUPPORTED",
          });
        }
      });
    });

    window.addEventListener("beforeunload", function () {
      logInfo("viewer_session_end", {
        traceId: traceId,
        modelId: Number(modelId),
        seconds: Number(seconds.toFixed(2)),
      });
    });
  }

  function requestFullscreen(target) {
    if (!target) {
      return Promise.resolve(false);
    }

    if (target.requestFullscreen) {
      return target.requestFullscreen().then(function () {
        return true;
      }).catch(function () {
        return false;
      });
    }

    if (target.webkitRequestFullscreen) {
      target.webkitRequestFullscreen();
      return Promise.resolve(true);
    }

    if (target.msRequestFullscreen) {
      target.msRequestFullscreen();
      return Promise.resolve(true);
    }

    return Promise.resolve(false);
  }

  function exitFullscreen() {
    if (document.exitFullscreen) {
      return document.exitFullscreen().then(function () {
        return true;
      }).catch(function () {
        return false;
      });
    }

    if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
      return Promise.resolve(true);
    }

    if (document.msExitFullscreen) {
      document.msExitFullscreen();
      return Promise.resolve(true);
    }

    return Promise.resolve(false);
  }

  function isElementFullscreen(target) {
    var current = document.fullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
    return !!(current && target && (current === target || current.contains(target)));
  }

  function applyWireframe(viewer, enabled) {
    if (!viewer || !viewer.model || !Array.isArray(viewer.model.materials)) {
      return;
    }

    viewer.model.materials.forEach(function (mat) {
      if (mat && typeof mat === "object") {
        mat.wireframe = !!enabled;
      }
    });
  }

  function setPanelLoading(panel, message) {
    if (!panel) {
      return;
    }
    var loading = panel.querySelector("[data-modelora-loading]");
    if (!loading) {
      return;
    }

    loading.style.display = "flex";
    if (message) {
      loading.textContent = message;
    }
  }

  function clearPanelLoading(panel) {
    if (!panel) {
      return;
    }
    var loading = panel.querySelector("[data-modelora-loading]");
    if (!loading) {
      return;
    }
    loading.style.display = "none";
  }

  function validateModelBeforeRender(src, traceId, side) {
    if (!src || !window.fetch) {
      return Promise.resolve(true);
    }

    var ext = src.split("?")[0].split(".").pop().toLowerCase();
    if (ext !== "glb" && ext !== "gltf") {
      return Promise.resolve(true);
    }

    if (ext === "glb") {
      return fetch(src, { method: "GET", headers: { Range: "bytes=0-3" }, cache: "no-store" })
        .then(function (res) {
          if (!res.ok) {
            throw new Error("GLB preflight failed with HTTP " + res.status);
          }
          return res.arrayBuffer();
        })
        .then(function (buff) {
          var sig = new Uint8Array(buff.slice(0, 4));
          var magic = String.fromCharCode(sig[0], sig[1], sig[2], sig[3]);
          if (magic !== "glTF") {
            throw new Error("GLB failed preflight signature check.");
          }
          return true;
        })
        .catch(function (err) {
          logWarn("compare_validation_warn", {
            traceId: traceId,
            side: side,
            src: src,
            message: err && err.message ? err.message : "Validation skipped.",
            issueCode: "COMPARE_VALIDATION_SKIPPED",
          });
          return true;
        });
    }

    return fetch(src, { method: "HEAD", cache: "no-store" })
      .then(function (res) {
        if (!res.ok) {
          throw new Error("GLTF preflight failed with HTTP " + res.status);
        }
        return true;
      })
      .catch(function (err) {
        logWarn("compare_validation_warn", {
          traceId: traceId,
          side: side,
          src: src,
          message: err && err.message ? err.message : "Validation skipped.",
          issueCode: "COMPARE_VALIDATION_SKIPPED",
        });
        return true;
      });
  }

  function setupCompare(shell) {
    var traceId = shell.getAttribute("data-trace-id") || createTraceId("compare");
    var syncEnabled = shell.getAttribute("data-sync-default") === "1";
    var wireframeEnabled = shell.getAttribute("data-wireframe-default") === "1";
    var syncing = false;

    var leftPanel = shell.querySelector('.modelora-compare-panel[data-side="left"]');
    var rightPanel = shell.querySelector('.modelora-compare-panel[data-side="right"]');
    var leftViewer = leftPanel ? leftPanel.querySelector("model-viewer") : null;
    var rightViewer = rightPanel ? rightPanel.querySelector("model-viewer") : null;
    var syncButton = shell.querySelector('[data-modelora-action="compare-sync-toggle"]');
    var wireframeButton = shell.querySelector('[data-modelora-action="compare-wireframe-toggle"]');
    var presetSelect = shell.querySelector('[data-modelora-action="compare-preset"]');
    var bgInput = shell.querySelector('[data-modelora-action="compare-bg"]');
    var fsButtons = shell.querySelectorAll('[data-modelora-action="compare-fullscreen"]');

    function getViewerBySide(side) {
      if (side === "left") {
        return leftViewer;
      }
      if (side === "right") {
        return rightViewer;
      }
      return null;
    }

    function getStageBySide(side) {
      var panel = side === "left" ? leftPanel : rightPanel;
      return panel ? panel.querySelector(".modelora-compare-stage") : null;
    }

    function updateSyncLabel() {
      if (!syncButton) {
        return;
      }
      syncButton.textContent = syncEnabled ? "Sync On" : "Independent";
    }

    function syncCamera(source, target) {
      if (!source || !target) {
        return;
      }

      syncing = true;
      target.setAttribute("camera-orbit", source.getAttribute("camera-orbit") || "0deg 75deg auto");
      target.setAttribute("camera-target", source.getAttribute("camera-target") || "auto auto auto");
      target.setAttribute("field-of-view", source.getAttribute("field-of-view") || "auto");
      if (typeof target.jumpCameraToGoal === "function") {
        target.jumpCameraToGoal();
      }
      window.requestAnimationFrame(function () {
        syncing = false;
      });
    }

    function setPanelError(panel, message) {
      if (!panel) {
        return;
      }

      clearPanelLoading(panel);
      panel.classList.add("has-error");
      if (!panel.querySelector(".modelora-friendly-error")) {
        panel.insertAdjacentHTML("beforeend", '<p class="modelora-friendly-error">' + message + "</p>");
      }
    }

    function bindViewer(panel, viewer) {
      if (!panel || !viewer) {
        return;
      }

      var side = viewer.getAttribute("data-side") || "unknown";
      var modelId = viewer.getAttribute("data-model-id") || "0";
      var src = viewer.getAttribute("src") || "";

      setPanelLoading(panel, "Loading model...");

      viewer.addEventListener("load", function () {
        panel.classList.remove("has-error");
        clearPanelLoading(panel);
        applyPreset(viewer, presetSelect ? presetSelect.value : viewer.getAttribute("data-preset") || "studio");
        applyWireframe(viewer, wireframeEnabled);
        forceSafeFraming(viewer, traceId, modelId, "compare_load");
      });

      viewer.addEventListener("error", function () {
        setPanelError(panel, "Model failed to load. You can still compare with the other panel.");
        logError("compare_viewer_error", {
          traceId: traceId,
          side: side,
          modelId: Number(modelId),
          src: src,
          issueCode: "COMPARE_MODEL_LOAD_ERROR",
        });
      });

      viewer.addEventListener("progress", function (event) {
        var detail = event && event.detail ? event.detail : {};
        var totalProgress = typeof detail.totalProgress === "number" ? detail.totalProgress : 0;
        var pct = Math.max(0, Math.min(100, Math.round(totalProgress * 100)));
        setPanelLoading(panel, "Loading model... " + pct + "%");
      });

      viewer.addEventListener("camera-change", function () {
        if (!syncEnabled || syncing) {
          return;
        }

        if (viewer === leftViewer) {
          syncCamera(leftViewer, rightViewer);
        } else {
          syncCamera(rightViewer, leftViewer);
        }
      });

      validateModelBeforeRender(src, traceId, side).then(function () {
        if (typeof viewer.load === "function") {
          viewer.load();
        }
      });
    }

    updateSyncLabel();

    ensureModelViewerDefined(traceId).then(function (defined) {
      if (!defined) {
        setPanelError(leftPanel, "3D runtime failed to initialize.");
        setPanelError(rightPanel, "3D runtime failed to initialize.");
        return;
      }

      bindViewer(leftPanel, leftViewer);
      bindViewer(rightPanel, rightViewer);
    });

    if (syncButton) {
      syncButton.addEventListener("click", function () {
        syncEnabled = !syncEnabled;
        updateSyncLabel();
      });
    }

    if (wireframeButton) {
      wireframeButton.addEventListener("click", function () {
        wireframeEnabled = !wireframeEnabled;
        applyWireframe(leftViewer, wireframeEnabled);
        applyWireframe(rightViewer, wireframeEnabled);
        shell.setAttribute("data-wireframe-default", wireframeEnabled ? "1" : "0");
      });
    }

    if (presetSelect) {
      presetSelect.addEventListener("change", function () {
        var preset = presetSelect.value || "studio";
        if (leftViewer) {
          applyPreset(leftViewer, preset);
        }
        if (rightViewer) {
          applyPreset(rightViewer, preset);
        }
      });
    }

    if (bgInput) {
      bgInput.addEventListener("input", function () {
        var stages = shell.querySelectorAll(".modelora-compare-stage");
        stages.forEach(function (stage) {
          stage.style.setProperty("--modelora-compare-bg", bgInput.value || "#f8fafc");
        });
      });
    }

    fsButtons.forEach(function (btn) {
      btn.addEventListener("click", function () {
        var side = btn.getAttribute("data-side") || "left";
        var stage = getStageBySide(side);
        if (!stage) {
          return;
        }

        if (isElementFullscreen(stage)) {
          exitFullscreen();
        } else {
          requestFullscreen(stage);
        }
      });
    });
  }

  function initAll() {
    var shells = document.querySelectorAll(".modelora-viewer-shell");
    var compareShells = document.querySelectorAll(".modelora-compare-shell");
    logInfo("viewer_markup_discovered", {
      shellCount: shells.length,
      compareCount: compareShells.length,
      issueCode: shells.length || compareShells.length ? "OK" : "NO_VIEWER_SHELL",
    });
    shells.forEach(setupViewer);
    compareShells.forEach(setupCompare);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", initAll);
  } else {
    initAll();
  }
})();
