(function(global) {
  'use strict';

  let nudges = [];
  let nudgeIndex = 0;
  let nudgeTimer;

  function fmt(n) {
    return '$' + Math.round(n).toLocaleString('en-US');
  }

  function getEl(id) {
    return document.getElementById(id);
  }

  /* ── Animated number counter ─────────────────────────────────
     Smoothly interpolates a displayed dollar value from old → new.
     Targets both the desktop element and its _m mobile clone.
     Duration: 350ms ease-out via requestAnimationFrame.
  ──────────────────────────────────────────────────────────── */
  var _animFrames = {};
  function animateValue(elId, newVal, formatFn) {
    var el = document.getElementById(elId);
    if (!el) return;
    var elM = document.getElementById(elId + '_m');

    var raw = (el.textContent || '').replace(/[^0-9.\-]/g, '');
    var from = parseFloat(raw) || 0;
    var to = parseFloat(newVal) || 0;

    if (from === to) {
      el.textContent = formatFn(to);
      if (elM) elM.textContent = formatFn(to);
      return;
    }

    if (_animFrames[elId]) cancelAnimationFrame(_animFrames[elId]);

    var duration = 350;
    var start = null;
    function step(ts) {
      if (!start) start = ts;
      var progress = Math.min((ts - start) / duration, 1);
      var eased = 1 - Math.pow(1 - progress, 3);
      var current = from + (to - from) * eased;
      el.textContent = formatFn(current);
      if (elM) elM.textContent = formatFn(current);
      if (progress < 1) {
        _animFrames[elId] = requestAnimationFrame(step);
      } else {
        delete _animFrames[elId];
      }
    }
    _animFrames[elId] = requestAnimationFrame(step);
  }

  function getQuote(q) {
    return q || (typeof global.calcQuote === 'function' ? global.calcQuote() : null);
  }

  function getPricing() {
    return typeof global.getPricingConfig === 'function' ? global.getPricingConfig() : global;
  }

  function getM365BundleSavings(options, pricing) {
    if (options && typeof options.m365BundleSavings === 'number') {
      return options.m365BundleSavings;
    }
    // Use the higher M365 rate (m2m) for maximum savings comparison
    const bestRate = Math.max(pricing.RATE_M365_M2M || 0, pricing.RATE_M365 || 0);
    return Math.max(0, bestRate - (pricing.RATE_BYOL || 0));
  }

  function renderQuoteUi(q, options) {
    if (!q) return;

    const pricing = getPricing();
    const m365BundleSavings = getM365BundleSavings(options, pricing);
    const onboardingWaived = !!options?.onboardingWaived;
    const onboardingHalfOff = !!options?.onboardingHalfOff;
    const onboardingWouldBe = typeof options?.onboardingWouldBe === 'number'
      ? options.onboardingWouldBe
      : Math.round((q.MRR || 0) / 2);
    const {
      users,
      voipTier,
      byol,
      addPWM,
      ztActive,
      adminWaived,
      clientName,
      contractTerm,
      baseSubtotal,
      siteAdminBase,
      admin1PWM,
      adminFeeNet,
      discountAmt
    } = q;

    if (adminWaived) {
      getEl('adminFeeDisplay').innerHTML =
        `<span class="admin-fee-strike">${fmt(adminFeeNet)}/mo</span> <span class="admin-fee-waived-badge">WAIVED</span>`;
    } else {
      getEl('adminFeeDisplay').textContent = fmt(adminFeeNet) + '/mo';
    }

    const fillPct = Math.min(100, Math.max(0, (baseSubtotal / pricing.ADMIN_FEE_MINIMUM) * 100));
    getEl('floorBar').style.width = fillPct + '%';
    const atFloor = baseSubtotal >= pricing.ADMIN_FEE_MINIMUM;
    getEl('floorBar').style.background = atFloor ? 'var(--green)' : 'var(--accent)';
    getEl('floorNote').textContent = atFloor
      ? `✓ Oversight fee is now at its minimum level (${fmt(pricing.ADMIN_FEE_FLOOR)}/mo)`
      : `Add ${fmt(Math.max(0, pricing.ADMIN_FEE_MINIMUM - baseSubtotal))} more in managed services and this fee will reduce further`;

    getEl('fb-base').textContent = fmt(siteAdminBase);
    getEl('fb-zt-row').classList.toggle('hidden', !ztActive);
    getEl('fb-zt').textContent = '+' + fmt(pricing.ADMIN_FEE_ZT);
    getEl('fb-pwm-row').classList.toggle('hidden', !addPWM);
    getEl('fb-pwm').textContent = '+' + fmt(admin1PWM);
    if (adminWaived) {
      getEl('fb-total').innerHTML =
        `<span class="admin-fee-strike">${fmt(adminFeeNet)}</span> <span class="admin-fee-waived-badge">WAIVED</span>`;
    } else {
      getEl('fb-total').textContent = fmt(adminFeeNet);
    }

    const adminWaivedSavingsEl = document.getElementById('adminWaivedSavings');
    if (adminWaivedSavingsEl) {
      adminWaivedSavingsEl.classList.toggle('hidden', !adminWaived || adminFeeNet === 0);
      const awAmt = document.getElementById('adminWaivedAmt');
      if (awAmt) awAmt.textContent = fmt(adminFeeNet);
    }

    getEl('byolCalloutGreen').classList.toggle('hidden', byol);
    getEl('byolCalloutRed').classList.toggle('hidden', !byol);
    getEl('userIncluded').classList.toggle('byol-mode', byol);
    getEl('userBundleStrip')?.classList.toggle('byol-disabled', byol);

    // M365 term-aware pricing display
    const isAnnualTerm = contractTerm === '12mo' || contractTerm === '24mo';
    const m365PriceM2m = getEl('m365PriceM2m');
    const m365PriceAnnual = getEl('m365PriceAnnual');
    if (m365PriceM2m && m365PriceAnnual) {
      if (isAnnualTerm) {
        m365PriceM2m.className = 'm365-price-grey';
        m365PriceM2m.textContent = '$' + pricing.RATE_M365_M2M;
        m365PriceAnnual.classList.remove('hidden');
        m365PriceAnnual.textContent = '$' + pricing.RATE_M365;
      } else {
        m365PriceM2m.className = '';
        m365PriceM2m.textContent = '$' + pricing.RATE_M365_M2M;
        m365PriceAnnual.classList.add('hidden');
      }
    }
    const retailRate = isAnnualTerm ? pricing.M365_RETAIL_ANNUAL : pricing.M365_RETAIL_MONTHLY;
    const svsRate = isAnnualTerm ? pricing.RATE_M365 : pricing.RATE_M365_M2M;
    const officePortion = svsRate - pricing.RATE_BYOL;
    const officeSavings = retailRate - officePortion;
    const savingsLine = getEl('m365SavingsLine');
    if (savingsLine) savingsLine.classList.add('hidden');
    const m365CalloutText = getEl('m365CalloutText');
    if (m365CalloutText && !byol) {
      const retailLabel = isAnnualTerm ? '$' + retailRate + '/user annual' : '$' + retailRate + '/user monthly';
      m365CalloutText.textContent = 'Recommended bundle \u2014 M365 Business Premium is built into this seat, saving $' + officeSavings + '/user vs ' + retailLabel + ' retail licensing';
    }

    if (byol) getEl('byolRedSavings').textContent = fmt(users * m365BundleSavings);

    ['basic', 'standard', 'premium'].forEach(tier => {
      const seg = getEl('seg-' + tier);
      if (seg) seg.classList.toggle('active', tier === voipTier);
    });

    ['m2m', '12mo', '24mo'].forEach(term => {
      const seg = getEl('seg-term-' + term);
      if (seg) seg.classList.toggle('active', term === contractTerm);
    });

    const savingsRow = document.getElementById('qsSavingsDisplay');
    const savingsCopy = document.getElementById('qsSavingsCopy');
    const firstYearRow = document.getElementById('qsFirstYearDisplay');
    const firstYearCopy = document.getElementById('qsFirstYearCopy');
    const termLabel = contractTerm === '12mo' ? '12-Month' : contractTerm === '24mo' ? '24-Month' : 'Month-to-Month';
    const annualizedTermSavings = discountAmt > 0 ? discountAmt * 12 : 0;
    const termOnboardingValue = onboardingWaived ? onboardingWouldBe
      : onboardingHalfOff ? Math.round(onboardingWouldBe / 2) : 0;
    const firstYearValue = annualizedTermSavings + termOnboardingValue;
    if (savingsRow && savingsCopy) {
      if (discountAmt > 0) {
        savingsCopy.innerHTML = `${termLabel} savings: <strong>${fmt(discountAmt)}</strong>/mo vs. month-to-month`;
        savingsRow.classList.remove('hidden');
      } else {
        savingsRow.classList.add('hidden');
      }
    }
    if (firstYearRow && firstYearCopy) {
      if (firstYearValue > 0) {
        let suffix = ' in year-one value';
        if (annualizedTermSavings > 0 && onboardingWaived) {
          suffix = ' from annualized term savings and complimentary onboarding';
        } else if (annualizedTermSavings > 0 && onboardingHalfOff) {
          suffix = ' from annualized term savings and 50% off onboarding';
        } else if (annualizedTermSavings > 0) {
          suffix = ' from annualized term savings';
        } else if (onboardingWaived) {
          suffix = ' from complimentary onboarding';
        } else if (onboardingHalfOff) {
          suffix = ' from 50% off onboarding';
        }
        firstYearCopy.innerHTML = `Year-one value unlocked: <strong>${fmt(firstYearValue)}</strong>${suffix}`;
        firstYearRow.classList.remove('hidden');
      } else {
        firstYearRow.classList.add('hidden');
      }
    }

    document.querySelectorAll('.addon-preview-pill[data-addon]').forEach(pill => {
      const cb = document.getElementById(pill.dataset.addon);
      pill.classList.toggle('active', cb?.checked || false);
    });
  }

  function renderSidebar(q, options) {
    if (!q) return;

    const pricing = getPricing();
    const {
      users,
      endpoints,
      servers,
      voipSeats,
      byol,
      addPWM,
      addINKY,
      addExtHours,
      addZT,
      addUSB,
      addBMB,
      ztActive,
      adminWaived,
      contractTerm,
      hstEnabled,
      baseUserRate,
      userPWM,
      userINKY,
      userExt,
      userZT,
      userTotal,
      serverBase,
      endpointUSB,
      endpointBMB,
      endpointTotal,
      ztNetTotal,
      voipTotal,
      siteAdminBase,
      admin1PWM,
      adminFeeNet,
      clientName,
      MRR,
      discountPct,
      discountAmt,
      effectiveMrr,
      effectiveAnnual,
      hstAmt,
      mrrWithHst,
      perUserServices,
      perUserSiteOvhd
    } = q;

    const onboardingWaived = !!options?.onboardingWaived;
    const onboardingHalfOff = !!options?.onboardingHalfOff;
    const onboardingWouldBe = typeof options?.onboardingWouldBe === 'number'
      ? options.onboardingWouldBe
      : Math.round(q.MRR / 2);
    const oneTimeFee = typeof options?.oneTimeFee === 'number'
      ? options.oneTimeFee
      : (parseFloat(q.oneTimeFee) || 0);
    const annualizedTermSavings = discountPct > 0 ? discountAmt * 12 : 0;
    const onboardingValueUnlocked = onboardingWaived ? onboardingWouldBe
      : onboardingHalfOff ? Math.round(onboardingWouldBe / 2) : 0;
    const isAnnualTermSb = contractTerm === '12mo' || contractTerm === '24mo';
    const retailRateSb = isAnnualTermSb ? (pricing.M365_RETAIL_ANNUAL || 30) : (pricing.M365_RETAIL_MONTHLY || 35);
    const svsRateSb = isAnnualTermSb ? pricing.RATE_M365 : (pricing.RATE_M365_M2M || pricing.RATE_M365);
    const officePortionSb = svsRateSb - pricing.RATE_BYOL;
    const officeSavingsSb = Math.max(0, retailRateSb - officePortionSb);
    const m365AnnualValue = (!byol && users > 0) ? (users * officeSavingsSb * 12) : 0;
    const byolOpportunityValue = (byol && users > 0) ? (users * getM365BundleSavings(options, pricing) * 12) : 0;
    const adminIncludedValue = adminWaived ? (adminFeeNet * 12) : 0;
    const totalValueUnlocked = m365AnnualValue + annualizedTermSavings + onboardingValueUnlocked + adminIncludedValue;
    const firstInvoiceTotal = effectiveMrr + (hstEnabled ? hstAmt : 0) + oneTimeFee;
    const sidebarClientNameEl = getEl('sidebarFocusClientName');
    if (sidebarClientNameEl) {
      sidebarClientNameEl.textContent = clientName && clientName.trim() ? clientName.trim() : 'Client Name';
    }

    const show = (id, val) => {
      const el = getEl(id);
      if (!el) return;
      el.classList.toggle('hidden', !val);
    };
    const renderSubRows = (rows) => rows.map(row => {
      if (!row.addon) {
        return `<div class="sl-sub-row sl-sub-row-base"><span class="sl-sub-copy">${row.copy}</span></div>`;
      }

      return `<div class="sl-sub-row sl-sub-row-addon"><span class="sl-sub-copy">${row.copy}</span><span class="sl-sub-val">${row.value}</span></div>`;
    }).join('');

    show('sl-users', users > 0);
    getEl('sl-users-sub')?.classList.toggle('hidden', users === 0);
    if (users > 0) {
      animateValue('sl-users-val', userTotal, fmt);
      const sub = getEl('sl-users-sub');
      sub.classList.remove('hidden');
      const subRows = [{ copy: `${users} × ${fmt(baseUserRate)}/user (${byol ? 'BYOL' : 'M365 Incl.'})` }];
      if (addExtHours) subRows.push({ copy: 'Extended Help Desk Hours', value: `${fmt(userExt)}/mo`, addon: true });
      if (addPWM) subRows.push({ copy: '1Password', value: `${fmt(userPWM)}/mo`, addon: true });
      if (addINKY) subRows.push({ copy: 'INKY Pro Upgrade', value: `${fmt(userINKY)}/mo`, addon: true });
      if (addZT) subRows.push({ copy: 'Zero Trust', value: `${fmt(userZT)}/mo`, addon: true });
      sub.innerHTML = renderSubRows(subRows);
    }

    show('sl-endpoints', endpoints > 0);
    getEl('sl-endpoints-sub')?.classList.toggle('hidden', endpoints === 0);
    if (endpoints > 0) {
      const epOnly = endpointTotal - serverBase;
      animateValue('sl-endpoints-val', epOnly, fmt);
      const sub = getEl('sl-endpoints-sub');
      sub.classList.remove('hidden');
      const epRows = [{ copy: `${endpoints} × ${fmt(pricing.RATE_ENDPOINT)}/endpoint` }];
      if (addBMB) epRows.push({ copy: 'Bare Metal Backup', value: `${fmt(endpointBMB)}/mo`, addon: true });
      if (addUSB) epRows.push({ copy: 'USB Blocking', value: `${fmt(endpointUSB)}/mo`, addon: true });
      sub.innerHTML = renderSubRows(epRows);
    }

    show('sl-servers', servers > 0);
    if (servers > 0) animateValue('sl-servers-val', serverBase, fmt);

    show('sl-zt', ztNetTotal > 0);
    if (ztNetTotal > 0) animateValue('sl-zt-val', ztNetTotal, fmt);

    show('sl-voip', voipTotal > 0);
    if (voipTotal > 0) animateValue('sl-voip-val', voipTotal, fmt);

    const slAdminEl = getEl('sl-admin');
    const slAdminValEl = getEl('sl-admin-val');
    const slAdminSubEl = getEl('sl-admin-sub');
    if (adminWaived) {
      slAdminEl?.classList.add('sl-admin-waived');
      if (slAdminValEl) {
        slAdminValEl.innerHTML =
          `<span class="otf-amt">${fmt(adminFeeNet)}</span> <span class="otf-waived-label">WAIVED</span>`;
      }
    } else {
      slAdminEl?.classList.remove('sl-admin-waived');
      if (slAdminValEl) slAdminValEl.textContent = fmt(adminFeeNet);
    }
    if (slAdminSubEl) {
      const adminRows = [{ copy: `Base ${fmt(siteAdminBase)}/mo` }];
      if (ztActive) adminRows.push({ copy: 'Zero Trust Supplement', value: `${fmt(pricing.ADMIN_FEE_ZT)}/mo`, addon: true });
      if (addPWM && admin1PWM > 0) adminRows.push({ copy: '1Password Admin', value: `${fmt(admin1PWM)}/mo`, addon: true });
      slAdminSubEl.classList.remove('hidden');
      slAdminSubEl.innerHTML = renderSubRows(adminRows);
    }

    animateValue('mrrDisplay', effectiveMrr, fmt);
    const monthlyTotalEl = getEl('sl-monthly-total-val');
    if (monthlyTotalEl) monthlyTotalEl.innerHTML = fmt(effectiveMrr) + '<span class="suffix-mo">/mo</span>';
    getEl('annualDisplay').textContent = fmt(effectiveAnnual);
    getEl('perUserRow').classList.toggle('hidden', users === 0);
    if (users > 0) getEl('perUserDisplay').textContent = fmt(effectiveMrr / users) + '/user';

    const discountRow = getEl('sl-discount-row');
    if (discountRow) {
      discountRow.classList.toggle('hidden', discountPct === 0);
      if (discountPct > 0) {
        const termLabel = contractTerm === '12mo' ? '12-Month' : '24-Month';
        const discDetailEl = getEl('sl-discount-detail');
        if (discDetailEl) {
          discDetailEl.textContent = `(${termLabel}, ${Math.round(discountPct * 100)}% off)`;
        }
        const discEl = getEl('sl-discount-val');
        if (discEl) {
          discEl.textContent = `−${fmt(discountAmt)}/mo`;
        }
      }
    }

    const baseMrrRow = getEl('sl-base-mrr-row');
    if (baseMrrRow) baseMrrRow.classList.toggle('hidden', discountPct === 0);
    const baseMrrEl = getEl('sl-base-mrr-val');
    if (baseMrrEl && discountPct > 0) baseMrrEl.textContent = fmt(MRR) + '/mo';

    const firstMriEl = getEl('sl-first-mri-val');
    if (firstMriEl) firstMriEl.textContent = fmt(effectiveMrr);
    show('sl-first-hst-row', hstEnabled);
    const firstHstEl = getEl('sl-first-hst-val');
    if (firstHstEl && hstEnabled) firstHstEl.textContent = fmt(hstAmt);

    getEl('sl-hst-row')?.classList.toggle('hidden', !hstEnabled);
    getEl('sl-hst-total-row')?.classList.toggle('hidden', !hstEnabled);
    if (hstEnabled) {
      const hstEl = getEl('sl-hst-val');
      if (hstEl) hstEl.textContent = `+${fmt(hstAmt)}`;
      const totalEl = getEl('sl-hst-total-val');
      if (totalEl) totalEl.textContent = fmt(mrrWithHst) + '/mo';
    }

    const otfRow = getEl('sl-otf-row');
    const otfValEl = getEl('sl-otf-val');
    if (otfRow) {
      if (onboardingWaived && onboardingWouldBe > 0) {
        otfRow.classList.remove('hidden');
        otfRow.classList.add('sl-otf-waived');
        if (otfValEl) {
          otfValEl.innerHTML =
            '<span class="otf-amt-strike">' + fmt(onboardingWouldBe) + '</span> <span class="otf-waived-label">COMPLIMENTARY</span>';
        }
      } else if (onboardingHalfOff && onboardingWouldBe > 0) {
        otfRow.classList.remove('hidden');
        otfRow.classList.add('sl-otf-waived');
        if (otfValEl) {
          otfValEl.innerHTML =
            '<span class="otf-waived-label">50% OFF</span> ' +
            '<span class="otf-amt-strike">' + fmt(onboardingWouldBe) + '</span> ' +
            '<span class="otf-amt">' + fmt(oneTimeFee) + '</span>';
        }
      } else if (oneTimeFee > 0) {
        otfRow.classList.remove('hidden', 'sl-otf-waived');
        if (otfValEl) otfValEl.textContent = fmt(oneTimeFee) + ' (one-time)';
      } else {
        otfRow.classList.add('hidden');
        otfRow.classList.remove('sl-otf-waived');
      }
    }
    const firstTotalEl = getEl('sl-first-total-val');
    if (firstTotalEl) firstTotalEl.textContent = fmt(firstInvoiceTotal);

    show('sl-value-m365-row', m365AnnualValue > 0);
    const valueM365El = getEl('sl-value-m365-val');
    if (valueM365El && m365AnnualValue > 0) valueM365El.textContent = fmt(m365AnnualValue) + '/yr';

    show('sl-value-term-row', annualizedTermSavings > 0);
    const valueTermEl = getEl('sl-value-term-val');
    if (valueTermEl && annualizedTermSavings > 0) valueTermEl.textContent = fmt(annualizedTermSavings) + '/yr';

    show('sl-value-onboarding-row', onboardingValueUnlocked > 0);
    const valueOnboardingEl = getEl('sl-value-onboarding-val');
    if (valueOnboardingEl && onboardingValueUnlocked > 0) valueOnboardingEl.textContent = fmt(onboardingValueUnlocked);
    const valueOnboardingLabel = getEl('sl-value-onboarding-label');
    if (valueOnboardingLabel) valueOnboardingLabel.textContent = onboardingWaived ? 'Complimentary Onboarding' : 'Onboarding Savings (50% off)';

    show('sl-value-admin-row', adminIncludedValue > 0);
    const valueAdminEl = getEl('sl-value-admin-val');
    if (valueAdminEl && adminIncludedValue > 0) valueAdminEl.textContent = fmt(adminIncludedValue) + '/yr';

    show('sl-value-byol-row', byolOpportunityValue > 0);
    const valueByolEl = getEl('sl-value-byol-val');
    if (valueByolEl && byolOpportunityValue > 0) valueByolEl.textContent = fmt(byolOpportunityValue) + '/yr';

    show('sl-value-total-row', totalValueUnlocked > 0);
    const valueTotalEl = getEl('sl-value-total-val');
    if (valueTotalEl && totalValueUnlocked > 0) valueTotalEl.textContent = fmt(totalValueUnlocked) + '/yr';

    const puBreakdown = getEl('perUserBreakdown');
    if (puBreakdown) {
      puBreakdown.classList.toggle('hidden', users === 0);
      if (users > 0) {
        puBreakdown.textContent = `Includes ${fmt(perUserServices)} in user services + ${fmt(perUserSiteOvhd)} in shared site overhead`;
      }
    }
  }

  function buildNudges(q, options) {
    if (!q) return [];

    const pricing = getPricing();
    const m365BundleSavings = getM365BundleSavings(options, pricing);
    const {
      users,
      endpoints,
      servers,
      voipSeats,
      voipTier,
      contractTerm,
      MRR,
      byol,
      addPWM,
      addExtHours,
      addZT,
      addBMB
    } = q;

    const nextNudges = [];

    if (!addZT && users > 0) {
      nextNudges.push({
        text: `Zero Trust Deny By Default adds enterprise-grade access control — ${fmt(users * pricing.ADDON_ZERO_TRUST_USER)}/mo for all ${users} user${users !== 1 ? 's' : ''}. Recommended for any client with remote staff or sensitive data.`,
        color: 'amber'
      });
    }

    if (!addPWM && users > 0) {
      nextNudges.push({
        text: `1Password Management — one vault for every credential, enforce strong passwords, revoke access instantly when staff leave. Only ${fmt(users * pricing.ADDON_1PASSWORD)}/mo for ${users} user${users !== 1 ? 's' : ''}.`,
        color: 'green'
      });
    }

    if (contractTerm === 'm2m' && MRR > 0) {
      const onboardingWouldBe = typeof options?.onboardingWouldBe === 'number'
        ? options.onboardingWouldBe
        : Math.round(MRR / 2);
      nextNudges.push({
        text: `A 12-month term saves 3% on MRR and 50% off onboarding. The 24-month option saves ${fmt(MRR * pricing.DISCOUNT_24MO)}/mo (5%) and includes complimentary onboarding — saving an estimated ${fmt(onboardingWouldBe)}.`,
        color: 'green'
      });
    }

    if (byol && users > 0) {
      const m365RateForTerm = contractTerm === 'm2m' ? (pricing.RATE_M365_M2M || pricing.RATE_M365) : pricing.RATE_M365;
      nextNudges.push({
        text: `BYOL keeps the client's existing licensing in place. Switching to M365 Included (${fmt(m365RateForTerm)}/user) bundles licensing at well below retail — saving up to ${fmt(users * m365BundleSavings)}/mo across ${users} user${users !== 1 ? 's' : ''}.`,
        color: 'green'
      });
    }

    if (q.ztActive) {
      nextNudges.push({
        text: `Zero Trust is active — a ${fmt(pricing.ADMIN_FEE_ZT)}/mo site admin supplement applies to cover additional tenant management, policy enforcement, and network oversight.`,
        color: 'amber'
      });
    }

    if (endpoints > 0 && !addBMB) {
      nextNudges.push({
        text: `Bare Metal Backup adds the recovery layer to this endpoint package — ${fmt(endpoints * pricing.ADDON_BARE_METAL_BACKUP)}/mo protects all ${endpoints} endpoint${endpoints !== 1 ? 's' : ''} with image-level restore after ransomware, hardware failure, or a full rebuild event.`,
        color: 'amber'
      });
    }

    if (voipSeats > 0 && voipTier === 'basic') {
      nextNudges.push({
        text: `Standard tier adds HD video, call analytics, and advanced call routing — only ${fmt(voipSeats * (pricing.VOIP_RATE_STANDARD - pricing.VOIP_RATE_BASIC))}/mo more for ${voipSeats} seat${voipSeats !== 1 ? 's' : ''}. Most clients upgrade within 6 months.`,
        color: 'green'
      });
    }

    if (servers > 0 && !addBMB) {
      nextNudges.push({
        text: `Servers are in scope but Bare Metal Backup is not selected — adding BMB closes the DR gap by pairing endpoint protection with full-system recovery across workstations and server workloads.`,
        color: 'amber'
      });
    }

    if (users > 0 && endpoints > 0 && endpoints > users * 1.5) {
      nextNudges.push({
        text: `${endpoints} endpoints vs ${users} users — that's a high endpoint-to-user ratio. Consider whether unmanaged devices should be attached to user seats to ensure full coverage.`,
        color: 'amber'
      });
    }

    if (users > 0 && endpoints === 0) {
      nextNudges.push({
        text: `${users} user${users !== 1 ? 's' : ''} configured but no endpoints — most environments need workstation management alongside user services. Add endpoints to close the coverage gap.`,
        color: 'amber'
      });
    }

    if (voipSeats > 0 && users > 0 && voipSeats !== users) {
      const diff = Math.abs(voipSeats - users);
      const direction = voipSeats > users ? 'more VoIP seats than users' : 'fewer VoIP seats than users';
      nextNudges.push({
        text: `${voipSeats} VoIP seat${voipSeats !== 1 ? 's' : ''} vs ${users} user${users !== 1 ? 's' : ''} — ${diff} ${direction}. Verify whether every user needs a phone line, or if shared/common-area lines explain the difference.`,
        color: 'amber'
      });
    }

    if (MRR > 0 && !q.adminWaived && q.adminFeeNet > MRR * 0.25) {
      nextNudges.push({
        text: `Site admin is ${fmt(q.adminFeeNet)}/mo — over 25% of the ${fmt(MRR)} MRR. Adding more managed services will reduce this ratio as the admin fee floors out at ${fmt(pricing.ADMIN_FEE_FLOOR)}/mo.`,
        color: 'amber'
      });
    }

    if (!addExtHours && users > 0) {
      nextNudges.push({
        text: `Extended Help Desk Hours adds after-hours and weekend support coverage — only ${fmt(users * pricing.ADDON_EXT_HOURS)}/mo for ${users} user${users !== 1 ? 's' : ''}. Ideal for clients with shift workers or off-hours operations.`,
        color: 'green'
      });
    }

    return nextNudges;
  }

  function setNudges(nextNudges) {
    nudges = Array.isArray(nextNudges) ? nextNudges : [];

    if (!nudges.length) {
      nudgeIndex = 0;
      return;
    }

    if (nudgeIndex >= nudges.length) {
      nudgeIndex = 0;
    }
  }

  function renderNudge() {
    const currentIndex = nudges.length ? nudgeIndex : 0;

    function applyNudge(suffix) {
      const s = suffix || '';
      const banner = document.getElementById('nudgeBanner' + s);
      if (!banner) return;
      if (!nudges.length) {
        banner.classList.add('hidden');
        return;
      }

      const n = nudges[currentIndex];
      banner.classList.remove('hidden', 'amber', 'green');
      banner.classList.add(n.color);
      const textEl = document.getElementById('nudgeText' + s);
      const cntEl = document.getElementById('nudgeCounter' + s);
      if (textEl) textEl.textContent = n.text;
      if (cntEl) cntEl.textContent = nudges.length > 1 ? `${currentIndex + 1}/${nudges.length}` : '';
      const btns = banner.querySelectorAll('button');
      btns.forEach(btn => {
        btn.style.display = nudges.length > 1 ? 'flex' : 'none';
      });
    }

    applyNudge('');
    applyNudge('_m');
  }

  function cycleNudge(dir) {
    if (!nudges.length) return;
    // Crossfade: fade out, swap, fade in
    var banners = [document.getElementById('nudgeBanner'), document.getElementById('nudgeBanner_m')];
    banners.forEach(function(b) { if (b) b.classList.add('nudge-fading'); });
    setTimeout(function() {
      nudgeIndex = (nudgeIndex + dir + nudges.length) % nudges.length;
      renderNudge();
      banners.forEach(function(b) { if (b) b.classList.remove('nudge-fading'); });
    }, 180);
  }

  function startNudgeRotation() {
    if (nudgeTimer) clearInterval(nudgeTimer);
    nudgeTimer = setInterval(() => {
      if (nudges.length > 1) {
        cycleNudge(1);
      }
    }, 30000);
  }

  function updateSectionSummaries(q) {
    q = getQuote(q);
    if (!q) return;

    const collapsed = id => !document.getElementById(id)?.classList.contains('sec-open');
    const setSummary = (id, text, numericVal) => {
      const el = document.getElementById(id);
      if (!el) return;
      const secId = id.replace('-summary', '').replace('sec0', 'sec-0');
      const show = collapsed(secId) && !!text;
      if (numericVal !== undefined && show) {
        animateValue(id, numericVal, function(v) { return fmt(v) + '/mo'; });
      } else {
        el.textContent = text || '';
      }
      el.hidden = !show;
      el.style.display = show ? 'flex' : 'none';
    };

    setSummary('sec01-summary', q.adminWaived ? 'WAIVED' : fmt(q.adminFeeNet) + '/mo');
    setSummary('sec02-summary', q.users > 0 ? `${fmt(q.userTotal)}/mo` : '', q.users > 0 ? q.userTotal : undefined);
    setSummary('sec03-summary', q.endpoints > 0 ? `${fmt(q.endpointTotal - q.serverBase)}/mo` : '', q.endpoints > 0 ? q.endpointTotal - q.serverBase : undefined);
    setSummary('sec04-summary', q.servers > 0 ? `${fmt(q.serverBase)}/mo` : '', q.servers > 0 ? q.serverBase : undefined);
    setSummary('sec05-summary', q.ztNetTotal > 0 ? `${fmt(q.ztNetTotal)}/mo` : '', q.ztNetTotal > 0 ? q.ztNetTotal : undefined);
    setSummary('sec06-summary', q.voipSeats > 0 ? `${fmt(q.voipTotal)}/mo` : '', q.voipSeats > 0 ? q.voipTotal : undefined);
  }

  function updateVsComparison(q) {
    const vsEl = document.getElementById('vsComparison');
    if (!vsEl) return;

    q = getQuote(q);
    if (!q) return;

    const pricing = getPricing();
    const { users, endpoints, effectiveAnnual } = q;
    if (users < 1 && endpoints < 1) {
      vsEl.classList.add('hidden');
      return;
    }
    vsEl.classList.remove('hidden');

    const toolsMonthly = Math.max(
      pricing.TOOL_COST_MIN,
      (users * pricing.TOOL_COST_PER_USER) + (endpoints * pricing.TOOL_COST_PER_ENDPOINT)
    );
    const toolsAnnual = toolsMonthly * 12;
    const cost1 = pricing.IT_SALARY_1 + toolsAnnual;
    const cost5 = pricing.IT_SALARY_5 + toolsAnnual;
    const save1 = cost1 - effectiveAnnual;
    const save5 = cost5 - effectiveAnnual;
    const fmtK = n => n >= 0 ? '$' + Math.round(n / 1000) + 'K/yr' : '–$' + Math.round(Math.abs(n) / 1000) + 'K/yr';

    getEl('vs-svs-annual').textContent = '$' + Math.round(effectiveAnnual / 1000) + 'K/yr';
    getEl('vs-1man-cost').textContent = '$' + Math.round(cost1 / 1000) + 'K/yr';
    getEl('vs-5man-cost').textContent = '$' + Math.round(cost5 / 1000) + 'K/yr';

    const applyVsRow = (rowId, saveId, labelId, saving) => {
      const row = getEl(rowId);
      const val = getEl(saveId);
      const lbl = getEl(labelId);
      if (!row || !val || !lbl) return;
      val.textContent = fmtK(saving);
      row.classList.remove('vs-save-green', 'vs-save-amber');
      val.classList.remove('vs-val-green', 'vs-val-amber');
      lbl.classList.remove('vs-val-green', 'vs-val-amber');
      if (saving > 0) {
        row.classList.add('vs-save-green');
        val.classList.add('vs-val-green');
        lbl.classList.add('vs-val-green');
        lbl.textContent = 'EST. SAVINGS';
      } else {
        row.classList.add('vs-save-amber');
        val.classList.add('vs-val-amber');
        lbl.classList.add('vs-val-amber');
        lbl.textContent = 'DIFFERENCE';
      }
    };

    applyVsRow('vs-1man-save-row', 'vs-1man-save', 'vs-1man-save-lbl', save1);
    applyVsRow('vs-5man-save-row', 'vs-5man-save', 'vs-5man-save-lbl', save5);

    const toolsLabel = toolsMonthly <= pricing.TOOL_COST_MIN
      ? `min $${pricing.TOOL_COST_MIN}/mo`
      : `~$${toolsMonthly}/mo`;
    getEl('vs-footnote').textContent =
      `Based on ~$${Math.round(pricing.IT_SALARY_1 / 1000)}K Ottawa IT salary + ${toolsLabel} tool licensing (M365, EDR, RMM, backup, SAT & more). Excludes benefits, recruiting, PTO coverage, and turnover costs.`;
  }

  function updateSavings(q) {
    const billEl = document.getElementById('currentPhoneBill');
    const comparator = document.getElementById('savingsComparator');
    const prompt = document.getElementById('savingsPrompt');
    if (!billEl || !comparator) return;

    const bill = parseFloat(billEl.value) || 0;
    q = getQuote(q);
    if (!q) return;

    const { voipTotal, voipSeats } = q;
    if (voipSeats < 1) {
      comparator.classList.add('hidden');
      if (prompt) prompt.style.display = 'none';
      return;
    }

    if (prompt) prompt.style.display = bill === 0 ? '' : 'none';
    if (bill === 0) {
      comparator.classList.add('hidden');
      return;
    }

    comparator.classList.remove('hidden');
    const saving = bill - voipTotal;
    if (saving > 0) {
      comparator.textContent = `✓ Switching to SVS VoIP saves ~${fmt(saving)}/mo (${fmt(saving * 12)}/yr) vs your current bill of ${fmt(bill)}/mo`;
      comparator.classList.remove('savings-amber');
    } else {
      comparator.textContent = `Your current bill (${fmt(bill)}/mo) is lower than this VoIP quote (${fmt(voipTotal)}/mo) — consider reviewing the tier or seat count.`;
      comparator.classList.add('savings-amber');
    }
  }

  const api = {
    renderQuoteUi,
    renderSidebar,
    buildNudges,
    setNudges,
    updateSectionSummaries,
    updateVsComparison,
    renderNudge,
    cycleNudge,
    startNudgeRotation,
    updateSavings
  };

  global.SVSQuoteRender = api;
  global.updateSectionSummaries = updateSectionSummaries;
  global.updateVsComparison = updateVsComparison;
  global.renderNudge = renderNudge;
  global.cycleNudge = cycleNudge;
  global.startNudgeRotation = startNudgeRotation;
  global.updateSavings = updateSavings;

  startNudgeRotation();
})(window);
