(function (win, doc) {
  'use strict';

  var DEFAULT_DEBOUNCE_MS = 80;
  var DEFAULT_MIN_HEIGHT = 220;
  var DEFAULT_MIN_WIDTH = 360;
  var DEFAULT_VIEWPORT_WIDTH = 480;
  var DEFAULT_RESIZE_REFRESH_MS = 120;
  var DEFAULT_TIMEOUT_MS = 8000;
  var DEFAULT_WIDTH_JITTER_PX = 8;
  var HEIGHT_BUFFER_PX = 18;
  var HEIGHT_SYNC_DELAYS = [80, 220, 420, 620, 860, 1180, 1540];

  function isObject(value) {
    return !!value && typeof value === 'object' && !Array.isArray(value);
  }

  function sanitizeCalcType(value) {
    if (typeof value !== 'string') {
      return 'loan';
    }
    var normalized = value.trim().toLowerCase().replace(/[^a-z0-9\-]/g, '');
    return normalized || 'loan';
  }

  function normalizeConfig(rawConfig) {
    var source = isObject(rawConfig) ? rawConfig : {};
    var config = {};

    config.type = sanitizeCalcType(source.type);

    if (typeof source.theme === 'string' && source.theme.trim() !== '') {
      config.theme = source.theme.trim();
    }

    if (typeof source.branding !== 'undefined') {
      config.branding = !!source.branding;
    }

    ['colors', 'design', 'formatting', 'measurement', 'assets', 'accessibility', 'energyDefaults'].forEach(function (key) {
      if (isObject(source[key])) {
        config[key] = Object.assign({}, source[key]);
      }
    });

    return config;
  }

  function normalizePluginUrl(rawUrl) {
    if (typeof rawUrl !== 'string' || !rawUrl) {
      return '';
    }
    try {
      var parsed = new URL(rawUrl, win.location.origin).toString();
      if (parsed.charAt(parsed.length - 1) !== '/') {
        parsed += '/';
      }
      return parsed;
    } catch {
      return '';
    }
  }

  function appendLink(shadowRoot, href, key) {
    if (!shadowRoot || !href) {
      return;
    }

    var id = 'vareoncalc-shadow-style-' + key;
    if (shadowRoot.getElementById && shadowRoot.getElementById(id)) {
      return;
    }

    var link = doc.createElement('link');
    link.id = id;
    link.rel = 'stylesheet';
    link.href = href;
    shadowRoot.appendChild(link);
  }

  function clearAnimationFrameCompat(id) {
    if (!id) {
      return;
    }
    if (typeof win.cancelAnimationFrame === 'function') {
      win.cancelAnimationFrame(id);
      return;
    }
    clearTimeout(id);
  }

  function getNodeHeight(node) {
    if (!node) {
      return 0;
    }

    var rectHeight = 0;
    if (typeof node.getBoundingClientRect === 'function') {
      rectHeight = node.getBoundingClientRect().height;
    }

    return Math.max(
      node.scrollHeight || 0,
      node.offsetHeight || 0,
      node.clientHeight || 0,
      rectHeight || 0
    );
  }

  function shouldSkipForMeasurement(node, styles) {
    var computed = styles;
    if (!node || node.nodeType !== 1) {
      return true;
    }
    if (node.hidden || node.getAttribute('hidden') !== null || node.getAttribute('aria-hidden') === 'true') {
      return true;
    }
    if (!computed && typeof win.getComputedStyle === 'function') {
      computed = win.getComputedStyle(node);
    }
    if (!computed) {
      return false;
    }
    if (computed.display === 'none' || computed.visibility === 'hidden' || computed.visibility === 'collapse') {
      return true;
    }
    if (
      typeof node.closest === 'function' &&
      node.closest('.vareoncalc-two-step--blind[data-step="results"] .vareoncalc-two-step__step--inputs')
    ) {
      return true;
    }
    if (parseFloat(computed.opacity || '1') <= 0) {
      return true;
    }
    return computed.position === 'fixed';
  }

  function getVisibleDescendantBottom(node, rootTop) {
    if (!node || typeof node.querySelectorAll !== 'function') {
      return 0;
    }

    var maxBottom = 0;
    var descendants = node.querySelectorAll('*');
    Array.prototype.forEach.call(descendants, function (element) {
      if (!element || element.nodeType !== 1) {
        return;
      }

      var styles = typeof win.getComputedStyle === 'function' ? win.getComputedStyle(element) : null;
      if (shouldSkipForMeasurement(element, styles)) {
        return;
      }
      if (typeof element.getBoundingClientRect !== 'function') {
        return;
      }

      var rect = element.getBoundingClientRect();
      if ((!rect.width && !rect.height) || !Number.isFinite(rect.top) || !Number.isFinite(rect.bottom)) {
        return;
      }
      var marginBottom = styles ? parseFloat(styles.marginBottom || '0') : 0;
      if (!Number.isFinite(marginBottom) || marginBottom < 0) {
        marginBottom = 0;
      }

      var effectiveBottom = rect.bottom + marginBottom;
      if (effectiveBottom <= rootTop) {
        return;
      }

      maxBottom = Math.max(maxBottom, Math.ceil(effectiveBottom - rootTop));
    });

    return maxBottom;
  }

  function measureRenderedContentHeight(host, node) {
    if (!node) {
      return 0;
    }

    var previousHostHeight = host ? host.style.height : '';
    var previousHostMinHeight = host ? host.style.minHeight : '';
    var previousNodeHeight = node.style.height;
    var previousNodeMinHeight = node.style.minHeight;
    var previousTwoStepLayouts = [];

    if (host) {
      host.style.height = 'auto';
      host.style.minHeight = '0px';
    }
    node.style.height = 'auto';
    node.style.minHeight = '0px';
    if (typeof node.querySelectorAll === 'function') {
      Array.prototype.forEach.call(node.querySelectorAll('.vareoncalc-two-step'), function (element) {
        previousTwoStepLayouts.push({
          element: element,
          height: element.style.height,
          minHeight: element.style.minHeight
        });
        element.style.height = 'auto';
        element.style.minHeight = '0px';
      });
    }

    var measuredHeight = 0;
    if (typeof node.getBoundingClientRect === 'function') {
      var rootRect = node.getBoundingClientRect();
      var rootTop = Number.isFinite(rootRect.top) ? rootRect.top : 0;
      measuredHeight = getVisibleDescendantBottom(node, rootTop);
    }
    if (measuredHeight <= 0) {
      measuredHeight = getNodeHeight(node.firstElementChild || node);
    }

    previousTwoStepLayouts.forEach(function (entry) {
      entry.element.style.height = entry.height;
      entry.element.style.minHeight = entry.minHeight;
    });
    node.style.height = previousNodeHeight;
    node.style.minHeight = previousNodeMinHeight;
    if (host) {
      host.style.height = previousHostHeight;
      host.style.minHeight = previousHostMinHeight;
    }

    return Math.max(0, Math.ceil(measuredHeight + HEIGHT_BUFFER_PX));
  }

  function ShadowPreview(options) {
    this.options = options || {};
    this.container = this.options.container || null;
    this.host = null;
    this.shadowRoot = null;
    this.canvasShell = null;
    this.scaleWrap = null;
    this.mountNode = null;
    this.updateTimer = null;
    this.latestMessage = null;
    this.destroyed = false;
    this.loadedCalcStyles = {};
    this.loaderWaitTimer = null;
    this.loaderWaitInFlight = false;
    this.loaderWaitStartedAt = 0;
    this.loaderWaitCallbacks = [];
    this.loaderTimeoutReported = false;
    this.resizeObserver = null;
    this.resizeRefreshTimer = null;
    this.lastObservedWidth = 0;
    this.contentResizeObserver = null;
    this.contentMutationObserver = null;
    this.heightSyncFrame = 0;
    this.heightSyncTimers = [];
    this.lastAppliedHeight = 0;
    this.lastAppliedScale = 1;
    this.lastMeasuredContentHeight = 0;
    this.isCompactPreview = !!this.options.compactPreview;
    this.hasNoWrapFixes = false;
    this.isScaledPreview = false;
  }

  ShadowPreview.prototype.getPluginUrl = function () {
    var provided = normalizePluginUrl(this.options.pluginUrl || '');
    if (provided) {
      return provided;
    }

    var cfgPluginUrl = normalizePluginUrl((win.VAREONCALC_CFG && win.VAREONCALC_CFG.pluginUrl) || '');
    if (cfgPluginUrl) {
      return cfgPluginUrl;
    }

    return normalizePluginUrl((win.VAREONCALC_EDITOR && win.VAREONCALC_EDITOR.pluginUrl) || '');
  };

  ShadowPreview.prototype.getAssetVersion = function () {
    if (typeof this.options.assetVersion === 'string' || typeof this.options.assetVersion === 'number') {
      return String(this.options.assetVersion);
    }

    if (win.VAREONCALC_CFG) {
      return String(win.VAREONCALC_CFG.assetVersion || win.VAREONCALC_CFG.version || '');
    }

    if (win.VAREONCALC_EDITOR) {
      return String(win.VAREONCALC_EDITOR.assetVersion || '');
    }

    return '';
  };

  ShadowPreview.prototype.shouldUseMinifiedAssets = function () {
    if (typeof this.options.useMinifiedAssets === 'boolean') {
      return this.options.useMinifiedAssets;
    }

    if (win.VAREONCALC_CFG && typeof win.VAREONCALC_CFG.useMinifiedAssets === 'boolean') {
      return win.VAREONCALC_CFG.useMinifiedAssets;
    }

    if (win.VAREONCALC_EDITOR && typeof win.VAREONCALC_EDITOR.useMinifiedAssets === 'boolean') {
      return win.VAREONCALC_EDITOR.useMinifiedAssets;
    }

    return false;
  };

  ShadowPreview.prototype.resolveAssetRelativePath = function (relativePath) {
    var normalizedPath = String(relativePath || '').replace(/^\/+/, '');

    if (
      this.shouldUseMinifiedAssets()
      && /\.(css|js)$/i.test(normalizedPath)
      && !/\.min\.(css|js)$/i.test(normalizedPath)
    ) {
      return normalizedPath.replace(/\.(css|js)$/i, '.min.$1');
    }

    return normalizedPath;
  };

  ShadowPreview.prototype.buildAssetHref = function (relativePath) {
    var pluginUrl = this.getPluginUrl();
    if (!pluginUrl) {
      return '';
    }

    var href = pluginUrl + this.resolveAssetRelativePath(relativePath);
    var version = this.getAssetVersion();
    if (version) {
      href += '?v=' + encodeURIComponent(version);
    }
    return href;
  };

  ShadowPreview.prototype.reportError = function (message) {
    if (typeof this.options.onError === 'function') {
      this.options.onError(message || 'Preview failed');
    }
  };

  ShadowPreview.prototype.reportReady = function () {
    if (typeof this.options.onReady === 'function') {
      this.options.onReady();
    }
  };

  ShadowPreview.prototype.teardownDom = function () {
    if (this.contentResizeObserver) {
      try {
        this.contentResizeObserver.disconnect();
      } catch {
        // Ignore observer teardown errors.
      }
      this.contentResizeObserver = null;
    }

    if (this.contentMutationObserver) {
      try {
        this.contentMutationObserver.disconnect();
      } catch {
        // Ignore observer teardown errors.
      }
      this.contentMutationObserver = null;
    }

    if (this.heightSyncFrame) {
      clearAnimationFrameCompat(this.heightSyncFrame);
      this.heightSyncFrame = 0;
    }

    this.heightSyncTimers.forEach(function (timerId) {
      clearTimeout(timerId);
    });
    this.heightSyncTimers = [];

    if (this.resizeObserver) {
      try {
        this.resizeObserver.disconnect();
      } catch {
        // Ignore observer teardown errors.
      }
      this.resizeObserver = null;
    }

    if (this.resizeRefreshTimer) {
      clearTimeout(this.resizeRefreshTimer);
      this.resizeRefreshTimer = null;
    }

    if (this.host && this.host.parentNode) {
      this.host.parentNode.removeChild(this.host);
    }
    this.host = null;
    this.shadowRoot = null;
    this.canvasShell = null;
    this.scaleWrap = null;
    this.mountNode = null;
    this.loadedCalcStyles = {};
    this.lastObservedWidth = 0;
    this.lastAppliedHeight = 0;
    this.lastAppliedScale = 1;
    this.lastMeasuredContentHeight = 0;
    this.isCompactPreview = !!this.options.compactPreview;
    this.hasNoWrapFixes = false;
    this.isScaledPreview = false;
  };

  ShadowPreview.prototype.getCompactEnterWidth = function () {
    return Number.isFinite(this.options.compactEnterWidth) && this.options.compactEnterWidth > 0
      ? this.options.compactEnterWidth
      : 0;
  };

  ShadowPreview.prototype.getCompactExitWidth = function () {
    var enterWidth = this.getCompactEnterWidth();
    if (Number.isFinite(this.options.compactExitWidth) && this.options.compactExitWidth > 0) {
      return Math.max(enterWidth, this.options.compactExitWidth);
    }
    return enterWidth;
  };

  ShadowPreview.prototype.getNoWrapFixMinWidth = function () {
    return Number.isFinite(this.options.nowrapFixMinWidth) && this.options.nowrapFixMinWidth > 0
      ? this.options.nowrapFixMinWidth
      : 0;
  };

  ShadowPreview.prototype.getWidthJitterPx = function () {
    return Number.isFinite(this.options.widthJitterPx) && this.options.widthJitterPx > 0
      ? this.options.widthJitterPx
      : DEFAULT_WIDTH_JITTER_PX;
  };

  ShadowPreview.prototype.getMinHeight = function () {
    return Number.isFinite(this.options.minHeight) ? this.options.minHeight : DEFAULT_MIN_HEIGHT;
  };

  ShadowPreview.prototype.getMaxHeight = function () {
    return Number.isFinite(this.options.maxHeight) ? this.options.maxHeight : 2200;
  };

  ShadowPreview.prototype.useNaturalHeight = function () {
    return !!this.options.naturalHeight;
  };

  ShadowPreview.prototype.getViewportWidth = function () {
    if (!this.options.scaleToFit) {
      return 0;
    }

    return Number.isFinite(this.options.viewportWidth) && this.options.viewportWidth > 0
      ? this.options.viewportWidth
      : DEFAULT_VIEWPORT_WIDTH;
  };

  ShadowPreview.prototype.getAvailableWidth = function () {
    var width = 0;

    if (this.host) {
      width = Math.max(width, Math.round(this.host.clientWidth || 0));
    }
    if (this.container) {
      width = Math.max(width, Math.round(this.container.clientWidth || 0));
    }

    return width;
  };

  ShadowPreview.prototype.resolveWidthMode = function (availableWidth) {
    var width = Number.isFinite(availableWidth) && availableWidth > 0
      ? Math.round(availableWidth)
      : this.getAvailableWidth();
    var compactEnterWidth = this.getCompactEnterWidth();
    var compactExitWidth = this.getCompactExitWidth();
    var nowrapFixMinWidth = this.getNoWrapFixMinWidth();
    var compactPreview = this.isCompactPreview;

    if (width > 0 && compactEnterWidth > 0) {
      if (compactPreview) {
        compactPreview = width < compactExitWidth;
      } else {
        compactPreview = width < compactEnterWidth;
      }
    } else if (compactEnterWidth <= 0) {
      compactPreview = !!this.options.compactPreview;
    }

    return {
      width: width,
      compactPreview: compactPreview,
      noWrapFixes: !compactPreview && nowrapFixMinWidth > 0 && width >= nowrapFixMinWidth
    };
  };

  ShadowPreview.prototype.updateWidthMode = function (availableWidth, forceSync) {
    if (!this.host || !this.mountNode) {
      return false;
    }

    var mode = this.resolveWidthMode(availableWidth);
    var widthModeChanged = forceSync
      || mode.compactPreview !== this.isCompactPreview
      || mode.noWrapFixes !== this.hasNoWrapFixes;

    this.isCompactPreview = mode.compactPreview;
    this.hasNoWrapFixes = mode.noWrapFixes;

    if (this.host.classList) {
      this.host.classList.toggle('is-compact-preview', this.isCompactPreview);
      this.host.classList.toggle('is-nowrap-fixes', this.hasNoWrapFixes);
    }
    if (this.mountNode.classList) {
      this.mountNode.classList.toggle('vareoncalc-shadow-canvas--compact', this.isCompactPreview);
    }

    var previousScale = this.isScaledPreview;
    var metrics = this.applyScaleLayout();
    this.isScaledPreview = !!metrics.isScaled;

    return widthModeChanged || previousScale !== this.isScaledPreview;
  };

  ShadowPreview.prototype.getScaleMetrics = function () {
    var viewportWidth = this.getViewportWidth();
    var availableWidth = this.getAvailableWidth();
    var scale = 1;
    var isScaled = false;

    if (viewportWidth > 0 && availableWidth > 0 && availableWidth < viewportWidth) {
      isScaled = true;
      scale = availableWidth / viewportWidth;
    }

    if (!Number.isFinite(scale) || scale <= 0) {
      scale = 1;
      isScaled = false;
    }

    return {
      availableWidth: availableWidth,
      isScaled: isScaled,
      scale: Math.min(1, scale),
      viewportWidth: viewportWidth
    };
  };

  ShadowPreview.prototype.applyScaleLayout = function () {
    if (!this.host || !this.canvasShell || !this.scaleWrap || !this.mountNode) {
      return {
        isScaled: false,
        scale: 1,
        viewportWidth: 0
      };
    }

    var metrics = this.getScaleMetrics();
    var isScaled = metrics.isScaled;
    var naturalHeight = this.useNaturalHeight();

    this.canvasShell.style.width = '100%';
    this.canvasShell.style.maxWidth = '100%';
    this.canvasShell.style.overflow = (naturalHeight && !isScaled) ? 'visible' : 'hidden';

    this.scaleWrap.style.display = 'block';
    this.scaleWrap.style.transformOrigin = 'top left';
    this.scaleWrap.style.transform = isScaled
      ? 'scale(' + metrics.scale.toFixed(4) + ')'
      : 'none';
    this.scaleWrap.style.willChange = isScaled ? 'transform' : 'auto';
    this.scaleWrap.style.width = isScaled ? metrics.viewportWidth + 'px' : '100%';
    this.scaleWrap.style.maxWidth = isScaled ? 'none' : '100%';

    this.mountNode.style.width = '100%';
    this.mountNode.style.maxWidth = '100%';
    this.mountNode.style.minWidth = isScaled
      ? '0px'
      : (this.mountNode.getAttribute('data-shadow-min-width') || '0px');

    if (this.host.classList) {
      this.host.classList.toggle('is-scaled-preview', isScaled);
    }

    return metrics;
  };

  ShadowPreview.prototype.resetHeightFloor = function () {
    if (!this.host || !this.mountNode) {
      return;
    }

    var minHeight = this.getMinHeight();
    this.lastAppliedHeight = 0;
    this.lastAppliedScale = 1;
    this.lastMeasuredContentHeight = 0;
    this.updateWidthMode(this.getAvailableWidth(), false);

    if (this.useNaturalHeight() && !this.isScaledPreview) {
      this.host.style.height = 'auto';
      this.host.style.minHeight = minHeight + 'px';
      if (this.canvasShell) {
        this.canvasShell.style.height = 'auto';
        this.canvasShell.style.minHeight = minHeight + 'px';
      }
      if (this.scaleWrap) {
        this.scaleWrap.style.height = 'auto';
        this.scaleWrap.style.minHeight = '0px';
      }
      this.mountNode.style.height = 'auto';
      this.mountNode.style.minHeight = minHeight + 'px';
      return;
    }

    this.host.style.height = 'auto';
    this.host.style.minHeight = minHeight + 'px';
    if (this.canvasShell) {
      this.canvasShell.style.height = 'auto';
      this.canvasShell.style.minHeight = minHeight + 'px';
    }
    if (this.scaleWrap) {
      this.scaleWrap.style.height = 'auto';
      this.scaleWrap.style.minHeight = '0px';
    }
    this.mountNode.style.height = 'auto';
    this.mountNode.style.minHeight = minHeight + 'px';
  };

  ShadowPreview.prototype.applyMeasuredHeight = function (height) {
    if (!this.host || !this.mountNode) {
      return;
    }

    var nextHeight = Math.ceil(height);
    if (!Number.isFinite(nextHeight) || nextHeight <= 0) {
      this.resetHeightFloor();
      return;
    }

    nextHeight = Math.min(this.getMaxHeight(), nextHeight);
    this.lastMeasuredContentHeight = nextHeight;

    var scaleMetrics = this.applyScaleLayout();
    if (this.useNaturalHeight() && !(scaleMetrics && scaleMetrics.isScaled)) {
      this.lastAppliedHeight = nextHeight;
      this.lastAppliedScale = 1;
      this.host.style.height = 'auto';
      this.host.style.minHeight = '0px';
      if (this.canvasShell) {
        this.canvasShell.style.height = 'auto';
        this.canvasShell.style.minHeight = '0px';
      }
      if (this.scaleWrap) {
        this.scaleWrap.style.height = 'auto';
        this.scaleWrap.style.minHeight = '0px';
      }
      this.mountNode.style.height = 'auto';
      this.mountNode.style.minHeight = '0px';
      return;
    }

    var appliedHeight = Math.ceil(nextHeight * (scaleMetrics.scale || 1));
    if (
      Math.abs(appliedHeight - this.lastAppliedHeight) < 1
      && Math.abs((scaleMetrics.scale || 1) - this.lastAppliedScale) < 0.001
    ) {
      return;
    }

    this.lastAppliedHeight = appliedHeight;
    this.lastAppliedScale = scaleMetrics.scale || 1;
    this.host.style.height = appliedHeight + 'px';
    this.host.style.minHeight = '0px';
    if (this.canvasShell) {
      this.canvasShell.style.height = appliedHeight + 'px';
      this.canvasShell.style.minHeight = '0px';
    }
    if (this.scaleWrap) {
      this.scaleWrap.style.height = nextHeight + 'px';
      this.scaleWrap.style.minHeight = '0px';
    }
    this.mountNode.style.height = nextHeight + 'px';
    this.mountNode.style.minHeight = '0px';
  };

  ShadowPreview.prototype.syncHeight = function () {
    if (this.destroyed || !this.mountNode) {
      return;
    }

    var nextHeight = measureRenderedContentHeight(this.host, this.mountNode);
    if (nextHeight > 0) {
      this.applyMeasuredHeight(nextHeight);
    }
  };

  ShadowPreview.prototype.scheduleHeightSync = function (includeSettledPasses) {
    if (this.destroyed || !this.mountNode) {
      return;
    }

    var self = this;
    if (!this.heightSyncFrame) {
      if (typeof win.requestAnimationFrame === 'function') {
        this.heightSyncFrame = win.requestAnimationFrame(function () {
          self.heightSyncFrame = 0;
          self.syncHeight();
        });
      } else {
        this.heightSyncFrame = setTimeout(function () {
          self.heightSyncFrame = 0;
          self.syncHeight();
        }, 0);
      }
    }

    if (!includeSettledPasses) {
      return;
    }

    this.heightSyncTimers.forEach(function (timerId) {
      clearTimeout(timerId);
    });
    this.heightSyncTimers = HEIGHT_SYNC_DELAYS.map(function (delayMs) {
      return setTimeout(function () {
        self.syncHeight();
      }, delayMs);
    });
  };

  ShadowPreview.prototype.bindContentObservers = function () {
    if (!this.mountNode) {
      return;
    }

    var self = this;

    if (this.contentResizeObserver) {
      this.contentResizeObserver.disconnect();
      this.contentResizeObserver = null;
    }
    if (typeof win.ResizeObserver === 'function') {
      this.contentResizeObserver = new win.ResizeObserver(function () {
        self.scheduleHeightSync(false);
      });
      try {
        this.contentResizeObserver.observe(this.mountNode);
      } catch {
        this.contentResizeObserver = null;
      }
    }

    if (this.contentMutationObserver) {
      this.contentMutationObserver.disconnect();
      this.contentMutationObserver = null;
    }
    if (typeof win.MutationObserver === 'function') {
      this.contentMutationObserver = new win.MutationObserver(function () {
        self.scheduleHeightSync(true);
      });
      try {
        this.contentMutationObserver.observe(this.mountNode, {
          subtree: true,
          childList: true,
          attributes: true,
          attributeFilter: ['style', 'class', 'hidden', 'aria-hidden', 'data-step', 'data-drawer-open']
        });
      } catch {
        this.contentMutationObserver = null;
      }
    }
  };

  ShadowPreview.prototype.setupResizeRefresh = function () {
    if (!this.options.refreshOnResize || !this.container || typeof win.ResizeObserver !== 'function') {
      return;
    }

    var self = this;
    this.resizeObserver = new win.ResizeObserver(function (entries) {
      if (!entries || !entries.length || self.destroyed || !self.latestMessage) {
        return;
      }

      var width = 0;
      entries.forEach(function (entry) {
        if (!entry || !entry.contentRect) {
          return;
        }
        width = Math.max(width, Math.round(entry.contentRect.width || 0));
      });

      if (!width) {
        return;
      }

      if (!self.lastObservedWidth) {
        self.lastObservedWidth = width;
        return;
      }

      // Ignore tiny jiggles caused by subpixel layout churn.
      if (Math.abs(width - self.lastObservedWidth) < self.getWidthJitterPx()) {
        return;
      }
      self.lastObservedWidth = width;
      var layoutChanged = self.updateWidthMode(width, false);
      if (self.lastMeasuredContentHeight > 0) {
        self.applyMeasuredHeight(self.lastMeasuredContentHeight);
      } else if (layoutChanged) {
        self.resetHeightFloor();
      }

      if (self.resizeRefreshTimer) {
        clearTimeout(self.resizeRefreshTimer);
      }

      var resizeDebounceMs = Number.isFinite(self.options.resizeDebounceMs)
        ? self.options.resizeDebounceMs
        : DEFAULT_RESIZE_REFRESH_MS;

      self.resizeRefreshTimer = setTimeout(function () {
        self.resizeRefreshTimer = null;
        if (!self.destroyed && self.latestMessage) {
          self.scheduleHeightSync(layoutChanged);
        }
      }, Math.max(0, resizeDebounceMs));
    });

    try {
      this.resizeObserver.observe(this.container);
      if (this.host) {
        this.resizeObserver.observe(this.host);
      }
    } catch {
      this.resizeObserver = null;
    }
  };

  ShadowPreview.prototype.mount = function () {
    if (this.destroyed || !this.container) {
      return false;
    }

    this.teardownDom();

    var host = doc.createElement('div');
    host.className = this.options.hostClassName || 'vareoncalc-shadow-preview-host';
    host.style.display = 'block';
    host.style.width = '100%';
    host.style.overflow = 'hidden';

    while (this.container.firstChild) {
      this.container.removeChild(this.container.firstChild);
    }
    this.container.appendChild(host);

    var shadowRoot = host.attachShadow({ mode: 'open' });
    var minHeight = this.getMinHeight();
    var minWidth = Number.isFinite(this.options.minWidth) ? this.options.minWidth : DEFAULT_MIN_WIDTH;
    var minWidthCssValue = minWidth > 0 ? 'min(100%, ' + minWidth + 'px)' : '0';
    var shellStyle = doc.createElement('style');
    shellStyle.textContent = [
      ':host { display: block; width: 100%; }',
      ':host(.is-nowrap-fixes) .vareoncalc-section__header { flex-wrap: nowrap !important; }',
      ':host(.is-nowrap-fixes) .vareoncalc-disclosure__header { flex-wrap: nowrap !important; }',
      ':host(.is-nowrap-fixes) .vareoncalc-disclosure__toggle { margin-left: auto; flex: 0 0 auto; max-width: 100%; }',
      '.vareoncalc-shadow-shell { display: block; width: 100%; max-width: 100%; min-height: ' + minHeight + 'px; overflow: hidden; box-sizing: border-box; }',
      '.vareoncalc-shadow-scale-wrap { display: block; width: 100%; max-width: 100%; transform-origin: top left; }',
      '.vareoncalc-shadow-canvas { display: block; width: 100%; max-width: 100%; min-height: ' + minHeight + 'px; min-width: ' + minWidthCssValue + '; box-sizing: border-box; }'
    ].join('\n');
    shadowRoot.appendChild(shellStyle);

    appendLink(shadowRoot, this.buildAssetHref('assets/styles.css'), 'base');
    appendLink(shadowRoot, this.buildAssetHref('assets/calculators.css'), 'calculators');

    if (typeof this.options.inlineCss === 'string' && this.options.inlineCss.trim() !== '') {
      var inlineStyle = doc.createElement('style');
      inlineStyle.textContent = this.options.inlineCss;
      shadowRoot.appendChild(inlineStyle);
    }

    var canvasShell = doc.createElement('div');
    canvasShell.className = 'vareoncalc-shadow-shell';

    var scaleWrap = doc.createElement('div');
    scaleWrap.className = 'vareoncalc-shadow-scale-wrap';

    var mountNode = doc.createElement('div');
    mountNode.className = [
      'vareoncalc',
      'vareoncalc-root',
      'vareoncalc-shadow-canvas',
      this.options.compactPreview ? 'vareoncalc-shadow-canvas--compact' : ''
    ].filter(Boolean).join(' ');
    mountNode.style.minHeight = minHeight + 'px';
    mountNode.style.minWidth = minWidthCssValue;
    mountNode.setAttribute('data-shadow-min-width', minWidthCssValue);
    scaleWrap.appendChild(mountNode);
    canvasShell.appendChild(scaleWrap);
    shadowRoot.appendChild(canvasShell);

    this.host = host;
    this.shadowRoot = shadowRoot;
    this.canvasShell = canvasShell;
    this.scaleWrap = scaleWrap;
    this.mountNode = mountNode;
    this.updateWidthMode(this.getAvailableWidth(), true);
    this.resetHeightFloor();
    this.bindContentObservers();
    this.setupResizeRefresh();

    if (this.latestMessage) {
      this.sendConfig(this.latestMessage, 0);
    }

    return true;
  };

  ShadowPreview.prototype.ensureCalculatorStyle = function (calcType) {
    if (!this.shadowRoot) {
      return;
    }

    var normalizedType = sanitizeCalcType(calcType);
    if (this.loadedCalcStyles[normalizedType]) {
      return;
    }

    appendLink(
      this.shadowRoot,
      this.buildAssetHref('assets/calculators/' + normalizedType + '.css'),
      'calc-' + normalizedType
    );

    this.loadedCalcStyles[normalizedType] = true;
  };

  ShadowPreview.prototype.flushLoaderWaitCallbacks = function (isReady) {
    if (this.loaderWaitTimer) {
      clearTimeout(this.loaderWaitTimer);
      this.loaderWaitTimer = null;
    }
    this.loaderWaitInFlight = false;
    var callbacks = this.loaderWaitCallbacks.slice();
    this.loaderWaitCallbacks = [];
    callbacks.forEach(function (callback) {
      try {
        callback(!!isReady);
      } catch {
        // Ignore callback errors.
      }
    });
  };

  ShadowPreview.prototype.waitForLoaderReady = function (callback) {
    if (typeof callback === 'function') {
      this.loaderWaitCallbacks.push(callback);
    }

    if (typeof win.vareoncalcMount === 'function') {
      this.flushLoaderWaitCallbacks(true);
      return;
    }

    if (this.loaderWaitInFlight) {
      return;
    }

    this.loaderWaitInFlight = true;
    this.loaderWaitStartedAt = Date.now();
    var timeoutMs = Number.isFinite(this.options.timeoutMs) ? this.options.timeoutMs : DEFAULT_TIMEOUT_MS;
    var self = this;

    var startPolling = function () {
      function pollLoader() {
        if (typeof win.vareoncalcMount === 'function') {
          self.flushLoaderWaitCallbacks(true);
          return;
        }

        if ((Date.now() - self.loaderWaitStartedAt) >= timeoutMs) {
          self.flushLoaderWaitCallbacks(false);
          return;
        }

        self.loaderWaitTimer = setTimeout(pollLoader, 25);
      }

      pollLoader();
    };

    if (typeof this.options.ensureLoader === 'function') {
      Promise.resolve(this.options.ensureLoader())
        .catch(function () {
          return null;
        })
        .finally(startPolling);
      return;
    }

    startPolling();
  };

  ShadowPreview.prototype.renderLatestConfig = function () {
    if (this.destroyed || !this.mountNode || !this.latestMessage) {
      return;
    }

    var config = this.latestMessage;
    var self = this;
    this.resetHeightFloor();
    this.updateWidthMode(this.getAvailableWidth(), false);
    this.ensureCalculatorStyle(config.type);

    this.waitForLoaderReady(function (isReady) {
      if (!isReady) {
        if (!self.loaderTimeoutReported) {
          self.loaderTimeoutReported = true;
          self.reportError('Preview loader did not become ready in time.');
        }
        return;
      }

      self.loaderTimeoutReported = false;

      try {
        self.mountNode.setAttribute('data-config', JSON.stringify(config));
        self.mountNode.removeAttribute('data-vareoncalc-ready');
        self.mountNode.innerHTML = '';
        win.vareoncalcMount(self.mountNode);
        self.syncHeight();
        self.scheduleHeightSync(true);
        self.reportReady();
      } catch (error) {
        self.reportError((error && error.message) ? error.message : 'Failed to mount calculator preview.');
      }
    });
  };

  ShadowPreview.prototype.sendConfig = function (config, debounceMs) {
    if (this.destroyed) {
      return;
    }

    this.latestMessage = normalizeConfig(config);
    if (!this.mountNode) {
      return;
    }

    var delay = Number.isFinite(debounceMs) ? debounceMs : DEFAULT_DEBOUNCE_MS;
    if (delay <= 0) {
      this.renderLatestConfig();
      return;
    }

    if (this.updateTimer) {
      clearTimeout(this.updateTimer);
    }

    var self = this;
    this.updateTimer = setTimeout(function () {
      self.renderLatestConfig();
    }, delay);
  };

  ShadowPreview.prototype.setAvailableWidth = function (availableWidth) {
    if (this.destroyed || !this.mountNode) {
      return;
    }

    var layoutChanged = this.updateWidthMode(availableWidth, false);
    if (this.lastMeasuredContentHeight > 0) {
      this.applyMeasuredHeight(this.lastMeasuredContentHeight);
    }
    if (layoutChanged) {
      this.scheduleHeightSync(true);
    }
  };

  ShadowPreview.prototype.retry = function () {
    if (this.destroyed) {
      return;
    }

    if (!this.mountNode) {
      this.mount();
    }
    this.sendConfig(this.latestMessage || { type: 'loan' }, 0);
  };

  ShadowPreview.prototype.destroy = function () {
    this.destroyed = true;

    if (this.updateTimer) {
      clearTimeout(this.updateTimer);
      this.updateTimer = null;
    }

    if (this.loaderWaitTimer) {
      clearTimeout(this.loaderWaitTimer);
      this.loaderWaitTimer = null;
    }

    if (this.resizeRefreshTimer) {
      clearTimeout(this.resizeRefreshTimer);
      this.resizeRefreshTimer = null;
    }

    this.loaderWaitCallbacks = [];
    this.loaderWaitInFlight = false;
    this.teardownDom();
  };

  win.VareoncalcShadowPreview = {
    normalizeConfig: normalizeConfig,
    create: function (options) {
      return new ShadowPreview(options || {});
    }
  };
})(window, document);
