Files
svsmspcalc/SVS-MSP-Calculator.html
John OReilly e462953a2a css: tokenize admin fee margins + remove redundant inline style — spacing audit pass 3
Replaced 2 hardcoded 6px margin-bottom values with var(--space-xs) in admin-fee-header
and admin-waive-savings. Removed redundant inline style="margin-bottom:16px" from sec-01
subtitle (handled by section-content * + * rule). Full visual audit across Dark/Light/Glass
at 1920px, print media, and mobile (375px/600px/780px landscape) — no regressions found.
Updated SESSION-HANDOFF, CLAUDE.md, DECISION-LOG, KNOWN-ISSUES, and MASTER-SESSION-PROMPT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 15:28:49 -04:00

1013 lines
66 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVS MSP — Live Quote Calculator</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@700&family=Poppins:wght@500;600;700&family=Lato:wght@400;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="SVS-MSP-Calculator.css">
</head>
<body>
<!-- ════════════════════════════════════════════════════════════
MOBILE FLOATING QUOTE PILL
Fixed position, top-right, z-index:200 (above header z:100).
Visible only on ≤1100px via CSS.
Shows live MRR from #mrrDisplay via update() wrapper.
Click → openMobilePanel()
════════════════════════════════════════════════════════════ -->
<button class="mobile-quote-pill" onclick="openMobilePanel()" aria-label="View quote summary">
<div class="mobile-pill-icon">
<span class="fa-icon fa-icon-file-invoice fa-icon--white" style="--icon-size:14px;"></span>
</div>
<div>
<div class="mobile-pill-mrr" id="mobilePillMrr"></div>
<div class="mobile-pill-label">MRR</div>
</div>
</button>
<!-- ════════════════════════════════════════════════════════════
MOBILE QUOTE PANEL — full-screen bottom sheet overlay
z-index:300 (above everything). Hidden by default (opacity:0,
pointer-events:none). .open class triggers slide-up animation.
STRUCTURE:
.mobile-panel-backdrop — dark blur overlay, click to close
.mobile-panel-sheet — slides up from bottom, max-height:92vh
.mobile-panel-handle — decorative drag indicator bar
.mobile-panel-close-row — "QUOTE SUMMARY" label + × button
#mobilePanelContent — receives a JS-cloned sidebar with _m IDs
MOBILE SIDEBAR: Built from the desktop sidebar on boot.
update() syncs the cloned _m elements after each render.
Never DOM-move the real .sidebar here — it breaks desktop.
════════════════════════════════════════════════════════════ -->
<div class="mobile-quote-panel" id="mobileQuotePanel">
<div class="mobile-panel-backdrop" onclick="closeMobilePanel()"></div>
<div class="mobile-panel-sheet" id="mobilePanelSheet">
<div class="mobile-panel-handle"></div>
<div class="mobile-panel-close-row">
<span class="mobile-panel-close-title">Quote Summary</span>
<button class="mobile-panel-close-btn" onclick="closeMobilePanel()" aria-label="Close">&times;</button>
</div>
<div class="mobile-panel-actions">
<button class="btn-export" onclick="printInvoice()">
<span class="fa-icon fa-icon-print" style="--icon-size:14px;margin-right:7px;"></span>
Print / Save PDF
</button>
<button class="btn-export btn-export-secondary" onclick="exportQuoteJSON()">
<span class="fa-icon fa-icon-file-code" style="--icon-size:14px;margin-right:7px;"></span>
Export JSON
</button>
</div>
<!-- Sidebar content injected by JS from the desktop sidebar markup -->
<div id="mobilePanelContent"></div>
</div>
</div>
<!-- TOP BAR -->
<!-- ── TOP BAR ── sticky, z-index:100, cream bg (#ddd8d0) ──────── -->
<header class="top-bar">
<div class="top-bar-inner">
<svg class="top-bar-logo" height="32" viewBox="0 0 424.27 97.38" xmlns="http://www.w3.org/2000/svg">
<polyline points="7.32 8.88 62.11 8.88 34.72 58.22" fill="#1f75a6"/>
<polyline points="40.7 55.33 64.4 12.64 71.88 12.64 44.48 61.99 40.7 55.33" fill="#8d252f"/>
<path d="M110.03,89.91c-5.99,0-11.27-.66-15.86-1.99-4.59-1.33-8.6-3.2-12.05-5.63-3.45-2.42-6.54-5.27-9.27-8.53l14.21-15.92c3.79,4.85,7.75,8.05,11.88,9.61,4.13,1.55,8.01,2.33,11.65,2.33,1.44,0,2.73-.13,3.87-.4,1.14-.26,2.01-.7,2.62-1.31.61-.61.91-1.44.91-2.5,0-.98-.32-1.82-.97-2.5-.65-.68-1.5-1.27-2.56-1.76-1.06-.49-2.22-.91-3.47-1.25-1.25-.34-2.46-.62-3.64-.85-1.18-.23-2.22-.46-3.13-.68-4.55-1.06-8.53-2.35-11.94-3.87-3.41-1.51-6.25-3.33-8.53-5.46-2.27-2.12-3.96-4.55-5.06-7.28-1.1-2.73-1.65-5.8-1.65-9.21,0-3.87.89-7.39,2.67-10.57,1.78-3.18,4.17-5.91,7.16-8.19,2.99-2.27,6.4-4.02,10.23-5.23,3.83-1.21,7.79-1.82,11.88-1.82,5.99,0,10.99.55,15.01,1.65,4.02,1.1,7.39,2.67,10.12,4.72,2.73,2.05,5.08,4.43,7.05,7.16l-14.32,13.76c-1.67-1.59-3.41-2.9-5.23-3.92-1.82-1.02-3.7-1.78-5.63-2.27-1.93-.49-3.85-.74-5.74-.74-1.74,0-3.22.13-4.43.4-1.21.27-2.16.68-2.84,1.25-.68.57-1.02,1.35-1.02,2.33s.43,1.8,1.31,2.44c.87.64,1.97,1.19,3.3,1.65,1.32.46,2.65.82,3.98,1.08,1.33.27,2.44.47,3.35.62,4.17.76,8,1.8,11.48,3.13,3.49,1.33,6.54,3,9.15,5,2.61,2.01,4.62,4.51,6.03,7.5,1.4,3,2.1,6.54,2.1,10.63,0,5.84-1.46,10.73-4.38,14.67-2.92,3.94-6.84,6.92-11.77,8.92-4.93,2.01-10.42,3.01-16.48,3.01Z" fill="#0c0c0c"/>
<path d="M174.27,88.77l-31.72-79.58h24.56l12.73,34.79c.91,2.35,1.65,4.36,2.22,6.03.57,1.67,1.08,3.24,1.54,4.72.46,1.48.89,3.05,1.31,4.72.42,1.67.89,3.71,1.42,6.14h-3.98c.76-3.18,1.42-5.8,1.99-7.84.57-2.05,1.21-4.07,1.93-6.08.72-2.01,1.65-4.56,2.79-7.67l12.73-34.79h23.76l-31.95,79.58h-19.33Z" fill="#0c0c0c"/>
<path d="M257.94,89.91c-5.99,0-11.27-.66-15.86-1.99-4.59-1.33-8.6-3.2-12.05-5.63-3.45-2.42-6.54-5.27-9.27-8.53l14.21-15.92c3.79,4.85,7.75,8.05,11.88,9.61,4.13,1.55,8.02,2.33,11.65,2.33,1.44,0,2.73-.13,3.87-.4,1.14-.26,2.01-.7,2.62-1.31.61-.61.91-1.44.91-2.5,0-.98-.32-1.82-.97-2.5-.64-.68-1.5-1.27-2.56-1.76-1.06-.49-2.22-.91-3.47-1.25-1.25-.34-2.46-.62-3.64-.85-1.18-.23-2.22-.46-3.13-.68-4.55-1.06-8.53-2.35-11.94-3.87-3.41-1.51-6.25-3.33-8.53-5.46-2.27-2.12-3.96-4.55-5.06-7.28-1.1-2.73-1.65-5.8-1.65-9.21,0-3.87.89-7.39,2.67-10.57,1.78-3.18,4.17-5.91,7.16-8.19,2.99-2.27,6.4-4.02,10.23-5.23,3.83-1.21,7.79-1.82,11.88-1.82,5.99,0,10.99.55,15.01,1.65,4.02,1.1,7.39,2.67,10.12,4.72,2.73,2.05,5.08,4.43,7.05,7.16l-14.33,13.76c-1.67-1.59-3.41-2.9-5.23-3.92-1.82-1.02-3.69-1.78-5.63-2.27-1.93-.49-3.85-.74-5.74-.74-1.74,0-3.22.13-4.43.4-1.21.27-2.16.68-2.84,1.25-.68.57-1.02,1.35-1.02,2.33s.44,1.8,1.31,2.44c.87.64,1.97,1.19,3.3,1.65,1.33.46,2.65.82,3.98,1.08,1.33.27,2.44.47,3.35.62,4.17.76,8,1.8,11.48,3.13,3.49,1.33,6.54,3,9.15,5,2.62,2.01,4.62,4.51,6.03,7.5,1.4,3,2.1,6.54,2.1,10.63,0,5.84-1.46,10.73-4.38,14.67-2.92,3.94-6.84,6.92-11.77,8.92-4.93,2.01-10.42,3.01-16.48,3.01Z" fill="#0c0c0c"/>
<path d="M300.67,47.36V8.02h10.45l14.33,23.33-8.49-.06,14.5-23.27h10.12v39.35h-11.69v-9.39c0-3.37.08-6.41.25-9.11.17-2.7.46-5.38.87-8.04l1.35,3.54-9.5,14.73h-3.71l-9.33-14.73,1.41-3.54c.41,2.51.7,5.09.87,7.73.17,2.64.25,5.78.25,9.42v9.39h-11.69Z" fill="#0c0c0c"/>
<path d="M364.36,47.93c-2.96,0-5.57-.33-7.84-.98-2.27-.66-4.25-1.58-5.96-2.78-1.71-1.2-3.23-2.6-4.58-4.22l7.03-7.87c1.87,2.4,3.83,3.98,5.87,4.75,2.04.77,3.96,1.15,5.76,1.15.71,0,1.35-.07,1.91-.2.56-.13.99-.35,1.29-.65.3-.3.45-.71.45-1.24,0-.49-.16-.9-.48-1.24s-.74-.63-1.26-.87c-.53-.24-1.1-.45-1.71-.62-.62-.17-1.22-.31-1.8-.42-.58-.11-1.1-.22-1.55-.34-2.25-.52-4.22-1.16-5.9-1.91-1.69-.75-3.09-1.65-4.22-2.7-1.12-1.05-1.96-2.25-2.5-3.6-.54-1.35-.81-2.87-.81-4.55,0-1.91.44-3.65,1.32-5.23.88-1.57,2.06-2.92,3.54-4.05s3.17-1.99,5.06-2.58,3.85-.9,5.87-.9c2.96,0,5.43.27,7.42.81,1.99.54,3.65,1.32,5,2.33,1.35,1.01,2.51,2.19,3.49,3.54l-7.08,6.8c-.83-.79-1.69-1.43-2.59-1.94s-1.83-.88-2.78-1.12c-.96-.24-1.9-.37-2.84-.37-.86,0-1.59.07-2.19.2-.6.13-1.07.34-1.41.62-.34.28-.51.67-.51,1.15s.21.89.65,1.21c.43.32.97.59,1.63.82.66.22,1.31.4,1.97.53.66.13,1.21.23,1.66.31,2.06.38,3.95.89,5.68,1.55,1.72.66,3.23,1.48,4.53,2.47s2.29,2.23,2.98,3.71c.69,1.48,1.04,3.23,1.04,5.26,0,2.89-.72,5.3-2.16,7.25-1.44,1.95-3.38,3.42-5.82,4.41-2.44.99-5.15,1.49-8.15,1.49Z" fill="#0c0c0c"/>
<path d="M386.61,47.36V8.02h17.71c2.7,0,5.1.58,7.2,1.74,2.1,1.16,3.75,2.75,4.95,4.78,1.2,2.02,1.8,4.35,1.8,6.97s-.6,5.17-1.8,7.31c-1.2,2.14-2.85,3.81-4.95,5.03-2.1,1.22-4.5,1.83-7.2,1.83h-5.56v11.69h-12.14ZM398.53,25.33h3.54c.71,0,1.35-.14,1.91-.42.56-.28,1-.68,1.32-1.21.32-.52.48-1.18.48-1.97s-.16-1.42-.48-1.91c-.32-.49-.76-.85-1.32-1.1-.56-.24-1.2-.37-1.91-.37h-3.54v6.97Z" fill="#0c0c0c"/>
</svg>
<div class="top-bar-right">
<span id="quoteRef">SVS-00000000-0000</span><br>
<span id="headerDate"></span>
</div>
<button id="themeToggle" class="theme-toggle-btn" onclick="toggleTheme()" aria-label="Toggle light/dark theme" title="Switch to light theme">
<span id="themeToggleIcon"><span class="fa-icon fa-icon-moon-stars fa-icon--theme" style="--icon-size:15px;"></span></span>
</button>
</div>
</header>
<!-- ── MAIN LAYOUT GRID: 3fr main-col | 2fr side-col ────────────── -->
<div class="outer">
<!-- ── LEFT COLUMN: sections IVI + client bar ──────────────────── -->
<div class="main-col">
<!-- CLIENT BAR -->
<div class="client-bar">
<div class="client-label">Prepared for</div>
<input class="client-input" id="clientName" type="text" placeholder="Client Name" oninput="update()">
<div class="client-rep-row">
<div class="client-label">Prepared by</div>
<input class="client-input client-input--rep" id="repName" type="text" placeholder="Your Name" oninput="debouncedSave()">
</div>
</div>
<div class="sections-toolbar">
<button class="btn-toggle-all" id="toggleAllBtn" onclick="toggleAllSections()">
<span class="toggle-all-collapse-icon" style="display:none;"><span class="fa-icon fa-icon-angles-up" style="--icon-size:11px;margin-right:5px;"></span></span>
<span class="toggle-all-expand-icon"><span class="fa-icon fa-icon-angles-down" style="--icon-size:11px;margin-right:5px;"></span></span>
<span class="toggle-all-label">Expand All</span>
</button>
</div>
<!-- ── QUOTE SETTINGS BAR ────────────────────────────────────────
Contract term toggle (3-way) and onboarding fee.
Contract term drives discountPct in calcQuote().
HST toggle shows/hides the +13% line in the sidebar.
One-time fee is included in JSON export but NOT in MRR.
──────────────────────────────────────────────────────────────── -->
<div class="quote-settings-bar">
<div class="qs-group">
<div class="qs-label-row">
<div class="qs-label">Contract Term &amp; Incentives</div>
</div>
<div class="tier-seg-wrap qs-term-wrap">
<input type="radio" name="contractTerm" id="termM2m" value="m2m" checked onchange="update()">
<label for="termM2m" class="tier-seg" id="seg-term-m2m">
<div class="tier-name">Month-to-Month</div>
<div class="tier-sub">Maximum flexibility</div>
</label>
<input type="radio" name="contractTerm" id="term12mo" value="12mo" onchange="update()">
<label for="term12mo" class="tier-seg" id="seg-term-12mo">
<div class="tier-name">12-Month</div>
<div class="tier-sub qs-discount-sub">3% off MRR + 50% off onboarding</div>
</label>
<input type="radio" name="contractTerm" id="term24mo" value="24mo" onchange="update()">
<label for="term24mo" class="tier-seg" id="seg-term-24mo">
<div class="tier-name">24-Month <span class="qs-best-badge">Best Value</span></div>
<div class="tier-sub qs-discount-sub">5% off MRR + complimentary onboarding</div>
</label>
</div>
<div class="qs-savings-stack">
<div class="qs-savings-row hidden" id="qsSavingsDisplay">
<span class="fa-icon fa-icon-square-check" style="--icon-size:15px;"></span>
<span id="qsSavingsCopy">Saving $0/mo vs. month-to-month</span>
</div>
<div class="qs-savings-row hidden" id="qsFirstYearDisplay">
<span class="fa-icon fa-icon-square-check" style="--icon-size:15px;"></span>
<span id="qsFirstYearCopy">Year-one value unlocked: $0 from term savings and complimentary onboarding</span>
</div>
</div>
</div>
<div class="qs-divider"></div>
<div class="qs-right">
<div class="qs-fee-row">
<div class="qs-fee-header">
<label class="qs-fee-label" for="oneTimeFee">Onboarding / Implementation</label>
</div>
<div class="qs-fee-input-wrap">
<span class="qs-fee-dollar">$</span>
<input type="number" id="oneTimeFee" class="qs-fee-input" min="0" placeholder="auto" oninput="this.dataset.manual='1'; update();">
</div>
<label class="qs-toggle-row qs-fee-waive">
<input type="checkbox" id="onboardingWaived" onchange="onWaiveToggle();">
<span class="qs-switch"></span>
<span class="qs-toggle-label">Waive</span>
</label>
</div>
</div>
</div>
<div class="group-label">Managed IT Services <span class="group-label-sections">(Sections I, II, III)</span></div>
<!-- ────────────────────────────────────────────────────────────
SECTION I — SITE ADMIN FEE
id="sec-01" starts collapsed
#adminFeeDisplay — rendered by update() → fmt(adminFeeNet)
#floorBar — progress bar width%, turns green at 100%
#floorProgress — "$X / $650" text label
#floorNote — threshold status message
#fb-base — siteAdminBase value
#fb-zt-row — hidden unless ztActive
#fb-pwm-row — hidden unless addPWM
#fb-total — adminFeeNet total
#sec01-summary — badge shown when section collapsed
──────────────────────────────────────────────────────────────── -->
<div class="section" id="sec-01">
<div class="section-header section-toggle" onclick="toggleSection('sec-01')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-01');event.preventDefault();}">
<div class="section-num">III</div>
<div class="section-title-block">
<div class="section-title">Site Management</div>
</div>
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
<div class="sec-controls-row">
<span class="section-badge">Operational Oversight</span>
<span id="sec01-summary" class="sec-summary-badge"></span>
</div>
</div>
<!-- Always-visible threshold bar (outside section-body so it shows when collapsed) -->
<div class="progress-wrap sec-01-threshold">
<div class="progress-label">
<span>Managed Service Threshold</span>
</div>
<div class="progress-track">
<div class="progress-fill" id="floorBar" style="width:0%"></div>
</div>
</div>
<div id="floorNote" class="floor-note sec-01-threshold"></div>
<div class="section-body" id="sec-01-body" style="display:none;">
<div class="section-content">
<div class="section-subtitle">Ongoing oversight for your environment, vendors, and IT documentation so everything stays coordinated, accountable, and easier to support.</div>
<div class="admin-fee-header">
<span class="admin-fee-title">Site Admin Fee</span>
<span id="adminFeeDisplay" class="admin-fee-val">$150/mo</span>
<label class="qs-toggle-row admin-fee-waive-toggle">
<input type="checkbox" id="adminWaived" onchange="update()">
<span class="qs-switch"></span>
<span class="qs-toggle-label">Waive</span>
</label>
</div>
<table class="fee-table" id="feeBreakdown">
<tr><td>Base Site Admin</td><td id="fb-base"></td></tr>
<tr id="fb-zt-row" class="hidden"><td>Zero Trust Supplement</td><td id="fb-zt">+$250</td></tr>
<tr id="fb-pwm-row" class="hidden"><td>1Password Admin (10%)</td><td id="fb-pwm"></td></tr>
<tr class="fee-total"><td>Total Admin Fee</td><td id="fb-total"></td></tr>
</table>
<div class="admin-waive-savings hidden" id="adminWaivedSavings">
<span class="fa-icon fa-icon-square-check" style="--icon-size:11px;"></span>
Site oversight fee waived on this quote — included by SVS on this proposal, saving <span id="adminWaivedAmt">$0</span>/mo
</div>
<!-- What's Covered collapsible -->
<div class="collapsible-header collapsible-header--mt16" onclick="toggleCollapsible('adminCovered')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('adminCovered');event.preventDefault();}">
<span class="collapsible-toggle" id="adminCovered-icon"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:12px;"></span></span>
<span class="collapsible-label">What This Fee Covers Behind the Scenes</span>
</div>
<div class="collapsible-body" id="adminCovered">
<div class="feature-card-grid">
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-users-gear fa-icon--accent" style="--icon-size:16px;margin-right:8px;"></span> Tenant &amp; Identity Management</div><div class="feature-card-desc">Microsoft 365 / Entra ID tenant administration, user lifecycle, MFA enforcement, and conditional access policies.</div></div>
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-network-wired fa-icon--accent" style="--icon-size:16px;margin-right:8px;"></span> Network &amp; Infrastructure Oversight</div><div class="feature-card-desc">Firewall configuration reviews, DNS management, VLAN segmentation oversight, and network performance monitoring.</div></div>
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-book-open-cover fa-icon--accent" style="--icon-size:15px;margin-right:8px;"></span> Documentation &amp; Runbooks</div><div class="feature-card-desc">Living IT documentation, network diagrams, asset registers, and runbooks updated continuously in your client portal.</div></div>
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-handshake fa-icon--accent" style="--icon-size:16px;margin-right:8px;"></span> Vendor Management</div><div class="feature-card-desc">Single point of contact for all technology vendors — ISPs, software, hardware, and cloud providers.</div></div>
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-shield-check fa-icon--accent" style="--icon-size:15px;margin-right:8px;"></span> Security Posture Management</div><div class="feature-card-desc">Monthly security reviews, vulnerability scan oversight, patch compliance reporting, and security baseline enforcement.</div></div>
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-chart-line-up fa-icon--accent" style="--icon-size:16px;margin-right:8px;"></span> Reporting &amp; QBRs</div><div class="feature-card-desc">Monthly infrastructure health reports, ticket trend analysis, and quarterly business reviews with your account manager.</div></div>
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-cloud fa-icon--accent" style="--icon-size:18px;margin-right:8px;"></span> Cloud Governance</div><div class="feature-card-desc">License optimization, cloud spend visibility, Microsoft Secure Score improvement, and policy compliance alignment.</div></div>
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-triangle-exclamation fa-icon--accent" style="--icon-size:16px;margin-right:8px;"></span> Incident Coordination</div><div class="feature-card-desc">Major incident management, root cause analysis, and coordinated response across all your managed services.</div></div>
<div class="feature-card"><div class="feature-card-title"><span class="fa-icon fa-icon-hard-drive fa-icon--accent" style="--icon-size:18px;margin-right:8px;"></span> Backup Monitoring</div><div class="feature-card-desc">Daily backup job verification, restore testing schedules, retention compliance checks, and failure alerting — separate from the Bare Metal Backup add-on.</div></div>
</div>
</div>
</div>
</div>
</div>
<!-- ────────────────────────────────────────────────────────────
SECTION II — USER PACKAGE
#rateM365 / #rateBYOL — radio inputs, read by calcQuote()
#byolCalloutGreen — shown when M365 selected (byol=false)
#byolCalloutRed — shown when BYOL selected (byol=true)
#byolRedSavings — dynamic savings $ inside red callout
#userCount — number input → users in calcQuote()
Add-ons: #addExtHours #addPWM #addINKY #addZT (checkboxes)
Row highlights: #row-ext #row-pwm #row-inky #row-zt
#sec02-summary — badge shown when collapsed
──────────────────────────────────────────────────────────────── -->
<div class="section" id="sec-02">
<div class="section-header section-toggle" onclick="toggleSection('sec-02')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-02');event.preventDefault();}">
<div class="section-num">I</div>
<div class="section-title-block">
<div class="section-title">User Package</div>
</div>
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
<div class="sec-controls-row" onclick="event.stopPropagation()">
<div class="num-stepper">
<button class="step-btn" aria-label="Decrease users" onclick="stepInput('userCount',-1)">&minus;</button>
<input class="num-input" id="userCount" type="number" min="0" value="1" oninput="update()">
<button class="step-btn" aria-label="Increase users" onclick="stepInput('userCount',1)">+</button>
</div>
<span class="section-badge">Managed Users</span>
<span id="sec02-summary" class="sec-summary-badge"></span>
</div>
<div class="section-subtitle">Per-user managed productivity, identity, security, and helpdesk with one clear recommended path for most clients.</div>
</div>
<div class="section-body" id="sec-02-body" style="display:none;">
<div class="section-content">
<!-- Base Rate Toggle -->
<div class="pill-toggle">
<input type="radio" name="baseRate" id="rateM365" value="m365" checked onchange="update()">
<label for="rateM365">
<span class="pill-price">
<span id="m365PriceM2m">$140</span><span id="m365PriceAnnual" class="hidden">$130</span><small>/user/mo</small>
</span>
<span class="pill-desc">M365 Included — Recommended for most clients: licensing, identity, email &amp; business protection in one seat</span>
<span class="pill-savings" id="m365SavingsLine"></span>
</label>
<input type="radio" name="baseRate" id="rateBYOL" value="byol" onchange="update()">
<label for="rateBYOL">
<span class="pill-price">$110<small>/user/mo</small></span>
<span class="pill-desc">BYOL — Best when the client already has a licensing standard they need to keep (M365 or Google Workspace)</span>
</label>
</div>
<div id="byolCalloutGreen" class="callout-green">
<span class="fa-icon fa-icon-square-check fa-icon--green" style="--icon-size:14px;margin-top:2px;"></span><span id="m365CalloutText">Recommended bundle — M365 Business Premium is built into this seat and can save up to $11/user/mo versus buying it retail on its own</span>
</div>
<div id="byolCalloutRed" class="callout-red hidden">
<span class="fa-icon fa-icon-triangle-exclamation fa-icon--amber" style="--icon-size:14px;margin-top:2px;"></span><span>Client-owned licensing selected — switching to the bundled M365 seat would recover up to <span id="byolRedSavings">$150</span>/mo in retail-equivalent spend</span>
</div>
<div class="m365-app-strip" id="userBundleStrip" aria-label="Microsoft 365 apps included with the bundled seat">
<div class="m365-app-list">
<div class="m365-app-item">
<img src="M365icons/word.svg" alt="Microsoft Word" class="m365-app-icon">
<span class="m365-app-name">Word</span>
</div>
<div class="m365-app-item">
<img src="M365icons/excel.svg" alt="Microsoft Excel" class="m365-app-icon">
<span class="m365-app-name">Excel</span>
</div>
<div class="m365-app-item">
<img src="M365icons/powerpoint.svg" alt="Microsoft PowerPoint" class="m365-app-icon">
<span class="m365-app-name">PowerPoint</span>
</div>
<div class="m365-app-item">
<img src="M365icons/outlook-svgrepo-com.svg" alt="Microsoft Outlook" class="m365-app-icon">
<span class="m365-app-name">Outlook</span>
</div>
<div class="m365-app-item">
<img src="M365icons/teams.svg" alt="Microsoft Teams" class="m365-app-icon">
<span class="m365-app-name">Teams</span>
</div>
<div class="m365-app-item">
<img src="M365icons/azure-svgrepo-com.svg" alt="Microsoft Azure" class="m365-app-icon">
<span class="m365-app-name">Azure AD</span>
</div>
</div>
<div class="m365-app-strip-note">
<span class="m365-app-note-default">Included with the bundled Microsoft 365 seat</span>
<span class="m365-app-note-byol">Not included with BYOL</span>
</div>
</div>
<!-- What's Included collapsible -->
<div class="collapsible-header" onclick="toggleCollapsible('userIncluded')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('userIncluded');event.preventDefault();}">
<span class="collapsible-toggle" id="userIncluded-icon"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:12px;"></span></span>
<span class="collapsible-label">What's Included in This Package</span>
</div>
<div class="collapsible-body" id="userIncluded">
<ul class="feature-list">
<li class="m365-feature">Microsoft 365 Business Premium</li>
<li class="m365-feature">Word, Excel, PowerPoint, Teams, Exchange</li>
<li class="m365-feature">Entra ID / conditional access / SSO</li>
<li class="m365-feature">Defender for Business</li>
<li class="m365-feature">MFA enforcement</li>
<li class="m365-feature">SaaS alerting / geo-location lock</li>
<li>CORK cyber warranty with $100,000 annual coverage</li>
<li>INKY advanced mail protection</li>
<li>Security awareness training and phishing simulation</li>
<li>SaaS backup for business email, OneDrive, and Google Workspace</li>
<li>Dark web ID scanning</li>
<li>SOC account monitoring</li>
<li>Unlimited remote bilingual help desk support (9am-5pm / Mon-Fri)</li>
</ul>
</div>
<!-- Per-User Add-Ons collapsible -->
<div class="collapsible-header collapsible-header--addon" onclick="toggleCollapsible('addonsA')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('addonsA');event.preventDefault();}">
<span class="collapsible-toggle" id="addonsA-icon"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:12px;"></span></span>
<span class="collapsible-label">WORKFORCE ADD-ONS</span>
<div id="addonsA-preview" class="addon-preview-wrap">
<span class="addon-preview-pill" data-addon="addExtHours">Extended Hours</span>
<span class="addon-preview-pill" data-addon="addPWM">1Password</span>
<span class="addon-preview-pill" data-addon="addINKY">INKY Pro Upgrade</span>
<span class="addon-preview-pill" data-addon="addZT">Zero Trust</span>
</div>
</div>
<div class="collapsible-body" id="addonsA">
<div class="addon-grid">
<label class="addon-row" id="row-ext" onclick="toggleAddon('addExtHours','row-ext');update()">
<input type="checkbox" id="addExtHours">
<div><div class="addon-name">Extended Help Desk Hours</div><div class="addon-desc">8am8pm MonFri coverage via staggered shifts</div></div>
<span class="addon-price" data-price-key="ADDON_EXT_HOURS" data-price-unit="user">+$25/user/mo</span>
</label>
<label class="addon-row" id="row-pwm" onclick="toggleAddon('addPWM','row-pwm');update()">
<input type="checkbox" id="addPWM">
<div><div class="addon-name">1Password Management</div><div class="addon-desc">Business password vault, admin console &amp; SSO integration</div></div>
<span class="addon-price" data-price-key="ADDON_1PASSWORD" data-price-unit="user">+$9/user/mo</span>
</label>
<label class="addon-row" id="row-inky" onclick="toggleAddon('addINKY','row-inky');update()">
<input type="checkbox" id="addINKY">
<div><div class="addon-name">INKY Pro Upgrade</div><div class="addon-desc">Adds enhanced phishing defense and email security controls on top of the included INKY mail protection</div></div>
<span class="addon-price" data-price-key="ADDON_INKY" data-price-unit="user">+$8/user/mo</span>
</label>
<label class="addon-row" id="row-zt" onclick="toggleAddon('addZT','row-zt');update()">
<input type="checkbox" id="addZT">
<div><div class="addon-name">Zero Trust User Seat</div><div class="addon-desc">Cytracom ZT identity-aware access — deny by default network control</div></div>
<span class="addon-price" data-price-key="ADDON_ZERO_TRUST_USER" data-price-unit="user">+$55/user/mo</span>
</label>
</div>
</div>
</div>
</div>
</div>
<!-- ────────────────────────────────────────────────────────────
SECTION III — ENDPOINT PACKAGE
#endpointCount — number input → endpoints in calcQuote()
Add-ons: #addBMB (+$25/endpoint) #addUSB (+$4/endpoint)
Row highlights: #row-bmb #row-usb
#sec03-summary — badge shown when collapsed
──────────────────────────────────────────────────────────────── -->
<div class="section" id="sec-03">
<div class="section-header section-toggle" onclick="toggleSection('sec-03')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-03');event.preventDefault();}">
<div class="section-num">II</div>
<div class="section-title-block">
<div class="section-title">Endpoint Package</div>
</div>
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
<div class="sec-controls-row" onclick="event.stopPropagation()">
<div class="num-stepper">
<button class="step-btn" aria-label="Decrease endpoints" onclick="stepInput('endpointCount',-1)">&minus;</button>
<input class="num-input" id="endpointCount" type="number" min="0" value="1" oninput="update()">
<button class="step-btn" aria-label="Increase endpoints" onclick="stepInput('endpointCount',1)">+</button>
</div>
<span class="section-badge">Protected Endpoints</span>
<span id="sec03-summary" class="sec-summary-badge"></span>
</div>
<div class="section-subtitle">Per-device managed protection built to reduce downtime and recovery risk across workstations and laptops.</div>
</div>
<div class="section-body" id="sec-03-body" style="display:none;">
<div class="section-content">
<!-- What's Included collapsible -->
<div class="collapsible-header" onclick="toggleCollapsible('endpointIncluded')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('endpointIncluded');event.preventDefault();}">
<span class="collapsible-toggle" id="endpointIncluded-icon"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:12px;"></span></span>
<span class="collapsible-label">What's Included in This Package</span>
</div>
<div class="collapsible-body" id="endpointIncluded">
<ul class="feature-list">
<li>Managed EDR threat protection</li>
<li>1 full workstation backup included</li>
<li>24/7 SOC monitoring and response</li>
<li>Encryption enforcement</li>
<li>RMM monitoring and maintenance</li>
<li>Firmware, updates, and patch management</li>
<li>Admin password rotation</li>
<li>Application ringfencing</li>
</ul>
</div>
<!-- Per-Endpoint Add-Ons collapsible -->
<div class="collapsible-header collapsible-header--addon" onclick="toggleCollapsible('addonsB')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('addonsB');event.preventDefault();}">
<span class="collapsible-toggle" id="addonsB-icon"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:12px;"></span></span>
<span class="collapsible-label">ENDPOINT BUSINESSGUARD ADD-ONS</span>
<div id="addonsB-preview" class="addon-preview-wrap">
<span class="addon-preview-pill" data-addon="addBMB">Bare Metal Backup</span>
<span class="addon-preview-pill" data-addon="addUSB">USB Blocking</span>
</div>
</div>
<div class="collapsible-body" id="addonsB">
<div class="addon-grid">
<label class="addon-row" id="row-bmb" onclick="toggleAddon('addBMB','row-bmb');update()">
<input type="checkbox" id="addBMB">
<div><div class="addon-name">Bare Metal Backup</div><div class="addon-desc">Image-based backup with bare metal restore — fast full-device recovery with local &amp; cloud retention</div></div>
<span class="addon-price" data-price-key="ADDON_BARE_METAL_BACKUP" data-price-unit="endpoint">+$25/endpoint/mo</span>
</label>
<label class="addon-row" id="row-usb" onclick="toggleAddon('addUSB','row-usb');update()">
<input type="checkbox" id="addUSB">
<div><div class="addon-name">USB Device Blocking</div><div class="addon-desc">Policy-enforced USB control to reduce malware and data-loss exposure from removable media</div></div>
<span class="addon-price" data-price-key="ADDON_USB_BLOCKING" data-price-unit="endpoint">+$4/endpoint/mo</span>
</label>
</div>
</div>
</div>
</div>
</div>
<hr class="group-divider">
<!-- ────────────────────────────────────────────────────────────
SECTION IV — SERVER MANAGEMENT
Starts COLLAPSED (no sec-open class, body display:none)
#serverCount — number input → servers in calcQuote()
Rate: $120/server/mo (serverBase = servers * 120)
Counted in baseSubtotal → affects admin fee threshold
#sec04-summary — badge shown when collapsed
──────────────────────────────────────────────────────────────── -->
<div class="section" id="sec-04">
<div class="section-header section-toggle" onclick="toggleSection('sec-04')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-04');event.preventDefault();}">
<div class="section-num">IV</div>
<div class="section-title-block">
<div class="section-title">Server Management</div>
</div>
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
<div class="sec-controls-row" onclick="event.stopPropagation()">
<div class="num-stepper">
<button class="step-btn" aria-label="Decrease servers" onclick="stepInput('serverCount',-1)">&minus;</button>
<input class="num-input" id="serverCount" type="number" min="0" value="0" oninput="update()">
<button class="step-btn" aria-label="Increase servers" onclick="stepInput('serverCount',1)">+</button>
</div>
<span class="section-badge">Managed Servers</span>
<span id="sec04-summary" class="sec-summary-badge"></span>
</div>
<div class="section-subtitle">Dedicated management for physical &amp; virtual servers</div>
</div>
<div class="section-body" id="sec-04-body" style="display:none;">
<div class="section-content">
<div class="collapsible-header" onclick="toggleCollapsible('serverIncluded')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('serverIncluded');event.preventDefault();}">
<span class="collapsible-toggle" id="serverIncluded-icon"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:12px;"></span></span>
<span class="collapsible-label">What's Included in Server Management</span>
</div>
<div class="collapsible-body" id="serverIncluded">
<ul class="feature-list">
<li>RMM monitoring — CPU, RAM, disk, services &amp; event log alerting</li>
<li>OS patch management — Windows Server updates, tested &amp; scheduled</li>
<li>Role &amp; service management — AD, DNS, DHCP, file shares, print services</li>
<li>Performance baselines — capacity planning &amp; proactive alerting</li>
<li>Backup monitoring — verify, test restores &amp; retention compliance</li>
<li>Security hardening — CIS benchmarks, local admin control, audit logging</li>
</ul>
</div>
</div>
</div>
</div>
<!-- ────────────────────────────────────────────────────────────
SECTION V — ZERO TRUST NETWORKING HaaS
Starts COLLAPSED. Two independent inputs:
#ztNetSeats — ZT user seats @ $25/seat/mo (ztNetSeats)
#ztNetRouters — HaaS router devices @ $100/device/mo (ztNetRouters)
ztActive = addZT (Sec II checkbox) OR ztSeats > 0
When ztActive → adds ADMIN_FEE_ZT ($250) to admin fee
ztNetTotal added to MRR but NOT to baseSubtotal
#sec05-summary — badge shown when collapsed
──────────────────────────────────────────────────────────────── -->
<div class="section" id="sec-05">
<div class="section-header section-toggle" onclick="toggleSection('sec-05')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-05');event.preventDefault();}">
<div class="section-num">V</div>
<div class="section-title-block">
<div class="section-title">Zero Trust Networking <span class="section-title-tag">HaaS</span></div>
</div>
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
<div class="sec-controls-row" onclick="event.stopPropagation()">
<div class="num-stepper">
<button class="step-btn" aria-label="Decrease ZT seats" onclick="stepInput('ztNetSeats',-1)">&minus;</button>
<input class="num-input" id="ztNetSeats" type="number" min="0" value="0" oninput="update()">
<button class="step-btn" aria-label="Increase ZT seats" onclick="stepInput('ztNetSeats',1)">+</button>
</div>
<span class="section-badge">ZT Seats</span>
<span id="sec05-summary" class="sec-summary-badge"></span>
</div>
<div class="section-subtitle">Cytracom-powered ZT network access — seats &amp; managed hardware as a service</div>
</div>
<div class="section-body" id="sec-05-body" style="display:none;">
<div class="section-content">
<!-- ZT relationship explainer -->
<div class="callout-green" style="margin-bottom:20px;">
<span class="fa-icon fa-icon-square-info fa-icon--accent" style="--icon-size:14px;margin-top:2px;"></span>
<span><strong>Section II vs Section V — what's the difference?</strong><br>
Section II's <em>Zero Trust User Seat</em> (+$55/user) adds the ZT software agent to each person's device — identity-aware access, deny-by-default policy, per-user.<br>
Section V adds <em>ZT network infrastructure</em>: <strong>seats</strong> cover non-user devices (printers, IoT, cameras) that need network access control, and <strong>routers</strong> are the managed ZTNA gateway hardware delivered as a service. Both can be active together.</span>
</div>
<div class="input-row">
<div>
<div class="input-label">5B — HaaS Devices</div>
<div class="input-sublabel">Managed router/firewall hardware · $100/device/mo</div>
</div>
<div class="num-stepper">
<button class="step-btn" aria-label="Decrease ZT routers" onclick="stepInput('ztNetRouters',-1)">&minus;</button>
<input class="num-input" id="ztNetRouters" type="number" min="0" value="0" oninput="update()">
<button class="step-btn" aria-label="Increase ZT routers" onclick="stepInput('ztNetRouters',1)">+</button>
</div>
</div>
</div>
</div>
</div>
<!-- ────────────────────────────────────────────────────────────
SECTION VI — VOIP / UCaaS
Starts COLLAPSED. Three-tier radio toggle:
#voipBasic/$28 #voipStandard/$35 #voipPremium/$45
.tier-seg.active set by activateTier() AND update()
#voipSeats — seat count input
#addVoipPhone — Desk Phone HaaS +$15/seat
#addVoipFax — eFax line +$10/seat/mo
#currentPhoneBill — optional savings comparator input
#savingsComparator — green/amber result, rendered by updateSavings()
voipTotal NOT counted in baseSubtotal (no effect on admin fee)
#sec06-summary — badge shown when collapsed
──────────────────────────────────────────────────────────────── -->
<div class="section" id="sec-06">
<div class="section-header section-toggle" onclick="toggleSection('sec-06')" aria-expanded="false" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-06');event.preventDefault();}">
<div class="section-num">VI</div>
<div class="section-title-block">
<div class="section-title">VoIP / Unified Communications <span class="section-title-tag">UCaaS</span></div>
</div>
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
<div class="sec-controls-row" onclick="event.stopPropagation()">
<div class="num-stepper">
<button class="step-btn" aria-label="Decrease VoIP seats" onclick="stepInput('voipSeats',-1)">&minus;</button>
<input class="num-input" id="voipSeats" type="number" min="0" value="0" oninput="update()">
<button class="step-btn" aria-label="Increase VoIP seats" onclick="stepInput('voipSeats',1)">+</button>
</div>
<span class="section-badge">VoIP Seats</span>
<span id="sec06-summary" class="sec-summary-badge"></span>
</div>
<div class="section-subtitle">United Cloud-powered business phone — seats, features &amp; optional desk phones</div>
</div>
<div class="section-body" id="sec-06-body" style="display:none;">
<div class="section-content">
<!-- Tier Selector -->
<div class="tier-seg-wrap">
<input type="radio" name="voipTier" id="voipBasic" value="basic" checked onchange="update()">
<label for="voipBasic" class="tier-seg" id="seg-basic" onclick="activateTier('basic')">
<div class="tier-name">Basic</div>
<div class="tier-price">$28</div>
<div class="tier-sub">/seat/mo</div>
</label>
<input type="radio" name="voipTier" id="voipStandard" value="standard" onchange="update()">
<label for="voipStandard" class="tier-seg" id="seg-standard" onclick="activateTier('standard')">
<div class="tier-name">Standard</div>
<div class="tier-price">$35</div>
<div class="tier-sub">/seat/mo</div>
</label>
<input type="radio" name="voipTier" id="voipPremium" value="premium" onchange="update()">
<label for="voipPremium" class="tier-seg" id="seg-premium" onclick="activateTier('premium')">
<div class="tier-name">Premium</div>
<div class="tier-price">$45</div>
<div class="tier-sub">/seat/mo</div>
</label>
</div>
<div class="addon-grid" style="margin-top:8px;">
<label class="addon-row" id="row-vphone" onclick="toggleAddon('addVoipPhone','row-vphone');update()">
<input type="checkbox" id="addVoipPhone">
<div><div class="addon-name">Desk Phone HaaS</div><div class="addon-desc">Managed physical desk phone hardware per seat</div></div>
<span class="addon-price" data-price-key="VOIP_PHONE_RATE" data-price-unit="seat">+$15/seat/mo</span>
</label>
<label class="addon-row" id="row-vfax" onclick="toggleAddon('addVoipFax','row-vfax');update()">
<input type="checkbox" id="addVoipFax">
<div><div class="addon-name">eFax Line</div><div class="addon-desc">Digital fax number — send &amp; receive via email or portal</div></div>
<span class="addon-price" data-price-key="VOIP_FAX_RATE" data-price-unit="line">+$10/line/mo</span>
</label>
</div>
<!-- Phone Bill Savings -->
<div class="savings-input-row">
<label for="currentPhoneBill">Current monthly phone bill (optional)</label>
<input type="number" id="currentPhoneBill" placeholder="$0" min="0"
oninput="update()" onchange="update()">
</div>
<div id="savingsPrompt" class="savings-prompt" style="display:none;">
Enter your current phone bill above to see potential savings.
</div>
<div id="savingsComparator" class="savings-result hidden"></div>
</div>
</div>
</div>
</div><!-- END main-col — sections IVI -->
<!-- SIDEBAR -->
<!-- ── RIGHT COLUMN: sticky sidebar (desktop only ≥1100px) ─────────
Hidden on mobile via CSS. Mobile clone is injected into #mobilePanelContent.
nudgeBanner must stay INSIDE .sidebar-body or it gets clipped.
──────────────────────────────────────────────────────────────── -->
<div class="side-col">
<div class="sidebar-focus-backdrop" id="sidebarFocusBackdrop" onclick="closeSidebarFocus()" aria-hidden="true"></div>
<div class="sidebar-utility">
<button class="btn-export" onclick="printInvoice()">
<span class="fa-icon fa-icon-print" style="--icon-size:14px;margin-right:7px;"></span>
Print / Save PDF
</button>
<button class="btn-export btn-export-secondary" id="btnExportJSON" onclick="exportQuoteJSON()">
<span class="fa-icon fa-icon-file-code" style="--icon-size:14px;margin-right:7px;"></span>
Export JSON
</button>
</div>
<div class="sidebar">
<div class="sidebar-header">
<div class="sidebar-header-row">
<div class="sidebar-title">SVS MSP — Live Quote</div>
<button
type="button"
class="sidebar-focus-print-btn"
onclick="printInvoice()"
aria-label="Print / Save PDF"
title="Print / Save PDF">
<span class="fa-icon fa-icon-print" style="--icon-size:14px;margin-right:6px;"></span>
Print
</button>
<button
type="button"
class="sidebar-focus-toggle"
id="sidebarFocusToggle"
onclick="toggleSidebarFocus()"
aria-label="Expand live quote"
aria-pressed="false"
title="Expand live quote">
<span class="sidebar-focus-icon sidebar-focus-icon-open">
<span class="fa-icon fa-icon-up-right-and-down-left-from-center" style="--icon-size:13px;"></span>
</span>
<span class="sidebar-focus-icon sidebar-focus-icon-close">
<span class="fa-icon fa-icon-down-left-and-up-right-to-center" style="--icon-size:13px;"></span>
</span>
</button>
</div>
</div>
<!-- ── INSIGHT NUDGE BANNER ──────────────────────────────────────
Sits flush under .sidebar-header, full width of .sidebar.
Controlled entirely by renderNudge() via applyNudge("").
Mobile clone target: #nudgeBanner_m (synced via syncClass/syncEl).
.hidden class toggled by renderNudge() when nudges=[]
──────────────────────────────────────────────────────────────── -->
<div id="nudgeBanner" class="nudge-banner amber hidden">
<div class="nudge-header-row">
<span class="nudge-banner-label"><span class="fa-icon fa-icon-lightbulb-on" style="--icon-size:15px;margin-right:6px;"></span> Insight <span id="nudgeCounter" class="nudge-counter"></span></span>
<div class="nudge-nav-group">
<button onclick="cycleNudge(-1)" class="nudge-nav-btn" title="Previous"><span class="fa-icon fa-icon-chevron-left"></span></button>
<button onclick="cycleNudge(1)" class="nudge-nav-btn" title="Next"><span class="fa-icon fa-icon-chevron-right"></span></button>
</div>
</div>
<span id="nudgeText"></span>
</div>
<div class="sidebar-body">
<div class="sidebar-focus-client" id="sidebarFocusClientWrap">
<span class="sidebar-focus-client-label">Prepared for</span>
<span class="sidebar-focus-client-name" id="sidebarFocusClientName">Client Name</span>
</div>
<div class="sidebar-hero">
<div class="sidebar-mrr-label">Managed Services Investment (MRI)</div>
<div class="sidebar-mrr" id="mrrDisplay">$150</div>
</div>
<div class="sidebar-focus-columns">
<div class="sidebar-focus-col sidebar-focus-col--left">
<div class="sidebar-group sidebar-group--monthly">
<div class="sidebar-group-title">Monthly Breakdown</div>
<!-- ── SIDEBAR SERVICE LINES ────────────────────────────────
Each .sidebar-line hidden by default.
update() calls show(id, condition) to toggle .hidden.
sl-users / sl-endpoints / sl-servers / sl-zt / sl-voip
shown only when their respective count > 0.
sl-admin is always visible (never hidden).
sl-*-sub rows are sub-labels (e.g. "5 × $130/user")
toggled via style.display not .hidden class.
────────────────────────────────────────────────────────── -->
<div id="sidebarLines">
<div class="sidebar-line hidden" id="sl-users">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-users" style="--icon-size:14px;"></span></span> Users</span>
<span class="val" id="sl-users-val"></span>
</div>
<div class="sl-sub hidden" id="sl-users-sub"></div>
<div class="sidebar-line hidden" id="sl-endpoints">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-desktop" style="--icon-size:14px;"></span></span> Endpoints</span>
<span class="val" id="sl-endpoints-val"></span>
</div>
<div class="sl-sub hidden" id="sl-endpoints-sub"></div>
<div class="sidebar-line hidden" id="sl-servers">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-server" style="--icon-size:13px;"></span></span> Servers</span>
<span class="val" id="sl-servers-val"></span>
</div>
<div class="sidebar-line hidden" id="sl-zt">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-shield-check" style="--icon-size:13px;"></span></span> Zero Trust</span>
<span class="val" id="sl-zt-val"></span>
</div>
<div class="sidebar-line hidden" id="sl-voip">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-phone" style="--icon-size:13px;"></span></span> VoIP</span>
<span class="val" id="sl-voip-val"></span>
</div>
<div class="sidebar-line" id="sl-admin">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-building" style="--icon-size:12px;"></span></span> Site Management</span>
<span class="val" id="sl-admin-val">$150</span>
</div>
<div class="sl-sub hidden" id="sl-admin-sub"></div>
</div>
<div class="sidebar-line sidebar-line-total" id="sl-monthly-total-row">
<span>Monthly Recurring Total</span>
<span class="val" id="sl-monthly-total-val">$150</span>
</div>
<div class="sidebar-line sidebar-line-discount hidden" id="sl-base-mrr-row">
<span class="sl-muted">Before Term Incentive</span>
<span class="val sl-muted" id="sl-base-mrr-val"></span>
</div>
<div class="sidebar-line sidebar-line-discount hidden" id="sl-discount-row">
<span class="sl-muted">Contract Incentive <span class="sl-discount-detail" id="sl-discount-detail"></span></span>
<span class="val sl-discount-val" id="sl-discount-val"></span>
</div>
</div>
<div class="sidebar-group sidebar-group--tax">
<label class="sl-hst-toggle qs-toggle-row">
<input type="checkbox" id="hstToggle" onchange="update()">
<span class="qs-switch"></span>
<span class="qs-toggle-label">Include Tax</span>
</label>
<div class="sidebar-line sidebar-line-hst hidden" id="sl-hst-row">
<span class="sl-muted">+ HST 13%</span>
<span class="val sl-hst-val" id="sl-hst-val"></span>
</div>
<div class="sidebar-line sidebar-line-total hidden" id="sl-hst-total-row">
<span>Monthly Total</span>
<span class="val" id="sl-hst-total-val"></span>
</div>
</div>
</div>
<div class="sidebar-focus-col sidebar-focus-col--right">
<div class="sidebar-group sidebar-group--invoice">
<div class="sidebar-group-title">First Invoice</div>
<div class="sidebar-line" id="sl-first-mri-row">
<span>Invoice Monthly Total</span>
<span class="val" id="sl-first-mri-val">$150</span>
</div>
<div class="sidebar-line hidden" id="sl-first-hst-row">
<span>+ HST 13%</span>
<span class="val" id="sl-first-hst-val"></span>
</div>
<div class="sidebar-line hidden" id="sl-otf-row">
<span>Onboarding / Implementation</span>
<span class="val" id="sl-otf-val"></span>
</div>
<div class="sidebar-line sidebar-line-total" id="sl-first-total-row">
<span>First Invoice Total</span>
<span class="val" id="sl-first-total-val">$150</span>
</div>
</div>
<div class="sidebar-group sidebar-group--value">
<div class="sidebar-group-title">Value &amp; Savings Snapshot</div>
<div class="sidebar-line hidden sidebar-line-value" id="sl-value-m365-row">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-square-check fa-icon--green" style="--icon-size:13px;"></span></span>M365 Licensing Savings</span>
<span class="val savings-amount" id="sl-value-m365-val"></span>
</div>
<div class="sidebar-line hidden sidebar-line-value" id="sl-value-term-row">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-square-check fa-icon--green" style="--icon-size:13px;"></span></span>Term Incentive Savings</span>
<span class="val savings-amount" id="sl-value-term-val"></span>
</div>
<div class="sidebar-line hidden sidebar-line-value" id="sl-value-onboarding-row">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-square-check fa-icon--green" style="--icon-size:13px;"></span></span><span id="sl-value-onboarding-label">Complimentary Onboarding</span></span>
<span class="val savings-amount" id="sl-value-onboarding-val"></span>
</div>
<div class="sidebar-line hidden sidebar-line-value" id="sl-value-admin-row">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-square-check fa-icon--green" style="--icon-size:13px;"></span></span>Site Management</span>
<span class="val savings-amount" id="sl-value-admin-val"></span>
</div>
<div class="sidebar-line hidden sidebar-line-opportunity" id="sl-value-byol-row">
<span><span class="lbl-icon"><span class="fa-icon fa-icon-triangle-exclamation fa-icon--amber" style="--icon-size:13px;"></span></span>Available M365 Business Premium Value</span>
<span class="val" id="sl-value-byol-val"></span>
</div>
<div class="sidebar-line sidebar-line-total hidden" id="sl-value-total-row">
<span>Total Value Unlocked</span>
<span class="val savings-amount" id="sl-value-total-val"></span>
</div>
</div>
<div class="sidebar-group sidebar-group--summary">
<div class="sidebar-line">
<span>Estimated Annual Managed Spend</span>
<span class="val" id="annualDisplay">$1,800</span>
</div>
<div class="sidebar-line hidden" id="perUserRow">
<span>Effective Managed Cost Per User<br><small id="perUserBreakdown" class="per-user-cost-sub sidebar-note-mono hidden"></small></span>
<span class="val" id="perUserDisplay"></span>
</div>
</div>
<!-- ── VS HIRING IN-HOUSE ─────────────────────────────────────
Hidden (.hidden) until users>0 OR endpoints>0.
updateVsComparison(q) renders all values.
vs-1man-save-row / vs-5man-save-row: background + text colour
set inline by JS (green = savings, amber = costs more).
These rows use document.querySelector to reach the
inner <span> — if the HTML structure changes, update that
selector in updateVsComparison().
──────────────────────────────────────────────────────────── -->
<div id="vsComparison" class="hidden vs-comparison-wrap">
<div class="vs-header">
<div class="vs-brand-row">
<svg width="18" height="24" viewBox="0 0 72 98" class="vs-brand-logo" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<polyline points="7.32 8.88 62.11 8.88 34.72 58.22" fill="#1f75a6"/>
<polyline points="40.7 55.33 64.4 12.64 71.88 12.64 44.48 61.99 40.7 55.33" fill="#8d252f"/>
</svg>
<div class="vs-brand-name">SVS MSP</div>
</div>
<div class="vs-label">ROI Snapshot vs. In-House IT</div>
</div>
<table class="vs-table">
<tr>
<td>
<span class="vs-svs-label">Managed Services</span>
</td>
<td class="vs-val-accent" id="vs-svs-annual"></td>
</tr>
<tr><td class="vs-td-muted"><span class="fa-icon fa-icon-user vs-td-icon" style="--icon-size:13px;"></span> 1 IT person + tools</td><td class="vs-td-muted" id="vs-1man-cost"></td></tr>
<tr class="vs-save-row" id="vs-1man-save-row"><td><span id="vs-1man-save-lbl" class="vs-val-green">YOU SAVE</span></td><td id="vs-1man-save" class="vs-val-green"></td></tr>
<tr><td class="vs-td-muted"><span class="fa-icon fa-icon-users vs-td-icon" style="--icon-size:14px;"></span> 5-person team</td><td class="vs-td-muted" id="vs-5man-cost"></td></tr>
<tr class="vs-save-row" id="vs-5man-save-row"><td><span id="vs-5man-save-lbl" class="vs-val-green">YOU SAVE</span></td><td id="vs-5man-save" class="vs-val-green"></td></tr>
</table>
<div class="vs-footnote" id="vs-footnote"></div>
</div>
</div>
</div>
<!-- ── QUOTE NOTES ──────────────────────────────────────────────
Free-text area for sales rep notes. Persisted in localStorage,
included in JSON export and printed invoice.
──────────────────────────────────────────────────────────────── -->
<div class="quote-notes-wrap">
<label class="quote-notes-label" for="quoteNotes">Quote Notes</label>
<textarea class="quote-notes-input" id="quoteNotes" rows="3" placeholder="Additional notes for this quote..." oninput="debouncedSave()"></textarea>
</div>
<!-- ── EXPORT BUTTONS ───────────────────────────────────────────
Export A: window.print() — triggers browser print/save-as-PDF.
Export B: exportQuoteJSON() — downloads .json + copies to clipboard.
exportQuote() — plain-text .txt (original, kept for reference).
──────────────────────────────────────────────────────────────── -->
<div class="export-wrap">
<button type="button" class="btn-reset-quote" onclick="openResetConfirm()">Reset Quote</button>
<button type="button" class="btn-import-quote" id="btnImportQuote" onclick="importQuoteJSON()">Import Quote</button>
</div>
</div><!-- END sidebar-body -->
</div><!-- END sidebar -->
</div><!-- END side-col — desktop sidebar -->
</div><!-- END outer grid (3fr main-col / 2fr side-col) -->
<!-- BOTTOM PITCH -->
<div class="pitch-wrap">
<div class="pitch-inner">
<div class="pitch-grid">
<div class="pitch-item">
<div class="pitch-head">
<div class="pitch-icon"><span class="fa-icon fa-icon-shield-check fa-icon--accent" style="--icon-size:20px;"></span></div>
<div class="pitch-title">Security-First MSP</div>
</div>
<div class="pitch-desc">Every engagement is built on a security baseline — EDR, MFA, patch management, and cyber warranty included.</div>
</div>
<div class="pitch-item">
<div class="pitch-head">
<div class="pitch-icon"><span class="fa-icon fa-icon-location-dot fa-icon--accent" style="--icon-size:20px;"></span></div>
<div class="pitch-title">Ottawa-Based Team</div>
</div>
<div class="pitch-desc">Local presence, Canadian data sovereignty, and an account team that knows your business and your region.</div>
</div>
<div class="pitch-item">
<div class="pitch-head">
<div class="pitch-icon"><span class="fa-icon fa-icon-wrench-simple fa-icon--accent" style="--icon-size:20px;"></span></div>
<div class="pitch-title">Flat-Rate, No Surprises</div>
</div>
<div class="pitch-desc">Predictable monthly billing with no per-ticket charges — aligned incentives mean we fix things right the first time.</div>
</div>
<div class="pitch-item">
<div class="pitch-head">
<div class="pitch-icon"><span class="fa-icon fa-icon-chart-line-up fa-icon--accent" style="--icon-size:20px;"></span></div>
<div class="pitch-title">Scales With You</div>
</div>
<div class="pitch-desc">Add users, endpoints, servers, ZT networking, or VoIP as you grow — one vendor, one invoice, one relationship.</div>
</div>
</div>
<div class="pitch-footer">
<span class="fa-icon fa-icon-square-check" style="--icon-size:13px;margin-right:5px;"></span> Canadian company, Canadian support
</div>
</div>
</div>
<div class="confirm-modal" id="resetConfirmModal" aria-hidden="true">
<div class="confirm-modal-backdrop" onclick="closeResetConfirm()"></div>
<div class="confirm-modal-card" role="dialog" aria-modal="true" aria-labelledby="resetConfirmTitle">
<div class="confirm-modal-eyebrow">Reset Quote</div>
<h2 class="confirm-modal-title" id="resetConfirmTitle">Reset saved quote and return to defaults?</h2>
<p class="confirm-modal-copy">This will clear the saved calculator state and generate a fresh quote. Your theme preference will stay the same.</p>
<div class="confirm-modal-actions">
<button type="button" class="confirm-btn confirm-btn-secondary" id="resetConfirmCancel" onclick="closeResetConfirm()">Cancel</button>
<button type="button" class="confirm-btn confirm-btn-danger" onclick="confirmResetQuote()">Yes, Reset</button>
</div>
</div>
</div>
<script src="package-prices-data.js"></script>
<script src="quote-pricing.js"></script>
<script src="quote-engine.js"></script>
<script src="quote-render.js"></script>
<script src="quote-persistence.js"></script>
<script src="quote-export.js"></script>
<script src="quote-import.js"></script>
<script src="theme-manager.js"></script>
<script src="SVS-MSP-Calculator.js"></script>
<script src="mobile-sync.js"></script>
</body>
</html>