Massive AI Overhaul
2
M365icons/azure-svgrepo-com.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>file_type_azure</title><path d="M14.98,25.994c3.57-.726,6.518-1.327,6.552-1.335l.062-.015-3.37-4.615c-1.854-2.538-3.37-4.625-3.37-4.637s3.48-11.056,3.5-11.095c.007-.013,2.375,4.694,5.741,11.411l5.774,11.521.044.088-10.711,0-10.711,0Z" style="fill:#0089d6"/><path d="M2.125,24.586c0-.006,1.588-3.18,3.529-7.053l3.529-7.041L13.3,6.52c2.262-2.185,4.119-3.976,4.126-3.979a.914.914,0,0,1-.066.192L12.89,13.759,8.5,24.589l-3.189,0C3.56,24.6,2.125,24.593,2.125,24.586Z" style="fill:#0089d6"/></svg>
|
||||||
|
After Width: | Height: | Size: 706 B |
2
M365icons/excel.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 -1.27 110.037 110.037" xmlns="http://www.w3.org/2000/svg"><path d="M57.55 0h7.425v10c12.513 0 25.025.025 37.537-.038 2.113.087 4.438-.062 6.275 1.2 1.287 1.85 1.138 4.2 1.225 6.325-.062 21.7-.037 43.388-.024 65.075-.062 3.638.337 7.35-.425 10.938-.5 2.6-3.625 2.662-5.713 2.75-12.95.037-25.912-.025-38.875 0v11.25h-7.763c-19.05-3.463-38.138-6.662-57.212-10V10.013C19.188 6.675 38.375 3.388 57.55 0z" fill="#207245"/><path d="M64.975 13.75h41.25V92.5h-41.25V85h10v-8.75h-10v-5h10V62.5h-10v-5h10v-8.75h-10v-5h10V35h-10v-5h10v-8.75h-10v-7.5z" fill="#ffffff"/><path d="M79.975 21.25h17.5V30h-17.5v-8.75z" fill="#207245"/><path d="M37.025 32.962c2.825-.2 5.663-.375 8.5-.512a2607.344 2607.344 0 0 1-10.087 20.487c3.438 7 6.949 13.95 10.399 20.95a716.28 716.28 0 0 1-9.024-.575c-2.125-5.213-4.713-10.25-6.238-15.7-1.699 5.075-4.125 9.862-6.074 14.838-2.738-.038-5.476-.15-8.213-.263C19.5 65.9 22.6 59.562 25.912 53.312c-2.812-6.438-5.9-12.75-8.8-19.15 2.75-.163 5.5-.325 8.25-.475 1.862 4.888 3.899 9.712 5.438 14.725 1.649-5.312 4.112-10.312 6.225-15.45z" fill="#ffffff"/><path d="M79.975 35h17.5v8.75h-17.5V35zM79.975 48.75h17.5v8.75h-17.5v-8.75zM79.975 62.5h17.5v8.75h-17.5V62.5zM79.975 76.25h17.5V85h-17.5v-8.75z" fill="#207245"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
2
M365icons/excel2-svgrepo-com.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>file_type_excel2</title><path d="M28.781,4.405H18.651V2.018L2,4.588V27.115l16.651,2.868V26.445H28.781A1.162,1.162,0,0,0,30,25.349V5.5A1.162,1.162,0,0,0,28.781,4.405Zm.16,21.126H18.617L18.6,23.642h2.487v-2.2H18.581l-.012-1.3h2.518v-2.2H18.55l-.012-1.3h2.549v-2.2H18.53v-1.3h2.557v-2.2H18.53v-1.3h2.557v-2.2H18.53v-2H28.941Z" style="fill:#20744a;fill-rule:evenodd"/><rect x="22.487" y="7.439" width="4.323" height="2.2" style="fill:#20744a"/><rect x="22.487" y="10.94" width="4.323" height="2.2" style="fill:#20744a"/><rect x="22.487" y="14.441" width="4.323" height="2.2" style="fill:#20744a"/><rect x="22.487" y="17.942" width="4.323" height="2.2" style="fill:#20744a"/><rect x="22.487" y="21.443" width="4.323" height="2.2" style="fill:#20744a"/><polygon points="6.347 10.673 8.493 10.55 9.842 14.259 11.436 10.397 13.582 10.274 10.976 15.54 13.582 20.819 11.313 20.666 9.781 16.642 8.248 20.513 6.163 20.329 8.585 15.666 6.347 10.673" style="fill:#ffffff;fill-rule:evenodd"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
2
M365icons/outlook-svgrepo-com.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>file_type_outlook</title><path d="M19.484,7.937v5.477L21.4,14.619a.489.489,0,0,0,.21,0l8.238-5.554a1.174,1.174,0,0,0-.959-1.128Z" style="fill:#0072c6"/><path d="M19.484,15.457l1.747,1.2a.522.522,0,0,0,.543,0c-.3.181,8.073-5.378,8.073-5.378V21.345a1.408,1.408,0,0,1-1.49,1.555H19.483V15.457Z" style="fill:#0072c6"/><path d="M10.44,12.932a1.609,1.609,0,0,0-1.42.838,4.131,4.131,0,0,0-.526,2.218A4.05,4.05,0,0,0,9.02,18.2a1.6,1.6,0,0,0,2.771.022,4.014,4.014,0,0,0,.515-2.2,4.369,4.369,0,0,0-.5-2.281A1.536,1.536,0,0,0,10.44,12.932Z" style="fill:#0072c6"/><path d="M2.153,5.155V26.582L18.453,30V2ZM13.061,19.491a3.231,3.231,0,0,1-2.7,1.361,3.19,3.19,0,0,1-2.64-1.318A5.459,5.459,0,0,1,6.706,16.1a5.868,5.868,0,0,1,1.036-3.616A3.267,3.267,0,0,1,10.486,11.1a3.116,3.116,0,0,1,2.61,1.321,5.639,5.639,0,0,1,1,3.484A5.763,5.763,0,0,1,13.061,19.491Z" style="fill:#0072c6"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
2
M365icons/powerpoint.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 -1.27 110.031 110.031" xmlns="http://www.w3.org/2000/svg"><path d="M57.893 0h7.087v11.25c13.363.075 26.738-.138 40.088.062 2.875-.275 5.125 1.962 4.838 4.837.212 23.35-.05 46.712.125 70.075-.125 2.525.25 5.325-1.2 7.562-1.825 1.325-4.2 1.15-6.338 1.25-12.5-.062-25-.037-37.513-.037v12.5h-7.774c-19.05-3.475-38.138-6.65-57.2-10-.013-29.162 0-58.325 0-87.475C19.292 6.688 38.58 3.288 57.893 0z" fill="#d24625"/><path d="M64.98 15h41.25v76.25H64.98v-10h30v-5h-30V70h30v-5H64.993c-.013-2.45-.013-4.9-.024-7.35 4.95 1.537 10.587 1.5 15.012-1.476 4.788-2.837 7.288-8.25 7.7-13.65-5.487-.038-10.975-.025-16.45-.025-.012-5.438.062-10.875-.112-16.3-2.05.4-4.1.825-6.138 1.262V15z" fill="#ffffff"/><path d="M73.743 23.587c8.688.4 15.987 7.712 16.45 16.375-5.488.063-10.975.038-16.463.038 0-5.475-.012-10.95.013-16.413z" fill="#d24625"/><path d="M20.055 33.025c6.788.325 15.013-2.688 20.638 2.4 5.388 6.538 3.963 18.562-4.025 22.476-2.837 1.449-6.087 1.25-9.175 1.149-.013 4.888-.024 9.775-.013 14.663a1323.27 1323.27 0 0 0-7.438-.625c-.112-13.351-.136-26.713.013-40.063z" fill="#ffffff"/><path d="M27.48 39.788c2.463-.113 5.513-.562 7.176 1.75 1.425 2.45 1.35 5.675.162 8.2-1.425 2.575-4.65 2.325-7.138 2.625-.263-4.188-.237-8.376-.2-12.575z" fill="#d24625"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
21
M365icons/teams.svg
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 0 2381.4 2354.5" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#5558AF;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M2015.6,899.2c19.5,19.5,42.5,35,67.9,45.8c53,22.2,112.7,22.2,165.8,0c51.2-21.8,92-62.5,113.7-113.7
|
||||||
|
c22.2-53,22.2-112.7,0-165.8c-21.8-51.2-62.5-92-113.7-113.7c-53-22.2-112.7-22.2-165.8,0c-51.2,21.8-92,62.5-113.7,113.7
|
||||||
|
c-22.2,53-22.2,112.7,0,165.8C1980.6,856.6,1996.2,879.7,2015.6,899.2L2015.6,899.2z M1953.2,1097v642.1h107
|
||||||
|
c36.8-0.2,73.4-3.6,109.5-10.4c36.3-6.4,71.3-18.6,103.7-36.2c30.6-16.6,57-40,77.3-68.2c21.3-31.3,32-68.6,30.5-106.5V1097H1953.2
|
||||||
|
z M1606.4,827.8c28.4,0.2,56.6-5.5,82.8-16.7c51.2-21.8,91.9-62.5,113.6-113.7c22.2-53,22.2-112.7-0.1-165.8
|
||||||
|
c-21.8-51.2-62.5-92-113.7-113.7c-26.2-11.2-54.4-16.9-82.9-16.7c-28.3-0.2-56.3,5.5-82.3,16.7c-19.4,8.3-25.5,19.1-52.2,32.1v329
|
||||||
|
c26.8,13.1,32.8,23.8,52.2,32.1C1549.9,822.4,1578,828,1606.4,827.8L1606.4,827.8z M1471.6,1908.9c26.8,5.8,36.4,10.3,55.4,12.9
|
||||||
|
c20.8,3,41.8,4.5,62.8,4.6c32.4-0.2,64.8-3.6,96.5-10.4c32.3-6.5,63.3-18.6,91.5-35.7c27.7-17,51-40.2,68.2-67.7
|
||||||
|
c19-32.1,28.3-69.1,26.9-106.4v-743h-401.3V1908.9z M0,2113.7l1391.3,240.8V0L0,240.8V2113.7z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st1" d="M1016.7,722.4l-642.1,39.1v148.1l240.8-9.7v686.7l160.5,9.4V893.6l240.8-10.7V722.4z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
2
M365icons/word.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 -1.27 110.031 110.031" xmlns="http://www.w3.org/2000/svg"><path d="M57.505 0h7.475v10c13.375.075 26.738-.138 40.101.075 2.85-.288 5.087 1.925 4.825 4.775.212 24.625-.05 49.262.125 73.887-.125 2.525.25 5.325-1.213 7.562-1.825 1.3-4.188 1.138-6.312 1.237-12.514-.061-25.014-.036-37.526-.036v10h-7.812c-19.024-3.475-38.1-6.662-57.162-10-.013-29.162 0-58.325 0-87.475C19.167 6.675 38.343 3.413 57.506 0z" fill="#2a5699"/><path d="M64.98 13.75h41.25v80H64.98v-10h32.5v-5h-32.5V72.5h32.5v-5h-32.5v-6.25h32.5v-5h-32.5V50h32.5v-5h-32.5v-6.25h32.5v-5h-32.5V27.5h32.5v-5h-32.5v-8.75zM25.83 35.837c2.375-.137 4.75-.237 7.125-.362 1.662 8.438 3.362 16.862 5.162 25.262 1.413-8.675 2.976-17.325 4.487-25.987 2.5-.087 5-.225 7.488-.375-2.825 12.112-5.3 24.325-8.388 36.362-2.088 1.088-5.213-.05-7.688.125-1.663-8.274-3.6-16.5-5.088-24.812-1.462 8.075-3.362 16.075-5.037 24.101-2.4-.125-4.812-.275-7.226-.438-2.074-11-4.512-21.925-6.449-32.95 2.137-.1 4.287-.188 6.425-.263 1.287 7.962 2.75 15.888 3.875 23.862 1.765-8.174 3.564-16.349 5.314-24.525z" fill="#ffffff"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
282
SVS-MSP-Calculator-70retro.css
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
SVS MSP Calculator — Retro Cyberpunk Theme
|
||||||
|
Warm parchment paper base with neon-warm accents —
|
||||||
|
hot rose highlights, electric amber secondary, warm teal pop,
|
||||||
|
dark charcoal header. Vintage paper meets neon signage.
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* ── Core palette ────────────────────────────────────────── */
|
||||||
|
--ink: #1c1317;
|
||||||
|
--paper: #f0e4c8;
|
||||||
|
--accent: #e11d48;
|
||||||
|
--muted: #8a7a72;
|
||||||
|
--border: #d0c4b0;
|
||||||
|
--border-soft: #d8ccb8;
|
||||||
|
--card: #e8dcc0;
|
||||||
|
--green: #0d9488;
|
||||||
|
--amber: #d97706;
|
||||||
|
--sky: #0d9488;
|
||||||
|
--focus-ring-soft: rgba(225, 29, 72, 0.2);
|
||||||
|
|
||||||
|
/* ── Top bar ─────────────────────────────────────────────── */
|
||||||
|
--top-bar-bg: #1c1317;
|
||||||
|
--top-bar-border: rgba(225, 29, 72, 0.35);
|
||||||
|
--top-bar-meta: #c0aa98;
|
||||||
|
--top-bar-shadow: 0 4px 16px rgba(28, 19, 23, 0.2);
|
||||||
|
|
||||||
|
/* ── Theme chip ──────────────────────────────────────────── */
|
||||||
|
--theme-chip-bg: rgba(225, 29, 72, 0.1);
|
||||||
|
--theme-chip-hover: rgba(225, 29, 72, 0.18);
|
||||||
|
--theme-chip-active: rgba(225, 29, 72, 0.26);
|
||||||
|
--theme-chip-fg: #f0e4d0;
|
||||||
|
|
||||||
|
/* ── Surfaces ────────────────────────────────────────────── */
|
||||||
|
--surface-section: #e4d5b5;
|
||||||
|
--surface-feature: #dccda8;
|
||||||
|
--surface-settings: #d6c49e;
|
||||||
|
--surface-settings-divider: #c4ae8a;
|
||||||
|
--surface-input: #efe2c4;
|
||||||
|
--surface-term-wrap: #e0d0ad;
|
||||||
|
--surface-term-tile: transparent;
|
||||||
|
--surface-term-tile-hover: rgba(225, 29, 72, 0.05);
|
||||||
|
--surface-term-tile-active: linear-gradient(180deg, #e11d48 0%, #be123c 100%);
|
||||||
|
--border-term-wrap: #c4ae8a;
|
||||||
|
--border-term-tile-active: rgba(225, 29, 72, 0.3);
|
||||||
|
--shadow-term-wrap: inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||||
|
--shadow-term-tile-active: inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||||
|
|
||||||
|
/* ── Term text ───────────────────────────────────────────── */
|
||||||
|
--text-term-name: #8a7a72;
|
||||||
|
--text-term-sub: #6a5a50;
|
||||||
|
--text-term-sub-active: #fff;
|
||||||
|
--text-term-name-active: #fff;
|
||||||
|
--text-term-discount: #1c1317;
|
||||||
|
--text-term-discount-active: #ffffff;
|
||||||
|
|
||||||
|
/* ── Best value badge ────────────────────────────────────── */
|
||||||
|
--surface-best-value: rgba(13, 148, 136, 0.12);
|
||||||
|
--border-best-value: rgba(13, 148, 136, 0.32);
|
||||||
|
--text-best-value: #0d9488;
|
||||||
|
--surface-best-value-active: rgba(255, 255, 255, 0.2);
|
||||||
|
--border-best-value-active: rgba(255, 255, 255, 0.4);
|
||||||
|
--text-best-value-active: #ffffff;
|
||||||
|
|
||||||
|
/* ── Sidebar ─────────────────────────────────────────────── */
|
||||||
|
--surface-sidebar: #e0d4b6;
|
||||||
|
--surface-sidebar-header: #1c1317;
|
||||||
|
--surface-sidebar-body: #e6d8b8;
|
||||||
|
--surface-sidebar-utility: #d0be98;
|
||||||
|
--surface-export: #ccba94;
|
||||||
|
--border-sidebar: #c4ae8a;
|
||||||
|
--surface-sidebar-utility-border: #b5a07a;
|
||||||
|
--border-export-top: #c4ae8a;
|
||||||
|
|
||||||
|
/* ── Compare / Modal ─────────────────────────────────────── */
|
||||||
|
--surface-compare: #d6c8a6;
|
||||||
|
--border-compare: #c4ae8a;
|
||||||
|
--surface-modal: #efe2c4;
|
||||||
|
--surface-backdrop: rgba(28, 19, 23, 0.6);
|
||||||
|
--shadow-modal: 0 16px 50px rgba(28, 19, 23, 0.35);
|
||||||
|
|
||||||
|
/* ── Mobile ──────────────────────────────────────────────── */
|
||||||
|
--surface-mobile-sheet: #e4d5b5;
|
||||||
|
--surface-mobile-close-row: #d6c49e;
|
||||||
|
--surface-mobile-actions: #d6c49e;
|
||||||
|
--surface-mobile-sidebar: transparent;
|
||||||
|
--surface-mobile-close-btn: rgba(28, 19, 23, 0.08);
|
||||||
|
--surface-mobile-close-btn-active: rgba(28, 19, 23, 0.14);
|
||||||
|
--border-mobile-sheet: #c4ae8a;
|
||||||
|
--border-mobile-row: #c4ae8a;
|
||||||
|
|
||||||
|
/* ── Ghost / Utility surfaces ────────────────────────────── */
|
||||||
|
--surface-accent-soft: rgba(225, 29, 72, 0.05);
|
||||||
|
--surface-summary-badge: rgba(225, 29, 72, 0.08);
|
||||||
|
--border-summary-badge: rgba(225, 29, 72, 0.2);
|
||||||
|
--surface-chevron: rgba(28, 19, 23, 0.05);
|
||||||
|
--surface-chevron-active: rgba(28, 19, 23, 0.09);
|
||||||
|
--surface-ghost: rgba(28, 19, 23, 0.06);
|
||||||
|
--surface-ghost-hover: rgba(28, 19, 23, 0.12);
|
||||||
|
--surface-step: #efe2c4;
|
||||||
|
--surface-step-hover: #d6c49e;
|
||||||
|
--surface-step-active: var(--accent);
|
||||||
|
--surface-step-border: #b5a07a;
|
||||||
|
--text-step: var(--accent);
|
||||||
|
|
||||||
|
/* ── State surfaces ──────────────────────────────────────── */
|
||||||
|
--surface-success: #dceee6;
|
||||||
|
--surface-success-border: #6db89a;
|
||||||
|
--surface-danger: #f5dcd6;
|
||||||
|
--surface-danger-border: #d4827a;
|
||||||
|
--text-danger: #b91c1c;
|
||||||
|
--surface-warning: #f0e4c4;
|
||||||
|
--surface-warning-panel: #ecdcb8;
|
||||||
|
--surface-warning-border: #d4a860;
|
||||||
|
--surface-compare-success: rgba(13, 148, 136, 0.12);
|
||||||
|
--surface-compare-warning: rgba(217, 119, 6, 0.12);
|
||||||
|
--surface-selected: rgba(225, 29, 72, 0.06);
|
||||||
|
--surface-addon-hover: rgba(225, 29, 72, 0.04);
|
||||||
|
--border-addon-hover: rgba(225, 29, 72, 0.18);
|
||||||
|
--text-selected-accent: #9f1239;
|
||||||
|
|
||||||
|
/* ── Text colours ────────────────────────────────────────── */
|
||||||
|
--text-sidebar-kicker: rgba(255, 255, 255, 0.84);
|
||||||
|
--text-sidebar-heading: #fff;
|
||||||
|
--text-sidebar-placeholder: rgba(255, 255, 255, 0.72);
|
||||||
|
--text-money: var(--ink);
|
||||||
|
--text-money-hero: var(--ink);
|
||||||
|
--text-vs-heading: var(--ink);
|
||||||
|
--text-vs-accent: #be123c;
|
||||||
|
--text-vs-muted: var(--muted);
|
||||||
|
--text-incentive: #0d9488;
|
||||||
|
--text-pill-savings-active: #ccfbf1;
|
||||||
|
|
||||||
|
/* ── Section interaction ─────────────────────────────────── */
|
||||||
|
--section-hover-border: rgba(225, 29, 72, 0.18);
|
||||||
|
--section-hover-shadow: -3px 0 0 0 rgba(225, 29, 72, 0.2);
|
||||||
|
--section-open-border: rgba(225, 29, 72, 0.3);
|
||||||
|
--section-open-shadow: -3px 0 0 0 rgba(225, 29, 72, 0.35);
|
||||||
|
|
||||||
|
/* ── Buttons ─────────────────────────────────────────────── */
|
||||||
|
--btn-primary-fg: #ffffff;
|
||||||
|
--btn-primary-hover: #be123c;
|
||||||
|
--surface-pill-icon: rgba(255, 255, 255, 0.2);
|
||||||
|
--sidebar-zone-services: rgba(0, 0, 0, 0.03);
|
||||||
|
--sidebar-zone-invoice: rgba(0, 0, 0, 0.045);
|
||||||
|
--sidebar-zone-value: rgba(0, 0, 0, 0.02);
|
||||||
|
--surface-switch-off: #c0b4a0;
|
||||||
|
--surface-switch-on: var(--green);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Retro cyberpunk typography ───────────────────────────── */
|
||||||
|
.top-bar,
|
||||||
|
.section-header .section-title,
|
||||||
|
.sidebar-header,
|
||||||
|
.sidebar-mrr,
|
||||||
|
.sidebar-hero-label {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Top bar — warm dark with hot rose neon edge ─────────── */
|
||||||
|
.top-bar {
|
||||||
|
background: linear-gradient(180deg, #2a1e22 0%, #1c1317 60%, #140e11 100%) !important;
|
||||||
|
border-bottom: 1px solid rgba(225, 29, 72, 0.3) !important;
|
||||||
|
box-shadow: 0 2px 16px rgba(225, 29, 72, 0.06) !important;
|
||||||
|
color: #f0e4d0 !important;
|
||||||
|
}
|
||||||
|
.top-bar .top-bar-meta { color: #c0aa98 !important; }
|
||||||
|
|
||||||
|
/* ── Logo fix — SVG text paths hardcoded #0c0c0c,
|
||||||
|
override to cream so they pop on the dark header ─────── */
|
||||||
|
.top-bar-logo path {
|
||||||
|
fill: #f0e4d0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Sidebar header — matches top bar ────────────────────── */
|
||||||
|
.sidebar-header {
|
||||||
|
background: linear-gradient(180deg, #2a1e22 0%, #1c1317 100%) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Section number — hot rose (faded on paper) ──────────── */
|
||||||
|
.section-num {
|
||||||
|
color: rgba(225, 29, 72, 0.15) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Pill toggle checked — hot rose gradient ─────────────── */
|
||||||
|
.pill-toggle input:checked + label {
|
||||||
|
background: linear-gradient(180deg, #e11d48 0%, #be123c 100%) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.pill-toggle input:checked + label .pill-price,
|
||||||
|
.pill-toggle input:checked + label .pill-desc {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Export buttons — hot rose CTA ───────────────────────── */
|
||||||
|
.btn-export {
|
||||||
|
background: linear-gradient(180deg, #e11d48 0%, #be123c 100%) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.btn-export:hover {
|
||||||
|
filter: brightness(1.1) !important;
|
||||||
|
box-shadow: 0 2px 12px rgba(225, 29, 72, 0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Tier segment active — hot rose gradient ─────────────── */
|
||||||
|
.tier-seg.active {
|
||||||
|
background: linear-gradient(180deg, #e11d48 0%, #be123c 100%) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Import button hover — teal accent ───────────────────── */
|
||||||
|
.btn-import-quote:hover {
|
||||||
|
background: color-mix(in srgb, var(--sky) 8%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--sky) 30%, transparent);
|
||||||
|
color: var(--sky);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Addon selected — rose tint on paper ─────────────────── */
|
||||||
|
.addon-row.selected {
|
||||||
|
background: rgba(225, 29, 72, 0.04) !important;
|
||||||
|
border-color: rgba(225, 29, 72, 0.18) !important;
|
||||||
|
box-shadow: inset 3px 0 0 0 var(--accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Paper texture with warm scanlines ───────────────────── */
|
||||||
|
body {
|
||||||
|
background-color: #f0e4c8;
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent,
|
||||||
|
transparent 2px,
|
||||||
|
rgba(180, 150, 100, 0.03) 2px,
|
||||||
|
rgba(180, 150, 100, 0.03) 4px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Switch knob — hot rose when active ──────────────────── */
|
||||||
|
.switch input:checked + .switch-track {
|
||||||
|
background: var(--accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Nudge banners — warm paper tints ────────────────────── */
|
||||||
|
.nudge-banner.amber {
|
||||||
|
background: #f0dab0 !important;
|
||||||
|
border-color: #d4a040 !important;
|
||||||
|
}
|
||||||
|
.nudge-banner.green {
|
||||||
|
background: #d6ece4 !important;
|
||||||
|
border-color: #5aaa98 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Callout boxes ───────────────────────────────────────── */
|
||||||
|
.callout-green {
|
||||||
|
background: #dceee6 !important;
|
||||||
|
border-color: #6db89a !important;
|
||||||
|
color: #0d9488 !important;
|
||||||
|
}
|
||||||
|
.callout-red {
|
||||||
|
background: #f5dcd6 !important;
|
||||||
|
border-color: #d4827a !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Mobile quote pill — hot rose with glow ──────────────── */
|
||||||
|
.mobile-quote-pill {
|
||||||
|
background: linear-gradient(180deg, #e11d48 0%, #be123c 100%) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
box-shadow: 0 0 10px rgba(225, 29, 72, 0.25) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Progress bar — rose to teal gradient ────────────────── */
|
||||||
|
.progress-fill {
|
||||||
|
background: linear-gradient(90deg, #e11d48 0%, #0d9488 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Full-screen sidebar export-wrap ─────────────────────── */
|
||||||
|
body.sidebar-focus-open .side-col .export-wrap {
|
||||||
|
background: transparent !important;
|
||||||
|
border-top: none !important;
|
||||||
|
}
|
||||||
73
SVS-MSP-Calculator-base.css
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/* SVS MSP Calculator - Base */
|
||||||
|
/* Extracted during Phase 5 to keep the HTML shell stable while splitting the monolithic stylesheet. */
|
||||||
|
body {
|
||||||
|
background: var(--paper);
|
||||||
|
color: var(--ink);
|
||||||
|
font-family: 'Lato', sans-serif;
|
||||||
|
font-size: var(--text-body-size);
|
||||||
|
line-height: var(--text-body-line);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── TOP BAR ────────────────────────────────────────────────────
|
||||||
|
Sticky header. z-index:100 sits below mobile panel (z:300)
|
||||||
|
and mobile pill (z:200). Background is --ink (cream) not --paper.
|
||||||
|
Contains: SVS logo SVG (inline) | quote ref + date (DM Mono).
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
.top-bar {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: var(--top-bar-bg);
|
||||||
|
border-bottom: 2px solid var(--top-bar-border);
|
||||||
|
box-shadow: var(--top-bar-shadow);
|
||||||
|
padding: 14px 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.top-bar-inner {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--page-max-width);
|
||||||
|
padding: 0 var(--page-gutter-x);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.top-bar-logo { margin-left: clamp(26px, 5.2vw, 78px); flex-shrink: 0; }
|
||||||
|
.top-bar-right {
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: var(--text-meta-size);
|
||||||
|
letter-spacing: 0.07em;
|
||||||
|
color: var(--top-bar-meta);
|
||||||
|
text-align: right;
|
||||||
|
line-height: 1.55;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── THEME TOGGLE BUTTON ────────────────────────────────────────
|
||||||
|
Sits to the right of the quote ref/date in .top-bar-inner.
|
||||||
|
Slightly darker chip vs the cream top-bar bg so it reads as
|
||||||
|
a distinct control, not noise. Works on both theme top-bars.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
.theme-toggle-btn {
|
||||||
|
background: var(--theme-chip-bg);
|
||||||
|
border: 1px solid var(--theme-chip-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--theme-chip-fg);
|
||||||
|
transition: background var(--transition-fast);
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 14px;
|
||||||
|
box-shadow: var(--theme-chip-shadow);
|
||||||
|
}
|
||||||
|
.theme-toggle-btn:hover { background: var(--theme-chip-hover); }
|
||||||
|
.theme-toggle-btn:active { background: var(--theme-chip-active); }
|
||||||
|
.theme-toggle-btn svg { display: block; }
|
||||||
|
|
||||||
|
|
||||||
2054
SVS-MSP-Calculator-components.css
Normal file
@@ -18,23 +18,177 @@ html {
|
|||||||
--green: #63d8a2;
|
--green: #63d8a2;
|
||||||
--amber: #ffbe68;
|
--amber: #ffbe68;
|
||||||
--glass-header-text: #5f6d7f;
|
--glass-header-text: #5f6d7f;
|
||||||
|
--top-bar-bg: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(252, 255, 255, 0.96) 0%,
|
||||||
|
rgba(244, 249, 255, 0.93) 52%,
|
||||||
|
rgba(231, 240, 251, 0.91) 100%
|
||||||
|
);
|
||||||
|
--top-bar-border: rgba(118, 143, 171, 0.35);
|
||||||
|
--top-bar-meta: var(--glass-header-text);
|
||||||
|
--theme-chip-bg: linear-gradient(180deg, rgba(247, 250, 255, 0.88), rgba(217, 229, 242, 0.82));
|
||||||
|
--theme-chip-hover: linear-gradient(180deg, rgba(252, 254, 255, 0.94), rgba(226, 237, 248, 0.88));
|
||||||
|
--theme-chip-active: linear-gradient(180deg, rgba(226, 236, 248, 0.95), rgba(205, 219, 235, 0.9));
|
||||||
|
--theme-chip-fg: #223142;
|
||||||
|
--theme-chip-border: rgba(83, 117, 150, 0.24);
|
||||||
|
--theme-chip-shadow: 0 10px 24px rgba(6, 18, 31, 0.14);
|
||||||
|
--section-hover-border: rgba(105, 200, 255, 0.34);
|
||||||
|
--section-hover-shadow:
|
||||||
|
-4px 0 0 0 rgba(105, 200, 255, 0.36),
|
||||||
|
0 20px 54px rgba(2, 8, 17, 0.38),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.07);
|
||||||
|
--section-open-border: rgba(105, 200, 255, 0.5);
|
||||||
|
--section-open-shadow:
|
||||||
|
-4px 0 0 0 rgba(105, 200, 255, 0.5),
|
||||||
|
0 22px 58px rgba(2, 8, 17, 0.42),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||||
|
--selection-bg: rgba(105, 200, 255, 0.28);
|
||||||
|
--selection-text: #f8fbff;
|
||||||
|
--top-bar-shadow: 0 8px 24px rgba(7, 18, 33, 0.1);
|
||||||
|
--glass-page-bg:
|
||||||
|
linear-gradient(142deg, #030b14 0%, #071420 20%, #0a1d2c 46%, #081721 72%, #040b13 100%),
|
||||||
|
linear-gradient(128deg, rgba(58, 182, 255, 0.2) 0%, rgba(58, 182, 255, 0) 30%),
|
||||||
|
linear-gradient(148deg, rgba(22, 205, 164, 0.15) 18%, rgba(22, 205, 164, 0) 48%),
|
||||||
|
radial-gradient(circle at 8% 10%, rgba(84, 200, 255, 0.3), transparent 26%),
|
||||||
|
radial-gradient(circle at 28% 34%, rgba(36, 204, 168, 0.22), transparent 22%),
|
||||||
|
radial-gradient(circle at 78% 18%, rgba(58, 166, 255, 0.2), transparent 24%),
|
||||||
|
radial-gradient(circle at 84% 72%, rgba(24, 188, 150, 0.16), transparent 20%),
|
||||||
|
linear-gradient(160deg, rgba(6, 14, 24, 0.74) 0%, rgba(4, 10, 19, 0.84) 100%);
|
||||||
|
--glass-page-overlay:
|
||||||
|
linear-gradient(132deg, rgba(70, 184, 255, 0.15) 0%, rgba(70, 184, 255, 0) 34%),
|
||||||
|
linear-gradient(148deg, rgba(28, 198, 158, 0.12) 18%, rgba(28, 198, 158, 0) 46%),
|
||||||
|
radial-gradient(circle at 16% 18%, rgba(72, 198, 255, 0.12), transparent 24%),
|
||||||
|
radial-gradient(circle at 82% 24%, rgba(24, 188, 150, 0.1), transparent 22%);
|
||||||
|
--glass-page-bg-mobile:
|
||||||
|
linear-gradient(150deg, #030b13 0%, #091521 28%, #0d1b29 58%, #07121b 100%),
|
||||||
|
linear-gradient(136deg, rgba(62, 186, 255, 0.16) 0%, rgba(62, 186, 255, 0) 36%),
|
||||||
|
linear-gradient(152deg, rgba(24, 198, 160, 0.11) 18%, rgba(24, 198, 160, 0) 46%),
|
||||||
|
radial-gradient(circle at 14% 12%, rgba(78, 196, 255, 0.24), transparent 24%),
|
||||||
|
radial-gradient(circle at 70% 22%, rgba(24, 184, 148, 0.16), transparent 20%),
|
||||||
|
radial-gradient(circle at 30% 48%, rgba(42, 162, 255, 0.15), transparent 22%);
|
||||||
|
--glass-page-overlay-mobile:
|
||||||
|
linear-gradient(138deg, rgba(66, 182, 255, 0.13) 0%, rgba(66, 182, 255, 0) 36%),
|
||||||
|
linear-gradient(154deg, rgba(26, 194, 156, 0.1) 18%, rgba(26, 194, 156, 0) 46%),
|
||||||
|
radial-gradient(circle at 78% 24%, rgba(26, 186, 150, 0.08), transparent 22%);
|
||||||
|
--glass-panel-bg: linear-gradient(180deg, rgba(16, 27, 43, 0.76), rgba(9, 17, 29, 0.68));
|
||||||
|
--glass-section-bg: linear-gradient(180deg, rgba(17, 29, 46, 0.74), rgba(9, 17, 29, 0.66));
|
||||||
|
--glass-panel-border: rgba(143, 183, 221, 0.18);
|
||||||
|
--glass-panel-shadow:
|
||||||
|
0 18px 50px rgba(2, 8, 17, 0.32),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||||
|
--glass-section-num: rgba(226, 239, 255, 0.18);
|
||||||
|
--glass-section-num-glow: 0 0 26px rgba(105, 200, 255, 0.1);
|
||||||
|
--glass-heading: #f4f9ff;
|
||||||
|
--glass-heading-soft: #f1f8ff;
|
||||||
|
--glass-client-border: rgba(143, 183, 221, 0.24);
|
||||||
|
--glass-client-placeholder: rgba(159, 179, 201, 0.72);
|
||||||
|
--glass-ghost-bg: rgba(255, 255, 255, 0.04);
|
||||||
|
--glass-ghost-border: rgba(143, 183, 221, 0.18);
|
||||||
|
--glass-ghost-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||||
|
--glass-ghost-hover-bg: rgba(105, 200, 255, 0.12);
|
||||||
|
--glass-ghost-hover-border: rgba(105, 200, 255, 0.3);
|
||||||
|
--glass-ghost-hover-text: #f2f8ff;
|
||||||
|
--glass-group-surface: rgba(5, 11, 21, 0.3);
|
||||||
|
--glass-input-surface: rgba(5, 11, 21, 0.34);
|
||||||
|
--surface-term-wrap: linear-gradient(180deg, rgba(12, 21, 34, 0.62), rgba(8, 15, 26, 0.54));
|
||||||
|
--surface-term-tile: rgba(255, 255, 255, 0.02);
|
||||||
|
--surface-term-tile-hover: rgba(105, 200, 255, 0.08);
|
||||||
|
--surface-term-tile-active:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0) 42%),
|
||||||
|
linear-gradient(135deg, rgba(62, 142, 190, 0.58) 0%, rgba(42, 107, 156, 0.62) 58%, rgba(24, 70, 118, 0.68) 100%);
|
||||||
|
--border-term-wrap: rgba(143, 183, 221, 0.18);
|
||||||
|
--border-term-tile-active: rgba(105, 200, 255, 0.16);
|
||||||
|
--shadow-term-wrap: inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||||
|
--shadow-term-tile-active:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.08),
|
||||||
|
inset 0 -1px 0 rgba(3, 10, 20, 0.26);
|
||||||
|
--text-term-name: var(--muted);
|
||||||
|
--text-term-name-active: var(--text-on-accent);
|
||||||
|
--text-term-sub: var(--muted);
|
||||||
|
--text-term-sub-active: var(--text-on-accent);
|
||||||
|
--text-term-discount: #f2f8ff;
|
||||||
|
--text-term-discount-active: var(--text-on-accent);
|
||||||
|
--surface-best-value: rgba(99, 216, 162, 0.12);
|
||||||
|
--border-best-value: rgba(99, 216, 162, 0.26);
|
||||||
|
--text-best-value: #baf0d3;
|
||||||
|
--surface-best-value-active: rgba(255, 255, 255, 0.16);
|
||||||
|
--border-best-value-active: rgba(255, 255, 255, 0.34);
|
||||||
|
--text-best-value-active: #ffffff;
|
||||||
|
--text-pill-savings-active: #a8f0c8;
|
||||||
|
--glass-input-border: rgba(143, 183, 221, 0.2);
|
||||||
|
--glass-input-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
|
||||||
|
--glass-input-focus-border: rgba(105, 200, 255, 0.55);
|
||||||
|
--glass-input-focus-shadow: 0 0 0 3px rgba(105, 200, 255, 0.16);
|
||||||
|
--glass-cta-gradient: linear-gradient(135deg, #76dbff 0%, #48b8ff 54%, #268ee3 100%);
|
||||||
|
--glass-cta-checked-bg:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 42%),
|
||||||
|
linear-gradient(135deg, rgba(94, 206, 255, 0.72) 0%, rgba(55, 156, 233, 0.78) 58%, rgba(28, 104, 190, 0.82) 100%);
|
||||||
|
--glass-cta-checked-shadow:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.12),
|
||||||
|
inset 0 -1px 0 rgba(3, 10, 20, 0.2);
|
||||||
|
--glass-sidebar-header-bg:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0) 42%),
|
||||||
|
linear-gradient(135deg, rgba(63, 133, 175, 0.7) 0%, rgba(47, 113, 156, 0.68) 58%, rgba(31, 82, 126, 0.66) 100%);
|
||||||
|
--glass-sidebar-header-shadow:
|
||||||
|
inset 0 -1px 0 rgba(255, 255, 255, 0.08),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
||||||
|
--glass-switch-track: rgba(255, 255, 255, 0.12);
|
||||||
|
--surface-switch-off: rgba(255, 255, 255, 0.15);
|
||||||
|
--surface-switch-on: var(--green);
|
||||||
|
--glass-switch-knob: rgba(250, 252, 255, 0.95);
|
||||||
|
--glass-switch-shadow: 0 2px 8px rgba(3, 9, 18, 0.28);
|
||||||
|
--glass-selected-bg: rgba(105, 200, 255, 0.12);
|
||||||
|
--glass-selected-border: rgba(105, 200, 255, 0.28);
|
||||||
|
--glass-selected-text: #f3fbff;
|
||||||
|
--surface-addon-hover: rgba(105, 200, 255, 0.08);
|
||||||
|
--border-addon-hover: rgba(105, 200, 255, 0.24);
|
||||||
|
--glass-feature-bg: linear-gradient(180deg, rgba(19, 31, 49, 0.8), rgba(10, 18, 30, 0.72));
|
||||||
|
--glass-success-bg: linear-gradient(180deg, rgba(15, 48, 42, 0.82), rgba(10, 35, 30, 0.72));
|
||||||
|
--glass-success-border: rgba(99, 216, 162, 0.26);
|
||||||
|
--glass-danger-bg: linear-gradient(180deg, rgba(62, 23, 34, 0.82), rgba(41, 14, 22, 0.74));
|
||||||
|
--glass-danger-border: rgba(230, 117, 138, 0.26);
|
||||||
|
--glass-danger-text: #ffb7c6;
|
||||||
|
--glass-warning-bg: linear-gradient(180deg, rgba(66, 41, 12, 0.84), rgba(43, 27, 8, 0.76));
|
||||||
|
--glass-warning-border: rgba(255, 190, 104, 0.26);
|
||||||
|
--glass-addon-active-bg: rgba(105, 200, 255, 0.16);
|
||||||
|
--glass-addon-active-border: rgba(105, 200, 255, 0.28);
|
||||||
|
--glass-addon-active-text: #dff3ff;
|
||||||
|
--glass-pill-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||||
|
--glass-divider: rgba(143, 183, 221, 0.14);
|
||||||
|
--glass-sidebar-placeholder: rgba(255, 255, 255, 0.76);
|
||||||
|
--glass-compare-bg: linear-gradient(180deg, rgba(14, 24, 39, 0.72), rgba(9, 16, 29, 0.62));
|
||||||
|
--glass-compare-success: rgba(99, 216, 162, 0.14);
|
||||||
|
--glass-compare-warning: rgba(255, 190, 104, 0.14);
|
||||||
|
--glass-export-shadow: 0 14px 28px rgba(29, 108, 186, 0.26);
|
||||||
|
--glass-reset-text: #dceefe;
|
||||||
|
--glass-reset-hover-bg: rgba(105, 200, 255, 0.1);
|
||||||
|
--glass-reset-hover-border: rgba(105, 200, 255, 0.32);
|
||||||
|
--glass-reset-hover-text: #f2f8ff;
|
||||||
|
--sky: #7dd3fc;
|
||||||
|
--glass-modal-backdrop: rgba(2, 7, 15, 0.72);
|
||||||
|
--glass-modal-bg: linear-gradient(180deg, rgba(18, 29, 46, 0.86), rgba(10, 17, 29, 0.8));
|
||||||
|
--glass-pitch-bg: linear-gradient(180deg, rgba(14, 25, 40, 0.72), rgba(9, 16, 28, 0.68));
|
||||||
|
--glass-pitch-footer-bg: linear-gradient(135deg, rgba(11, 42, 34, 0.88), rgba(7, 28, 22, 0.86));
|
||||||
|
--glass-pitch-footer-text: #8ee8bf;
|
||||||
|
--glass-mobile-sheet-bg: linear-gradient(180deg, rgba(12, 21, 34, 0.92), rgba(8, 14, 24, 0.9));
|
||||||
|
--glass-mobile-row-bg: rgba(10, 18, 30, 0.84);
|
||||||
|
--glass-top-bar-mobile-bg: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(251, 255, 255, 0.95) 0%,
|
||||||
|
rgba(241, 247, 254, 0.91) 56%,
|
||||||
|
rgba(228, 238, 250, 0.89) 100%
|
||||||
|
);
|
||||||
|
--glass-theme-toggle-mobile-shadow: 0 8px 20px rgba(6, 18, 31, 0.12);
|
||||||
|
--glass-mobile-panel-shadow:
|
||||||
|
0 14px 36px rgba(2, 8, 17, 0.28),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||||
|
--sidebar-zone-services: rgba(105, 200, 255, 0.03);
|
||||||
|
--sidebar-zone-invoice: rgba(105, 200, 255, 0.05);
|
||||||
|
--sidebar-zone-value: rgba(105, 200, 255, 0.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background:
|
background: var(--glass-page-bg);
|
||||||
linear-gradient(138deg, #03070f 0%, #071120 14%, #10152a 30%, #1a1324 48%, #1a0d17 64%, #0a111b 82%, #050912 100%),
|
|
||||||
linear-gradient(125deg, rgba(44, 138, 255, 0.22) 0%, rgba(44, 138, 255, 0) 26%),
|
|
||||||
linear-gradient(142deg, rgba(18, 194, 152, 0.16) 18%, rgba(18, 194, 152, 0) 42%),
|
|
||||||
linear-gradient(154deg, rgba(118, 72, 224, 0.16) 34%, rgba(118, 72, 224, 0) 58%),
|
|
||||||
linear-gradient(164deg, rgba(198, 46, 86, 0.16) 52%, rgba(198, 46, 86, 0) 74%),
|
|
||||||
radial-gradient(circle at 8% 10%, rgba(72, 178, 255, 0.34), transparent 26%),
|
|
||||||
radial-gradient(circle at 26% 30%, rgba(32, 196, 144, 0.24), transparent 22%),
|
|
||||||
radial-gradient(circle at 58% 18%, rgba(116, 82, 222, 0.24), transparent 22%),
|
|
||||||
radial-gradient(circle at 88% 16%, rgba(200, 60, 92, 0.24), transparent 20%),
|
|
||||||
radial-gradient(circle at 76% 56%, rgba(42, 126, 255, 0.2), transparent 22%),
|
|
||||||
radial-gradient(circle at 24% 78%, rgba(112, 44, 138, 0.22), transparent 22%),
|
|
||||||
radial-gradient(circle at 92% 86%, rgba(18, 168, 132, 0.2), transparent 20%),
|
|
||||||
linear-gradient(160deg, rgba(7, 14, 25, 0.72) 0%, rgba(5, 10, 19, 0.8) 100%);
|
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -45,11 +199,7 @@ body::before {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background:
|
background: var(--glass-page-overlay);
|
||||||
linear-gradient(132deg, rgba(58, 154, 255, 0.16) 0%, rgba(58, 154, 255, 0) 34%),
|
|
||||||
linear-gradient(145deg, rgba(28, 201, 154, 0.12) 18%, rgba(28, 201, 154, 0) 44%),
|
|
||||||
linear-gradient(156deg, rgba(122, 84, 232, 0.12) 38%, rgba(122, 84, 232, 0) 60%),
|
|
||||||
linear-gradient(168deg, rgba(214, 68, 104, 0.12) 58%, rgba(214, 68, 104, 0) 78%);
|
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
@@ -61,30 +211,17 @@ body::before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
background: rgba(105, 200, 255, 0.28);
|
background: var(--selection-bg);
|
||||||
color: #f8fbff;
|
color: var(--selection-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-bar {
|
.top-bar {
|
||||||
position: sticky !important;
|
border-bottom-width: 1px !important;
|
||||||
top: 0 !important;
|
box-shadow: var(--top-bar-shadow) !important;
|
||||||
z-index: 100 !important;
|
|
||||||
background: linear-gradient(
|
|
||||||
180deg,
|
|
||||||
rgba(252, 255, 255, 0.96) 0%,
|
|
||||||
rgba(244, 249, 255, 0.93) 52%,
|
|
||||||
rgba(231, 240, 251, 0.91) 100%
|
|
||||||
) !important;
|
|
||||||
border-bottom: 1px solid rgba(118, 143, 171, 0.35) !important;
|
|
||||||
box-shadow: 0 8px 24px rgba(7, 18, 33, 0.1) !important;
|
|
||||||
backdrop-filter: blur(18px) saturate(135%);
|
backdrop-filter: blur(18px) saturate(135%);
|
||||||
-webkit-backdrop-filter: blur(18px) saturate(135%);
|
-webkit-backdrop-filter: blur(18px) saturate(135%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-bar-right {
|
|
||||||
color: var(--glass-header-text) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1101px) {
|
@media (min-width: 1101px) {
|
||||||
.outer {
|
.outer {
|
||||||
padding-top: var(--sidebar-top-gap) !important;
|
padding-top: var(--sidebar-top-gap) !important;
|
||||||
@@ -95,66 +232,32 @@ body::before {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-toggle-btn {
|
|
||||||
background: linear-gradient(180deg, rgba(247, 250, 255, 0.88), rgba(217, 229, 242, 0.82)) !important;
|
|
||||||
border: 1px solid rgba(83, 117, 150, 0.24) !important;
|
|
||||||
color: #223142 !important;
|
|
||||||
box-shadow: 0 10px 24px rgba(6, 18, 31, 0.14) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-toggle-btn:hover {
|
|
||||||
background: linear-gradient(180deg, rgba(252, 254, 255, 0.94), rgba(226, 237, 248, 0.88)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-toggle-btn:active {
|
|
||||||
background: linear-gradient(180deg, rgba(226, 236, 248, 0.95), rgba(205, 219, 235, 0.9)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section,
|
.section,
|
||||||
.quote-settings-bar,
|
.quote-settings-bar,
|
||||||
.sidebar,
|
.sidebar,
|
||||||
.sidebar-utility .btn-reset-quote,
|
.sidebar-utility .btn-export,
|
||||||
|
.sidebar-utility .btn-export-secondary,
|
||||||
.mobile-panel-sheet,
|
.mobile-panel-sheet,
|
||||||
.mobile-panel-close-row,
|
.mobile-panel-close-row,
|
||||||
.mobile-panel-actions,
|
.mobile-panel-actions,
|
||||||
.confirm-modal-card,
|
.confirm-modal-card,
|
||||||
.feature-card,
|
|
||||||
.addon-row,
|
|
||||||
.vs-comparison-wrap,
|
.vs-comparison-wrap,
|
||||||
.export-wrap,
|
.export-wrap,
|
||||||
.pitch-inner {
|
.pitch-inner {
|
||||||
background: linear-gradient(180deg, rgba(16, 27, 43, 0.76), rgba(9, 17, 29, 0.68)) !important;
|
background: var(--glass-panel-bg) !important;
|
||||||
border-color: rgba(143, 183, 221, 0.18) !important;
|
border-color: var(--glass-panel-border) !important;
|
||||||
box-shadow:
|
box-shadow: var(--glass-panel-shadow) !important;
|
||||||
0 18px 50px rgba(2, 8, 17, 0.32),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.06) !important;
|
|
||||||
backdrop-filter: blur(18px) saturate(135%);
|
backdrop-filter: blur(18px) saturate(135%);
|
||||||
-webkit-backdrop-filter: blur(18px) saturate(135%);
|
-webkit-backdrop-filter: blur(18px) saturate(135%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
background: linear-gradient(180deg, rgba(17, 29, 46, 0.74), rgba(9, 17, 29, 0.66)) !important;
|
background: var(--glass-section-bg) !important;
|
||||||
}
|
|
||||||
|
|
||||||
.section:hover {
|
|
||||||
border-color: rgba(105, 200, 255, 0.34) !important;
|
|
||||||
box-shadow:
|
|
||||||
-4px 0 0 0 rgba(105, 200, 255, 0.36),
|
|
||||||
0 20px 54px rgba(2, 8, 17, 0.38),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.07) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sec-open {
|
|
||||||
border-color: rgba(105, 200, 255, 0.5) !important;
|
|
||||||
box-shadow:
|
|
||||||
-4px 0 0 0 rgba(105, 200, 255, 0.5),
|
|
||||||
0 22px 58px rgba(2, 8, 17, 0.42),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.08) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-num {
|
.section-num {
|
||||||
color: rgba(226, 239, 255, 0.18) !important;
|
color: var(--glass-section-num) !important;
|
||||||
text-shadow: 0 0 26px rgba(105, 200, 255, 0.1);
|
text-shadow: var(--glass-section-num-glow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title,
|
.section-title,
|
||||||
@@ -162,7 +265,7 @@ body::before {
|
|||||||
.sidebar-mrr,
|
.sidebar-mrr,
|
||||||
.sidebar-line .val,
|
.sidebar-line .val,
|
||||||
.vs-svs-label {
|
.vs-svs-label {
|
||||||
color: #f4f9ff !important;
|
color: var(--glass-heading) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-subtitle,
|
.section-subtitle,
|
||||||
@@ -184,12 +287,12 @@ body::before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.client-input {
|
.client-input {
|
||||||
color: #f4f9ff !important;
|
color: var(--glass-heading) !important;
|
||||||
border-bottom-color: rgba(143, 183, 221, 0.24) !important;
|
border-bottom-color: var(--glass-client-border) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.client-input::placeholder {
|
.client-input::placeholder {
|
||||||
color: rgba(159, 179, 201, 0.72) !important;
|
color: var(--glass-client-placeholder) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sec-chevron,
|
.sec-chevron,
|
||||||
@@ -199,10 +302,10 @@ body::before {
|
|||||||
.btn-export-secondary,
|
.btn-export-secondary,
|
||||||
.mobile-panel-close,
|
.mobile-panel-close,
|
||||||
.nudge-nav-btn {
|
.nudge-nav-btn {
|
||||||
background: rgba(255, 255, 255, 0.04) !important;
|
background: var(--glass-ghost-bg) !important;
|
||||||
border-color: rgba(143, 183, 221, 0.18) !important;
|
border-color: var(--glass-ghost-border) !important;
|
||||||
color: var(--muted) !important;
|
color: var(--muted) !important;
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
box-shadow: var(--glass-ghost-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sec-open .sec-chevron,
|
.sec-open .sec-chevron,
|
||||||
@@ -212,16 +315,16 @@ body::before {
|
|||||||
.btn-export-secondary:hover,
|
.btn-export-secondary:hover,
|
||||||
.nudge-nav-btn:hover,
|
.nudge-nav-btn:hover,
|
||||||
.mobile-panel-close:hover {
|
.mobile-panel-close:hover {
|
||||||
background: rgba(105, 200, 255, 0.12) !important;
|
background: var(--glass-ghost-hover-bg) !important;
|
||||||
border-color: rgba(105, 200, 255, 0.3) !important;
|
border-color: var(--glass-ghost-hover-border) !important;
|
||||||
color: #f2f8ff !important;
|
color: var(--glass-ghost-hover-text) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pill-toggle,
|
.pill-toggle,
|
||||||
.tier-seg-wrap,
|
.tier-seg-wrap,
|
||||||
.qs-fee-input-wrap {
|
.qs-fee-input-wrap {
|
||||||
background: rgba(5, 11, 21, 0.3) !important;
|
background: var(--glass-group-surface) !important;
|
||||||
border-color: rgba(143, 183, 221, 0.18) !important;
|
border-color: var(--glass-ghost-border) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pill-toggle label,
|
.pill-toggle label,
|
||||||
@@ -232,7 +335,7 @@ body::before {
|
|||||||
.pill-toggle label:hover,
|
.pill-toggle label:hover,
|
||||||
.tier-seg:hover,
|
.tier-seg:hover,
|
||||||
.addon-row:hover {
|
.addon-row:hover {
|
||||||
background: rgba(255, 255, 255, 0.04) !important;
|
background: var(--glass-ghost-bg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier-seg.active,
|
.tier-seg.active,
|
||||||
@@ -240,28 +343,20 @@ body::before {
|
|||||||
.confirm-btn-danger,
|
.confirm-btn-danger,
|
||||||
.mobile-quote-pill,
|
.mobile-quote-pill,
|
||||||
.progress-fill {
|
.progress-fill {
|
||||||
background: linear-gradient(135deg, #7ad6ff 0%, #4da8ff 52%, #337dff 100%) !important;
|
background: var(--glass-cta-gradient) !important;
|
||||||
color: #ffffff !important;
|
color: var(--text-on-accent) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pill-toggle input:checked + label {
|
.pill-toggle input:checked + label {
|
||||||
background:
|
background: var(--glass-cta-checked-bg) !important;
|
||||||
linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 42%),
|
color: var(--text-on-accent) !important;
|
||||||
linear-gradient(135deg, rgba(103, 182, 248, 0.7) 0%, rgba(63, 122, 210, 0.76) 58%, rgba(45, 82, 155, 0.82) 100%) !important;
|
box-shadow: var(--glass-cta-checked-shadow) !important;
|
||||||
color: #ffffff !important;
|
|
||||||
box-shadow:
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.12),
|
|
||||||
inset 0 -1px 0 rgba(3, 10, 20, 0.2) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
background:
|
background: var(--glass-sidebar-header-bg) !important;
|
||||||
linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 44%),
|
color: var(--text-on-accent) !important;
|
||||||
linear-gradient(135deg, rgba(105, 188, 250, 0.82) 0%, rgba(71, 132, 224, 0.8) 54%, rgba(67, 72, 156, 0.76) 100%) !important;
|
box-shadow: var(--glass-sidebar-header-shadow) !important;
|
||||||
color: #ffffff !important;
|
|
||||||
box-shadow:
|
|
||||||
inset 0 -1px 0 rgba(255, 255, 255, 0.08),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.12) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pill-toggle input:checked + label .pill-desc,
|
.pill-toggle input:checked + label .pill-desc,
|
||||||
@@ -269,7 +364,7 @@ body::before {
|
|||||||
.tier-seg.active .tier-name,
|
.tier-seg.active .tier-name,
|
||||||
.tier-seg.active .tier-price,
|
.tier-seg.active .tier-price,
|
||||||
.tier-seg.active .tier-sub {
|
.tier-seg.active .tier-sub {
|
||||||
color: #ffffff !important;
|
color: var(--text-on-accent-strong) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.num-input,
|
.num-input,
|
||||||
@@ -277,77 +372,89 @@ body::before {
|
|||||||
.qs-fee-dollar,
|
.qs-fee-dollar,
|
||||||
.qs-fee-input-wrap,
|
.qs-fee-input-wrap,
|
||||||
.mobile-panel-sheet .sidebar-body {
|
.mobile-panel-sheet .sidebar-body {
|
||||||
background: rgba(5, 11, 21, 0.34) !important;
|
background: var(--glass-input-surface) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.num-input,
|
.num-input,
|
||||||
.qs-fee-input {
|
.qs-fee-input {
|
||||||
border-color: rgba(143, 183, 221, 0.2) !important;
|
border-color: var(--glass-input-border) !important;
|
||||||
color: #f2f8ff !important;
|
color: var(--glass-ghost-hover-text) !important;
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
|
box-shadow: var(--glass-input-inset-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.num-input:focus,
|
.num-input:focus,
|
||||||
.qs-fee-input:focus,
|
.qs-fee-input:focus,
|
||||||
.client-input:focus-visible {
|
.client-input:focus-visible {
|
||||||
border-color: rgba(105, 200, 255, 0.55) !important;
|
border-color: var(--glass-input-focus-border) !important;
|
||||||
box-shadow: 0 0 0 3px rgba(105, 200, 255, 0.16) !important;
|
box-shadow: var(--glass-input-focus-shadow) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qs-switch {
|
.qs-switch {
|
||||||
background: rgba(255, 255, 255, 0.12) !important;
|
background: var(--surface-switch-off) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.qs-toggle-row input:checked ~ .qs-switch {
|
||||||
|
background: var(--surface-switch-on) !important;
|
||||||
|
}
|
||||||
.qs-switch::after {
|
.qs-switch::after {
|
||||||
background: rgba(250, 252, 255, 0.95) !important;
|
background: var(--glass-switch-knob) !important;
|
||||||
box-shadow: 0 2px 8px rgba(3, 9, 18, 0.28);
|
box-shadow: var(--glass-switch-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-row.selected {
|
.addon-row.selected {
|
||||||
background: rgba(105, 200, 255, 0.12) !important;
|
background: var(--glass-selected-bg) !important;
|
||||||
border-color: rgba(105, 200, 255, 0.28) !important;
|
border-color: var(--glass-selected-border) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-row.selected .addon-name,
|
.addon-row.selected .addon-name,
|
||||||
.addon-row.selected .addon-price {
|
.addon-row.selected .addon-price,
|
||||||
color: #9edcff !important;
|
.addon-row.selected .addon-desc {
|
||||||
|
color: var(--glass-selected-text) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-card {
|
.feature-card {
|
||||||
background: linear-gradient(180deg, rgba(19, 31, 49, 0.8), rgba(10, 18, 30, 0.72)) !important;
|
background: var(--glass-feature-bg) !important;
|
||||||
|
border-color: var(--glass-panel-border) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.addon-row {
|
||||||
|
background: transparent !important;
|
||||||
|
border-color: transparent !important;
|
||||||
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.callout-green,
|
.callout-green,
|
||||||
.nudge-banner.green,
|
.nudge-banner.green,
|
||||||
.admin-waive-savings {
|
.admin-waive-savings {
|
||||||
background: linear-gradient(180deg, rgba(15, 48, 42, 0.82), rgba(10, 35, 30, 0.72)) !important;
|
background: var(--glass-success-bg) !important;
|
||||||
border-color: rgba(99, 216, 162, 0.26) !important;
|
border-color: var(--glass-success-border) !important;
|
||||||
color: var(--green) !important;
|
color: var(--green) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.callout-red {
|
.callout-red {
|
||||||
background: linear-gradient(180deg, rgba(62, 23, 34, 0.82), rgba(41, 14, 22, 0.74)) !important;
|
background: var(--glass-danger-bg) !important;
|
||||||
border-color: rgba(230, 117, 138, 0.26) !important;
|
border-color: var(--glass-danger-border) !important;
|
||||||
color: #ffb7c6 !important;
|
color: var(--glass-danger-text) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nudge-banner.amber,
|
.nudge-banner.amber,
|
||||||
.admin-fee-waived-badge {
|
.admin-fee-waived-badge {
|
||||||
background: linear-gradient(180deg, rgba(66, 41, 12, 0.84), rgba(43, 27, 8, 0.76)) !important;
|
background: var(--glass-warning-bg) !important;
|
||||||
border-color: rgba(255, 190, 104, 0.26) !important;
|
border-color: var(--glass-warning-border) !important;
|
||||||
color: var(--amber) !important;
|
color: var(--amber) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-waive-savings,
|
.admin-waive-savings,
|
||||||
.admin-fee-waived-badge,
|
.admin-fee-waived-badge,
|
||||||
.addon-preview-pill.active {
|
.addon-preview-pill.active {
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
box-shadow: var(--glass-pill-inset-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-preview-pill.active {
|
.addon-preview-pill.active {
|
||||||
background: rgba(105, 200, 255, 0.16) !important;
|
background: var(--glass-addon-active-bg) !important;
|
||||||
border-color: rgba(105, 200, 255, 0.28) !important;
|
border-color: var(--glass-addon-active-border) !important;
|
||||||
color: #dff3ff !important;
|
color: var(--glass-addon-active-text) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsible-header,
|
.collapsible-header,
|
||||||
@@ -359,12 +466,12 @@ body::before {
|
|||||||
.export-wrap,
|
.export-wrap,
|
||||||
.mobile-panel-close-row,
|
.mobile-panel-close-row,
|
||||||
.mobile-panel-actions {
|
.mobile-panel-actions {
|
||||||
border-color: rgba(143, 183, 221, 0.14) !important;
|
border-color: var(--glass-divider) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-title,
|
.sidebar-title,
|
||||||
.sidebar-client.placeholder {
|
.sidebar-client.placeholder {
|
||||||
color: rgba(255, 255, 255, 0.76) !important;
|
color: var(--glass-sidebar-placeholder) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-body,
|
.sidebar-body,
|
||||||
@@ -386,108 +493,119 @@ body::before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vs-comparison-wrap {
|
.vs-comparison-wrap {
|
||||||
background: linear-gradient(180deg, rgba(14, 24, 39, 0.72), rgba(9, 16, 29, 0.62)) !important;
|
background: var(--glass-compare-bg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs-save-green td {
|
.vs-save-green td {
|
||||||
background: rgba(99, 216, 162, 0.14) !important;
|
background: var(--glass-compare-success) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs-save-amber td {
|
.vs-save-amber td {
|
||||||
background: rgba(255, 190, 104, 0.14) !important;
|
background: var(--glass-compare-warning) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.export-wrap {
|
.export-wrap {
|
||||||
border-top: 1px solid rgba(143, 183, 221, 0.14) !important;
|
border-top: 1px solid var(--glass-divider) !important;
|
||||||
|
}
|
||||||
|
body.sidebar-focus-open .side-col .export-wrap {
|
||||||
|
background: transparent !important;
|
||||||
|
border-color: transparent !important;
|
||||||
|
border-top: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
backdrop-filter: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-export {
|
.btn-export {
|
||||||
box-shadow: 0 14px 28px rgba(29, 108, 186, 0.26) !important;
|
box-shadow: var(--glass-export-shadow) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-export:hover,
|
.btn-export:hover,
|
||||||
.mobile-quote-pill:hover {
|
.mobile-quote-pill:hover {
|
||||||
filter: brightness(1.05);
|
filter: brightness(1.2) !important;
|
||||||
|
box-shadow: 0 4px 16px rgba(99, 127, 136, 0.35) !important;
|
||||||
|
}
|
||||||
|
.btn-export:active {
|
||||||
|
filter: brightness(0.9) !important;
|
||||||
|
transform: scale(0.97);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-reset-quote {
|
.btn-reset-quote {
|
||||||
color: #dceefe !important;
|
color: var(--glass-reset-text) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-reset-quote:hover {
|
.btn-reset-quote:hover {
|
||||||
background: rgba(105, 200, 255, 0.1) !important;
|
background: var(--glass-reset-hover-bg) !important;
|
||||||
border-color: rgba(105, 200, 255, 0.32) !important;
|
border-color: var(--glass-reset-hover-border) !important;
|
||||||
color: #f2f8ff !important;
|
color: var(--glass-reset-hover-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-import-quote {
|
||||||
|
color: var(--glass-reset-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-import-quote:hover {
|
||||||
|
background: color-mix(in srgb, var(--sky) 10%, transparent) !important;
|
||||||
|
border-color: color-mix(in srgb, var(--sky) 35%, transparent) !important;
|
||||||
|
color: var(--sky) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-modal-backdrop {
|
.confirm-modal-backdrop {
|
||||||
background: rgba(2, 7, 15, 0.72) !important;
|
background: var(--glass-modal-backdrop) !important;
|
||||||
backdrop-filter: blur(10px) saturate(125%);
|
backdrop-filter: blur(10px) saturate(125%);
|
||||||
-webkit-backdrop-filter: blur(10px) saturate(125%);
|
-webkit-backdrop-filter: blur(10px) saturate(125%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-modal-card {
|
.confirm-modal-card {
|
||||||
background: linear-gradient(180deg, rgba(18, 29, 46, 0.86), rgba(10, 17, 29, 0.8)) !important;
|
background: var(--glass-modal-bg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pitch-inner {
|
.pitch-inner {
|
||||||
background: linear-gradient(180deg, rgba(14, 25, 40, 0.72), rgba(9, 16, 28, 0.68)) !important;
|
background: var(--glass-pitch-bg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pitch-title {
|
.pitch-title {
|
||||||
color: #f1f8ff !important;
|
color: var(--glass-heading-soft) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pitch-footer {
|
.pitch-footer {
|
||||||
background: linear-gradient(135deg, rgba(11, 42, 34, 0.88), rgba(7, 28, 22, 0.86)) !important;
|
background: var(--glass-pitch-footer-bg) !important;
|
||||||
color: #8ee8bf !important;
|
color: var(--glass-pitch-footer-text) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-panel-sheet {
|
.mobile-panel-sheet {
|
||||||
background: linear-gradient(180deg, rgba(12, 21, 34, 0.92), rgba(8, 14, 24, 0.9)) !important;
|
background: var(--glass-mobile-sheet-bg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-panel-close-row,
|
.mobile-panel-close-row,
|
||||||
.mobile-panel-actions {
|
.mobile-panel-actions {
|
||||||
background: rgba(10, 18, 30, 0.84) !important;
|
background: var(--glass-mobile-row-bg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
@media (max-width: 1100px) {
|
||||||
.top-bar {
|
.top-bar {
|
||||||
background: linear-gradient(
|
background: var(--glass-top-bar-mobile-bg) !important;
|
||||||
180deg,
|
}
|
||||||
rgba(251, 255, 255, 0.95) 0%,
|
}
|
||||||
rgba(241, 247, 254, 0.91) 56%,
|
|
||||||
rgba(228, 238, 250, 0.89) 100%
|
@media (max-width: 1100px) {
|
||||||
) !important;
|
body {
|
||||||
|
background-attachment: scroll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
body {
|
body {
|
||||||
background:
|
background: var(--glass-page-bg-mobile);
|
||||||
linear-gradient(150deg, #03070f 0%, #0a1220 24%, #171226 54%, #110d18 76%, #070c14 100%),
|
background-attachment: scroll;
|
||||||
linear-gradient(132deg, rgba(58, 154, 255, 0.16) 0%, rgba(58, 154, 255, 0) 34%),
|
|
||||||
linear-gradient(150deg, rgba(28, 201, 154, 0.12) 18%, rgba(28, 201, 154, 0) 44%),
|
|
||||||
linear-gradient(162deg, rgba(214, 68, 104, 0.11) 48%, rgba(214, 68, 104, 0) 72%),
|
|
||||||
radial-gradient(circle at 16% 12%, rgba(72, 178, 255, 0.24), transparent 24%),
|
|
||||||
radial-gradient(circle at 72% 20%, rgba(189, 58, 92, 0.18), transparent 20%),
|
|
||||||
radial-gradient(circle at 34% 46%, rgba(32, 196, 144, 0.18), transparent 22%),
|
|
||||||
radial-gradient(circle at 74% 60%, rgba(116, 82, 222, 0.16), transparent 20%);
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body::before {
|
body::before {
|
||||||
background:
|
background: var(--glass-page-overlay-mobile);
|
||||||
linear-gradient(138deg, rgba(58, 154, 255, 0.14) 0%, rgba(58, 154, 255, 0) 34%),
|
|
||||||
linear-gradient(154deg, rgba(28, 201, 154, 0.1) 18%, rgba(28, 201, 154, 0) 46%),
|
|
||||||
linear-gradient(166deg, rgba(122, 84, 232, 0.1) 36%, rgba(122, 84, 232, 0) 58%),
|
|
||||||
linear-gradient(174deg, rgba(214, 68, 104, 0.1) 56%, rgba(214, 68, 104, 0) 76%);
|
|
||||||
opacity: 0.82;
|
opacity: 0.82;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-toggle-btn {
|
.theme-toggle-btn {
|
||||||
box-shadow: 0 8px 20px rgba(6, 18, 31, 0.12) !important;
|
box-shadow: var(--glass-theme-toggle-mobile-shadow) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section,
|
.section,
|
||||||
@@ -495,8 +613,6 @@ body::before {
|
|||||||
.sidebar,
|
.sidebar,
|
||||||
.mobile-panel-sheet,
|
.mobile-panel-sheet,
|
||||||
.confirm-modal-card {
|
.confirm-modal-card {
|
||||||
box-shadow:
|
box-shadow: var(--glass-mobile-panel-shadow) !important;
|
||||||
0 14px 36px rgba(2, 8, 17, 0.28),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.05) !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
183
SVS-MSP-Calculator-layout.css
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
/* SVS MSP Calculator - Layout */
|
||||||
|
/* Extracted during Phase 5 to keep the HTML shell stable while splitting the monolithic stylesheet. */
|
||||||
|
/* ── PAGE LAYOUT ────────────────────────────────────────────────
|
||||||
|
.outer — CSS grid driven by shared desktop column tokens
|
||||||
|
(currently 3/5 main, 2/5 sidebar) plus shared max width
|
||||||
|
.main-col — left: sections I–VI stacked vertically
|
||||||
|
.side-col — right: sticky sidebar (desktop only; hidden ≤1100px)
|
||||||
|
Roman numeral .section-num floats LEFT outside .section via
|
||||||
|
position:absolute + a tokenized negative left offset.
|
||||||
|
This requires .section to have position:relative + a matching
|
||||||
|
tokenized left margin.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
.outer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--layout-main-col) var(--layout-side-col);
|
||||||
|
gap: var(--layout-column-gap);
|
||||||
|
padding: var(--sidebar-top-gap) var(--page-gutter-x) 44px;
|
||||||
|
max-width: var(--page-max-width);
|
||||||
|
margin: 0 auto;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
.main-col { display: flex; flex-direction: column; gap: clamp(16px, 1.5vw, 24px); container-type: inline-size; }
|
||||||
|
.side-col { position: sticky; top: var(--sidebar-sticky-top); z-index: 10; align-self: start; }
|
||||||
|
.sidebar-utility { margin-bottom: var(--sidebar-stack-gap); display: flex; flex-direction: column; gap: 8px; }
|
||||||
|
.btn-reset-quote,
|
||||||
|
.btn-import-quote {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--surface-sidebar-utility);
|
||||||
|
border: 1px solid var(--surface-sidebar-utility-border);
|
||||||
|
border-radius: var(--radius-control);
|
||||||
|
min-height: var(--control-min-height);
|
||||||
|
padding: var(--control-pad-y) var(--control-pad-x);
|
||||||
|
color: var(--muted);
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
letter-spacing: 0.09em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast), transform 0.1s;
|
||||||
|
}
|
||||||
|
.btn-reset-quote:active,
|
||||||
|
.btn-import-quote:active { transform: translateY(1px); }
|
||||||
|
.btn-reset-quote:hover {
|
||||||
|
background: color-mix(in srgb, var(--amber) 8%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--amber) 38%, transparent);
|
||||||
|
color: var(--amber);
|
||||||
|
}
|
||||||
|
.btn-import-quote:hover {
|
||||||
|
background: color-mix(in srgb, var(--sky) 8%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--sky) 38%, transparent);
|
||||||
|
color: var(--sky);
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-modal {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 400;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
.confirm-modal.open {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.confirm-modal-backdrop {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: var(--surface-backdrop);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
-webkit-backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
.confirm-modal-card {
|
||||||
|
position: relative;
|
||||||
|
width: min(460px, calc(100% - 32px));
|
||||||
|
margin: 12vh auto 0;
|
||||||
|
background: var(--surface-modal);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 22px 22px 20px;
|
||||||
|
box-shadow: var(--shadow-modal);
|
||||||
|
}
|
||||||
|
.confirm-modal-eyebrow {
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--amber);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.confirm-modal-title {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
color: var(--ink);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.confirm-modal-copy {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
.confirm-modal-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.confirm-btn {
|
||||||
|
border-radius: var(--radius-control);
|
||||||
|
min-height: var(--control-min-height);
|
||||||
|
padding: var(--control-pad-y) var(--control-pad-x);
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast), transform 0.1s;
|
||||||
|
}
|
||||||
|
.confirm-btn:active { transform: translateY(1px); }
|
||||||
|
.confirm-btn-secondary {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.confirm-btn-secondary:hover {
|
||||||
|
background: var(--surface-ghost-hover);
|
||||||
|
color: var(--ink);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
.confirm-btn-danger {
|
||||||
|
background: var(--amber);
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.confirm-btn-danger:hover { filter: brightness(1.05); }
|
||||||
|
|
||||||
|
/* ── CLIENT BAR ─────────────────────────────────────────────────
|
||||||
|
Lives inside .main-col, above section I.
|
||||||
|
Tokenized left padding aligns "PREPARED FOR" with section card
|
||||||
|
edges and stays in sync with the current numeral gutter.
|
||||||
|
.client-input — contenteditable-style text input; oninput calls
|
||||||
|
update() which syncs clientNameDisplay in sidebar.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
.client-bar {
|
||||||
|
padding: clamp(20px, 1.8vw, 28px) 0 clamp(20px, 1.6vw, 24px) var(--section-offset);
|
||||||
|
}
|
||||||
|
.client-label {
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: var(--text-label-size);
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.client-input {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
color: var(--accent);
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: clamp(1.75rem, 2vw, 1.9375rem);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 560px;
|
||||||
|
outline: none;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.client-input::placeholder { color: var(--muted); opacity: 0.6; font-weight: 400; }
|
||||||
|
.client-rep-row {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.client-rep-row .client-label {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
.client-input--rep {
|
||||||
|
font-size: clamp(0.875rem, 1.1vw, 1rem);
|
||||||
|
font-weight: 500;
|
||||||
|
max-width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,255 +1,124 @@
|
|||||||
/* ══════════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════════
|
||||||
SVS MSP Calculator — Light Theme
|
SVS MSP Calculator — Light Theme
|
||||||
Imported dynamically by toggleTheme() when user switches to light mode.
|
Phase 5 reduces this file to token overrides only so the light mode
|
||||||
Overrides :root design tokens and all hardcoded dark-mode colours
|
stays aligned with the shared product system.
|
||||||
in SVS-MSP-Calculator.css. Never edit layout or structure here.
|
|
||||||
══════════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
/* ── DESIGN TOKENS (light overrides) ───────────────────────────── */
|
|
||||||
:root {
|
:root {
|
||||||
--ink: #2c2825; /* soft near-black — readable without harshness */
|
--ink: #2c2825;
|
||||||
--paper: #ece7dc; /* warm sand/khaki page tone */
|
--paper: #e2dccf;
|
||||||
--accent: #1a6a98; /* blue — good for buttons, kept full strength */
|
--accent: #637f88;
|
||||||
--muted: #6b6360; /* mid-grey for secondary text */
|
--muted: #6a6157;
|
||||||
--border: #d2cbc0; /* warm border to match khaki palette */
|
--border: #c3baab;
|
||||||
--card: #f1ebdf; /* general light-mode utility surface */
|
--border-soft: #cdc6ba;
|
||||||
--green: #217045; /* darker green — accessible on white */
|
--card: #ece4d6;
|
||||||
--amber: #a05f00; /* darker amber — accessible on white */
|
--green: #217045;
|
||||||
|
--amber: #a05f00;
|
||||||
|
--sky: #0e7490;
|
||||||
|
--focus-ring-soft: rgba(99, 127, 136, 0.16);
|
||||||
|
--top-bar-bg: #d9d0c1;
|
||||||
|
--top-bar-border: rgba(0, 0, 0, 0.09);
|
||||||
|
--top-bar-meta: var(--muted);
|
||||||
|
--theme-chip-bg: rgba(67, 57, 50, 0.08);
|
||||||
|
--theme-chip-hover: rgba(67, 57, 50, 0.13);
|
||||||
|
--theme-chip-active: rgba(67, 57, 50, 0.18);
|
||||||
|
--theme-chip-fg: #2a2622;
|
||||||
|
--surface-section: #eee6d8;
|
||||||
|
--surface-feature: #e6ddd0;
|
||||||
|
--surface-settings: #e3d7c4;
|
||||||
|
--surface-settings-divider: #c8bcab;
|
||||||
|
--surface-input: #f1eadf;
|
||||||
|
--surface-term-wrap: #ece3d6;
|
||||||
|
--surface-term-tile: rgba(255, 255, 255, 0.04);
|
||||||
|
--surface-term-tile-hover: rgba(99, 127, 136, 0.06);
|
||||||
|
--surface-term-tile-active: linear-gradient(180deg, #829ea8 0%, #667f89 100%);
|
||||||
|
--border-term-wrap: #cabdaa;
|
||||||
|
--border-term-tile-active: rgba(82, 107, 116, 0.24);
|
||||||
|
--shadow-term-wrap: inset 0 1px 0 rgba(255,255,255,0.34);
|
||||||
|
--shadow-term-tile-active: inset 0 1px 0 rgba(255,255,255,0.18);
|
||||||
|
--text-term-name: #64594e;
|
||||||
|
--text-term-name-active: #f8f5ef;
|
||||||
|
--text-term-sub: #4d433a;
|
||||||
|
--text-term-sub-active: #f8f5ef;
|
||||||
|
--text-term-discount: #2f2a25;
|
||||||
|
--text-term-discount-active: #ffffff;
|
||||||
|
--surface-best-value: rgba(86, 146, 105, 0.12);
|
||||||
|
--border-best-value: rgba(86, 146, 105, 0.3);
|
||||||
|
--text-best-value: #35554a;
|
||||||
|
--surface-best-value-active: rgba(255, 255, 255, 0.18);
|
||||||
|
--border-best-value-active: rgba(255, 255, 255, 0.36);
|
||||||
|
--text-best-value-active: #ffffff;
|
||||||
|
--surface-sidebar: #e0dad1;
|
||||||
|
--surface-sidebar-header: #769aaa;
|
||||||
|
--surface-sidebar-body: #ebe5dd;
|
||||||
|
--surface-sidebar-utility: #d8d1c7;
|
||||||
|
--surface-export: #ddd6cd;
|
||||||
|
--surface-compare: #ddd7ce;
|
||||||
|
--surface-modal: #f0e8dc;
|
||||||
|
--surface-mobile-sheet: #e5dfd6;
|
||||||
|
--surface-mobile-close-row: #dbd4cb;
|
||||||
|
--surface-mobile-actions: #dbd4cb;
|
||||||
|
--surface-mobile-sidebar: transparent;
|
||||||
|
--surface-accent-soft: rgba(99, 127, 136, 0.09);
|
||||||
|
--surface-summary-badge: rgba(99, 127, 136, 0.09);
|
||||||
|
--border-summary-badge: rgba(99, 127, 136, 0.23);
|
||||||
|
--surface-chevron: rgba(58, 50, 43, 0.04);
|
||||||
|
--surface-chevron-active: rgba(58, 50, 43, 0.075);
|
||||||
|
--surface-ghost: rgba(58, 50, 43, 0.06);
|
||||||
|
--surface-ghost-hover: rgba(58, 50, 43, 0.1);
|
||||||
|
--surface-step: #f2ebdf;
|
||||||
|
--surface-step-hover: #e5dbcc;
|
||||||
|
--surface-step-active: var(--accent);
|
||||||
|
--surface-step-border: #a99e8f;
|
||||||
|
--text-step: var(--accent);
|
||||||
|
--surface-success: #e6f2e9;
|
||||||
|
--surface-success-border: #8fb69d;
|
||||||
|
--surface-danger: #f7e8ea;
|
||||||
|
--surface-danger-border: #d5a1ab;
|
||||||
|
--text-danger: #7a1520;
|
||||||
|
--surface-warning: #f7f0dd;
|
||||||
|
--surface-warning-panel: #f3ebda;
|
||||||
|
--surface-warning-border: #ddc39b;
|
||||||
|
--surface-compare-success: rgba(86, 146, 105, 0.12);
|
||||||
|
--surface-compare-warning: rgba(179, 133, 72, 0.11);
|
||||||
|
--surface-selected: #d6e0e1;
|
||||||
|
--surface-addon-hover: #dde2de;
|
||||||
|
--border-addon-hover: #b0bcc0;
|
||||||
|
--text-selected-accent: #264b5d;
|
||||||
|
--text-sidebar-kicker: rgba(248, 245, 239, 0.84);
|
||||||
|
--text-sidebar-heading: #fbf8f3;
|
||||||
|
--text-sidebar-placeholder: rgba(248, 245, 239, 0.76);
|
||||||
|
--text-money: var(--ink);
|
||||||
|
--text-money-hero: var(--ink);
|
||||||
|
--text-vs-heading: var(--ink);
|
||||||
|
--text-vs-accent: var(--accent);
|
||||||
|
--text-vs-muted: var(--muted);
|
||||||
|
--text-incentive: #35554a;
|
||||||
|
--text-pill-savings-active: #d4f5e0;
|
||||||
|
--section-hover-border: rgba(99, 127, 136, 0.18);
|
||||||
|
--section-hover-shadow: -3px 0 0 0 rgba(99, 127, 136, 0.18);
|
||||||
|
--section-open-border: rgba(99, 127, 136, 0.27);
|
||||||
|
--section-open-shadow: -3px 0 0 0 rgba(99, 127, 136, 0.3);
|
||||||
|
--surface-mobile-close-btn: rgba(61, 53, 46, 0.07);
|
||||||
|
--surface-mobile-close-btn-active: rgba(61, 53, 46, 0.12);
|
||||||
|
--btn-primary-fg: #fbf8f3;
|
||||||
|
--btn-primary-hover: #59737c;
|
||||||
|
--surface-pill-icon: rgba(255, 255, 255, 0.18);
|
||||||
|
--border-sidebar: #c6beb3;
|
||||||
|
--surface-sidebar-utility-border: #bfb7ad;
|
||||||
|
--border-compare: #c8c0b5;
|
||||||
|
--border-export-top: #ccc4ba;
|
||||||
|
--border-mobile-sheet: #c6beb3;
|
||||||
|
--border-mobile-row: #ccc4ba;
|
||||||
|
--sidebar-zone-services: rgba(0, 0, 0, 0.025);
|
||||||
|
--sidebar-zone-invoice: rgba(0, 0, 0, 0.04);
|
||||||
|
--sidebar-zone-value: rgba(0, 0, 0, 0.015);
|
||||||
|
--surface-switch-off: #b5ad9f;
|
||||||
|
--surface-switch-on: var(--green);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── BODY ────────────────────────────────────────────────────────── */
|
.btn-import-quote:hover {
|
||||||
body {
|
background: color-mix(in srgb, var(--sky) 8%, transparent);
|
||||||
background: var(--paper);
|
border-color: color-mix(in srgb, var(--sky) 35%, transparent);
|
||||||
color: var(--ink);
|
color: var(--sky);
|
||||||
}
|
|
||||||
|
|
||||||
/* ── TOP BAR ─────────────────────────────────────────────────────
|
|
||||||
Dark CSS sets background: var(--ink). In dark mode --ink is cream.
|
|
||||||
In light mode --ink becomes near-black, so we must pin it to cream
|
|
||||||
to keep the SVG logo (fill="#0c0c0c" text) legible.
|
|
||||||
─────────────────────────────────────────────────────────────────── */
|
|
||||||
.top-bar {
|
|
||||||
background: #e3ddd2 !important;
|
|
||||||
border-bottom-color: rgba(0, 0, 0, 0.09) !important; /* replace strong blue stripe with soft warm separator */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── SECTION CARDS ───────────────────────────────────────────────── */
|
|
||||||
.section {
|
|
||||||
background: #f5f2ea !important; /* warm cream — floats above khaki paper */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── SPLIT LIGHT-MODE SURFACES ─────────────────────────────────────
|
|
||||||
Keep sections as the main content cards.
|
|
||||||
Quote settings = warmer utility panel.
|
|
||||||
Live quote = cooler summary panel, including the mobile sheet.
|
|
||||||
*/
|
|
||||||
.quote-settings-bar {
|
|
||||||
background: #ebe1d2 !important;
|
|
||||||
border-color: #d8cab8 !important;
|
|
||||||
}
|
|
||||||
.qs-divider {
|
|
||||||
background: #d6c8b7 !important;
|
|
||||||
}
|
|
||||||
.qs-fee-input-wrap,
|
|
||||||
.qs-fee-dollar,
|
|
||||||
.qs-fee-input {
|
|
||||||
background: #f6f0e6 !important;
|
|
||||||
}
|
|
||||||
.sidebar {
|
|
||||||
background: #edf3f6 !important;
|
|
||||||
border-color: #cfdae2 !important;
|
|
||||||
}
|
|
||||||
.sidebar-utility .btn-reset-quote,
|
|
||||||
.mobile-panel-actions .btn-reset-quote {
|
|
||||||
background: #edf3f6 !important;
|
|
||||||
border-color: #cfdae2 !important;
|
|
||||||
}
|
|
||||||
.sidebar-body {
|
|
||||||
background: #f3f7f9 !important;
|
|
||||||
}
|
|
||||||
.export-wrap {
|
|
||||||
background: #edf3f6 !important;
|
|
||||||
border-top: 1px solid #d8e2e8 !important;
|
|
||||||
}
|
|
||||||
.mobile-panel-sheet {
|
|
||||||
background: #edf3f6 !important;
|
|
||||||
border-top-color: #cfdae2 !important;
|
|
||||||
}
|
|
||||||
.mobile-panel-close-row {
|
|
||||||
background: #edf3f6 !important;
|
|
||||||
border-bottom-color: #d8e2e8 !important;
|
|
||||||
}
|
|
||||||
.mobile-panel-actions {
|
|
||||||
background: #edf3f6 !important;
|
|
||||||
border-bottom-color: #d8e2e8 !important;
|
|
||||||
}
|
|
||||||
.mobile-panel-sheet .sidebar {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
.mobile-panel-sheet .sidebar-body {
|
|
||||||
background: #f3f7f9 !important;
|
|
||||||
}
|
|
||||||
.confirm-modal-card {
|
|
||||||
background: #f5f2ea !important;
|
|
||||||
}
|
|
||||||
.confirm-btn-secondary:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.04) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── CHEVRON PILL — swap white-alpha tint for dark-alpha ─────────── */
|
|
||||||
.sec-chevron {
|
|
||||||
background: rgba(0, 0, 0, 0.04) !important;
|
|
||||||
}
|
|
||||||
.sec-open .sec-chevron,
|
|
||||||
.section-toggle:hover .sec-chevron {
|
|
||||||
background: rgba(0, 0, 0, 0.07) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── SECTION HOVER / OPEN GLOW ───────────────────────────────────── */
|
|
||||||
.section:hover {
|
|
||||||
border-color: rgba(26, 106, 152, 0.22) !important;
|
|
||||||
box-shadow: -3px 0 0 0 rgba(26, 106, 152, 0.25) !important;
|
|
||||||
}
|
|
||||||
.sec-open {
|
|
||||||
border-color: rgba(26, 106, 152, 0.35) !important;
|
|
||||||
box-shadow: -3px 0 0 0 rgba(26, 106, 152, 0.45) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── CALLOUT BOXES ───────────────────────────────────────────────── */
|
|
||||||
.callout-green {
|
|
||||||
background: #e8f7ef !important;
|
|
||||||
border-color: #3ab870 !important;
|
|
||||||
color: var(--green) !important;
|
|
||||||
}
|
|
||||||
.callout-red {
|
|
||||||
background: #fceef0 !important;
|
|
||||||
border-color: #c4526a !important;
|
|
||||||
color: #7a1520 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── NUMBER STEPPER CONTROLS ─────────────────────────────────────── */
|
|
||||||
/* Dark mode steppers blend in naturally. Light mode needs explicit lift:
|
|
||||||
white surfaces stand off the warm card bg; accent symbols tie to brand. */
|
|
||||||
.step-btn {
|
|
||||||
background: #faf8f3 !important; /* soft cream — not stark white */
|
|
||||||
border-color: #b8b4ae !important; /* stronger than --border for crispness */
|
|
||||||
color: var(--accent) !important; /* accent blue +/- symbols instead of muted grey */
|
|
||||||
}
|
|
||||||
.step-btn:hover {
|
|
||||||
background: #ede8de !important; /* warm tint matches khaki family on hover */
|
|
||||||
border-color: var(--accent) !important;
|
|
||||||
color: var(--accent) !important;
|
|
||||||
}
|
|
||||||
.step-btn:active {
|
|
||||||
background: var(--accent) !important;
|
|
||||||
border-color: var(--accent) !important;
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
.num-input {
|
|
||||||
background: #faf8f3 !important; /* soft cream — matches step buttons */
|
|
||||||
border-color: #b8b4ae !important;
|
|
||||||
}
|
|
||||||
.num-input:focus {
|
|
||||||
border-color: var(--accent) !important;
|
|
||||||
box-shadow: 0 0 0 2px rgba(26, 106, 152, 0.15) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── ADDON ROW SELECTED ──────────────────────────────────────────── */
|
|
||||||
.addon-row.selected {
|
|
||||||
background: #daedf8 !important;
|
|
||||||
border-color: var(--accent) !important;
|
|
||||||
box-shadow: inset 3px 0 0 0 var(--accent) !important;
|
|
||||||
}
|
|
||||||
.addon-row.selected .addon-name {
|
|
||||||
color: var(--ink) !important;
|
|
||||||
}
|
|
||||||
.addon-row.selected .addon-price {
|
|
||||||
color: var(--accent) !important; /* override dark-mode #62c5f0 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── FEATURE CARDS ───────────────────────────────────────────────── */
|
|
||||||
.feature-card {
|
|
||||||
background: #e9e4da !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── SIDEBAR / SUMMARY TEXT ────────────────────────────────────────
|
|
||||||
Base dark theme hardcodes a few bright values for money and labels.
|
|
||||||
In light mode those need to return to dark text so the desktop
|
|
||||||
sidebar and the responsive mobile sheet remain readable.
|
|
||||||
*/
|
|
||||||
.sidebar-line .val {
|
|
||||||
color: var(--ink) !important;
|
|
||||||
}
|
|
||||||
.sidebar-mrr {
|
|
||||||
color: var(--ink) !important;
|
|
||||||
}
|
|
||||||
.vs-svs-label {
|
|
||||||
color: var(--ink) !important;
|
|
||||||
}
|
|
||||||
.vs-val-accent {
|
|
||||||
color: var(--accent) !important;
|
|
||||||
}
|
|
||||||
.vs-td-muted {
|
|
||||||
color: var(--muted) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── NUDGE BANNER ────────────────────────────────────────────────── */
|
|
||||||
.nudge-banner.amber {
|
|
||||||
background: #fff7e0 !important;
|
|
||||||
color: var(--amber) !important;
|
|
||||||
}
|
|
||||||
.nudge-banner.green {
|
|
||||||
background: #e8f7ef !important;
|
|
||||||
color: var(--green) !important;
|
|
||||||
}
|
|
||||||
.nudge-nav-btn {
|
|
||||||
background: rgba(0, 0, 0, 0.06) !important;
|
|
||||||
}
|
|
||||||
.nudge-nav-btn:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.11) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── VS COMPARISON ───────────────────────────────────────────────── */
|
|
||||||
.vs-comparison-wrap {
|
|
||||||
background: #e5edf2 !important;
|
|
||||||
border-color: #cfdae2 !important;
|
|
||||||
}
|
|
||||||
.vs-save-green td { background: rgba(39, 174, 96, 0.10) !important; }
|
|
||||||
.vs-save-amber td { background: rgba(210, 120, 30, 0.09) !important; }
|
|
||||||
|
|
||||||
/* ── SUMMARY BADGE ───────────────────────────────────────────────── */
|
|
||||||
.sec-summary-badge {
|
|
||||||
background: rgba(26, 106, 152, 0.09) !important;
|
|
||||||
border-color: rgba(26, 106, 152, 0.28) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── MOBILE PILL HOVER ───────────────────────────────────────────── */
|
|
||||||
.mobile-quote-pill:hover {
|
|
||||||
background: #1a5f8a !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── THEME TOGGLE BUTTON (light-mode variant) ────────────────────── */
|
|
||||||
.theme-toggle-btn {
|
|
||||||
background: rgba(0, 0, 0, 0.09) !important;
|
|
||||||
color: #2a2622 !important;
|
|
||||||
}
|
|
||||||
.theme-toggle-btn:hover { background: rgba(0, 0, 0, 0.15) !important; }
|
|
||||||
.theme-toggle-btn:active { background: rgba(0, 0, 0, 0.21) !important; }
|
|
||||||
|
|
||||||
/* ── SAVINGS RESULT BOX ──────────────────────────────────────────── */
|
|
||||||
.savings-result {
|
|
||||||
background: #eaf5ef !important;
|
|
||||||
border-color: #a8d5b8 !important;
|
|
||||||
}
|
|
||||||
/* Amber warning state — JS toggles .savings-amber class */
|
|
||||||
.savings-result.savings-amber {
|
|
||||||
background: #fff4e0 !important;
|
|
||||||
border-color: #e8c57a !important;
|
|
||||||
color: var(--amber) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── PITCH FOOTER (green strip) ──────────────────────────────────── */
|
|
||||||
.pitch-footer {
|
|
||||||
background: #d4eddc !important;
|
|
||||||
border-top-color: #a8d5b8 !important;
|
|
||||||
}
|
}
|
||||||
|
|||||||
155
SVS-MSP-Calculator-print.css
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/* SVS MSP Calculator - Print */
|
||||||
|
/* Extracted during Phase 5 to keep the HTML shell stable while splitting the monolithic stylesheet. */
|
||||||
|
/* ═══════════════════════════════════════════════════════
|
||||||
|
PRINT / PDF EXPORT (Export A)
|
||||||
|
window.print() triggers this via Print / Save PDF button.
|
||||||
|
Goals:
|
||||||
|
- Clean, branded quote document
|
||||||
|
- Hide all interactive controls
|
||||||
|
- Force all sections expanded (body shown)
|
||||||
|
- No background colours that waste ink (except header)
|
||||||
|
- Page-break control so summary never splits
|
||||||
|
═══════════════════════════════════════════════════════ */
|
||||||
|
@media print {
|
||||||
|
/* ── Force light background on body ── */
|
||||||
|
body { background: var(--print-paper) !important; color: var(--print-ink) !important; font-size: 13px; }
|
||||||
|
|
||||||
|
/* ── Hide interactive & mobile-only elements ── */
|
||||||
|
.mobile-quote-pill,
|
||||||
|
.mobile-quote-panel,
|
||||||
|
.step-btn,
|
||||||
|
.collapsible-header,
|
||||||
|
.sec-chevron,
|
||||||
|
.section-toggle,
|
||||||
|
.tier-seg-wrap,
|
||||||
|
.pill-toggle,
|
||||||
|
.addon-row input[type=checkbox],
|
||||||
|
.savings-input-row,
|
||||||
|
.export-wrap,
|
||||||
|
.nudge-banner,
|
||||||
|
.pitch-wrap,
|
||||||
|
.quote-settings-bar,
|
||||||
|
.section-header.section-toggle { pointer-events: none; }
|
||||||
|
|
||||||
|
.mobile-quote-pill { display: none !important; }
|
||||||
|
.mobile-quote-panel { display: none !important; }
|
||||||
|
.export-wrap { display: none !important; }
|
||||||
|
.nudge-banner { display: none !important; }
|
||||||
|
.theme-toggle-btn { display: none !important; }
|
||||||
|
.pitch-wrap { display: none !important; }
|
||||||
|
.step-btn { display: none !important; }
|
||||||
|
.collapsible-header { display: none !important; }
|
||||||
|
.sec-chevron { display: none !important; }
|
||||||
|
.sec-summary-badge { display: none !important; }
|
||||||
|
.sec-controls-row { display: none !important; }
|
||||||
|
.quote-settings-bar { display: none !important; }
|
||||||
|
.section-badge { display: none !important; }
|
||||||
|
#savingsPrompt { display: none !important; }
|
||||||
|
.quote-notes-wrap { display: none !important; }
|
||||||
|
.client-rep-row { display: none !important; }
|
||||||
|
.sidebar-focus-toggle { display: none !important; }
|
||||||
|
.sidebar-focus-print-btn { display: none !important; }
|
||||||
|
.sidebar-utility { display: none !important; }
|
||||||
|
.qs-switch { display: none !important; }
|
||||||
|
.confirm-modal { display: none !important; }
|
||||||
|
|
||||||
|
/* ── Show ALL section bodies (force expand) ── */
|
||||||
|
.section-body { display: block !important; }
|
||||||
|
.collapsible-body { max-height: none !important; opacity: 1 !important; overflow: visible !important; }
|
||||||
|
|
||||||
|
/* ── Reset layout to single column ── */
|
||||||
|
.outer {
|
||||||
|
display: block !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
.main-col, .side-col { width: 100% !important; position: static !important; }
|
||||||
|
|
||||||
|
/* ── Top bar: keep accent, reduce height ── */
|
||||||
|
.top-bar {
|
||||||
|
position: static !important;
|
||||||
|
padding: 10px 20px !important;
|
||||||
|
border-bottom: 2px solid var(--print-accent) !important;
|
||||||
|
background: var(--print-paper) !important;
|
||||||
|
}
|
||||||
|
.top-bar-inner { padding: 0 !important; }
|
||||||
|
.top-bar-right { color: var(--print-muted) !important; }
|
||||||
|
|
||||||
|
/* ── Section cards: clean borders, no dark bg ── */
|
||||||
|
.section {
|
||||||
|
background: var(--print-paper) !important;
|
||||||
|
border: 1px solid var(--print-border) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
break-inside: avoid;
|
||||||
|
padding: 16px 20px !important;
|
||||||
|
margin-bottom: 12px !important;
|
||||||
|
}
|
||||||
|
.section-num { color: var(--print-section-num) !important; }
|
||||||
|
.section-title { font-size: 16px !important; }
|
||||||
|
|
||||||
|
/* ── Sidebar: show inline after sections, styled for print ── */
|
||||||
|
.sidebar {
|
||||||
|
background: var(--print-paper) !important;
|
||||||
|
border: 2px solid var(--print-accent) !important;
|
||||||
|
border-radius: 6px !important;
|
||||||
|
margin: 16px 0 !important;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
break-inside: avoid;
|
||||||
|
}
|
||||||
|
.sidebar-header { background: var(--print-accent) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
.sidebar-mrr { font-size: 36px !important; color: var(--print-ink) !important; }
|
||||||
|
.sidebar-line { color: var(--print-sidebar-line) !important; border-bottom-color: var(--print-border-strong) !important; }
|
||||||
|
.sidebar-line .val { color: var(--print-ink) !important; }
|
||||||
|
|
||||||
|
/* ── VS comparison: clean for print ── */
|
||||||
|
.vs-save-green td { background: var(--print-save-green) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
.vs-save-amber td { background: var(--print-save-amber) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
.vs-save-amber { background: var(--print-save-amber-panel) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
|
||||||
|
/* ── Feature cards: minimal ── */
|
||||||
|
.feature-card { background: var(--print-feature) !important; border-color: var(--print-border-strong) !important; }
|
||||||
|
.feature-card-grid { grid-template-columns: 1fr 1fr !important; gap: 6px !important; }
|
||||||
|
|
||||||
|
/* ── Addon rows ── */
|
||||||
|
.addon-row { border-color: var(--print-border-strong) !important; }
|
||||||
|
.addon-row.selected { background: var(--print-addon-selected) !important; border-color: var(--print-accent) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
|
||||||
|
/* ── Callout boxes ── */
|
||||||
|
.callout-green { background: var(--print-callout-green) !important; border-color: var(--print-callout-green-border) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
.callout-red { background: var(--print-callout-red) !important; border-color: var(--print-callout-red-border) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
|
||||||
|
/* ── Progress bar ── */
|
||||||
|
.progress-fill { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
|
|
||||||
|
/* ── Print footer ── */
|
||||||
|
.pitch-footer { display: none !important; }
|
||||||
|
|
||||||
|
/* ── Page break: force summary sidebar to start fresh ── */
|
||||||
|
.side-col { page-break-before: always; break-before: always; }
|
||||||
|
|
||||||
|
/* ── Input fields: show values as static text ── */
|
||||||
|
.num-input, .qs-fee-input {
|
||||||
|
border: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
}
|
||||||
|
.client-input {
|
||||||
|
border: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Print footer note ── */
|
||||||
|
body::after {
|
||||||
|
content: 'Prepared by Silicon Valley Services (SVS) MSP · Ottawa, Ontario · This quote is valid for 30 days from date of issue. Questions? Contact your SVS account representative.';
|
||||||
|
display: block;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--print-footer-note);
|
||||||
|
border-top: 1px solid var(--print-border-strong);
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
428
SVS-MSP-Calculator-responsive.css
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
/* SVS MSP Calculator - Responsive */
|
||||||
|
/* Elastic fluid foundation — clamp() tokens in tokens.css handle continuous
|
||||||
|
scaling. Only two structural breakpoints remain:
|
||||||
|
≤1100px — 2-col → 1-col, sidebar → mobile pill/panel
|
||||||
|
≤ 600px — phone layout shifts (stacking, gutter collapse)
|
||||||
|
Plus one orientation rule:
|
||||||
|
≤ 780px landscape — restore 2-col sidebar
|
||||||
|
The old 1350px and 900px breakpoints are eliminated; fluid tokens cover them.
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
/* ── TABLET / SINGLE-COLUMN (≤ 1100px) ──────────────────────────
|
||||||
|
Structural shift: grid collapses to 1fr, sidebar hides,
|
||||||
|
mobile pill + panel appear.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.outer {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pitch-inner { margin-left: 0; }
|
||||||
|
.pitch-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.pitch-item:nth-child(2) { border-right: none; }
|
||||||
|
.pitch-item:nth-child(3) { border-top: 1px solid var(--border); }
|
||||||
|
.pitch-item:nth-child(4) { border-top: 1px solid var(--border); border-right: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── PHONE (≤ 600px) ────────────────────────────────────────────
|
||||||
|
True layout shifts only: gutter collapses, controls stack,
|
||||||
|
pill-toggles go vertical, touch targets enforced.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
:root {
|
||||||
|
--section-offset: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar-logo { margin-left: 0; }
|
||||||
|
.section { border-radius: 10px; }
|
||||||
|
|
||||||
|
.client-bar { padding: var(--space-xl) 0 var(--space-xl) 0; }
|
||||||
|
.sections-toolbar { margin-left: 0; margin-bottom: var(--space-md); }
|
||||||
|
.client-input { font-size: 1.375rem; max-width: 100%; }
|
||||||
|
.qs-savings-stack { margin-top: var(--space-stack-tight); }
|
||||||
|
.qs-fee-row { padding: 6px 0 0; }
|
||||||
|
|
||||||
|
.main-col > .section:first-of-type { margin-top: var(--space-sm); }
|
||||||
|
|
||||||
|
/* Pill toggle — stack vertically on tiny screens */
|
||||||
|
.pill-toggle {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.pill-toggle label { border-right: none; border-bottom: 1px solid var(--border); }
|
||||||
|
.pill-toggle label:last-child { border-bottom: none; }
|
||||||
|
|
||||||
|
/* Contract terms — vertical stack on phones */
|
||||||
|
.tier-seg { padding: var(--space-md) 6px; }
|
||||||
|
.tier-seg .tier-price { font-size: 1.125rem; }
|
||||||
|
.tier-seg .tier-name { font-size: 0.6875rem; }
|
||||||
|
.qs-term-wrap {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.qs-term-wrap .tier-seg {
|
||||||
|
padding: var(--space-stack) var(--space-stack) 13px;
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.qs-term-wrap .tier-seg:last-of-type {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.qs-term-wrap .tier-name { font-size: 0.75rem; margin-bottom: var(--space-xs); }
|
||||||
|
.qs-term-wrap .tier-sub { font-size: 0.6875rem; }
|
||||||
|
.qs-toggle-row.qs-fee-waive { padding: 6px 9px; }
|
||||||
|
|
||||||
|
/* Input rows — stack label above input */
|
||||||
|
.input-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--space-md);
|
||||||
|
}
|
||||||
|
.section-body .num-stepper { width: 100%; }
|
||||||
|
.section-body .num-input { width: 100%; font-size: 1.25rem; padding: var(--space-md); flex: 1; }
|
||||||
|
.section-body .step-btn { width: 48px; font-size: 1.375rem; }
|
||||||
|
|
||||||
|
/* Section titles — tighter on phone */
|
||||||
|
.section-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
.section-num {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Controls row — stack full-width on phone */
|
||||||
|
.sec-controls-row {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.sec-controls-row .num-stepper {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
.sec-controls-row .num-input {
|
||||||
|
flex: 1 1 0%;
|
||||||
|
width: auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.sec-controls-row .step-btn {
|
||||||
|
width: 40px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.section .sec-controls-row > .section-badge,
|
||||||
|
.section .sec-controls-row > .sec-summary-badge {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
align-self: stretch;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Collapsible */
|
||||||
|
.collapsible-body { padding: var(--space-sm) 0 var(--space-stack-tight) var(--space-stack-roomy); }
|
||||||
|
|
||||||
|
/* Feature cards — single col already, just tighter */
|
||||||
|
.feature-card { padding: var(--space-stack) var(--space-stack-roomy); }
|
||||||
|
.m365-app-strip { padding: var(--space-stack) var(--space-stack-roomy) var(--space-md); }
|
||||||
|
.m365-app-list {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: var(--space-stack-tight);
|
||||||
|
}
|
||||||
|
.m365-app-item { padding: var(--space-stack-tight) 6px; }
|
||||||
|
.m365-app-icon {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Savings row — stack */
|
||||||
|
.savings-input-row { flex-direction: column; align-items: flex-start; gap: var(--space-sm); }
|
||||||
|
.savings-input-row input { width: 100%; }
|
||||||
|
|
||||||
|
/* Sidebar */
|
||||||
|
.sidebar-body { padding: var(--space-lg); }
|
||||||
|
.sidebar-header { padding: var(--space-stack) var(--space-xl); }
|
||||||
|
.sidebar-mrr { font-size: 2.25rem; }
|
||||||
|
|
||||||
|
/* VS table — keep readable while fitting the mobile sidebar panel */
|
||||||
|
.vs-comparison-wrap { padding: var(--space-lg) var(--space-stack-roomy); }
|
||||||
|
.vs-header { gap: var(--space-sm); margin-bottom: var(--space-stack); }
|
||||||
|
.vs-brand-name { font-size: 0.9375rem; }
|
||||||
|
.vs-table td { padding: var(--space-sm) 3px; font-size: 0.78125rem; }
|
||||||
|
.vs-save-row td { padding: var(--space-stack-tight) var(--space-md); }
|
||||||
|
.vs-footnote { font-size: 0.65625rem; line-height: 1.55; }
|
||||||
|
|
||||||
|
/* Pitch footer */
|
||||||
|
.pitch-wrap { padding: 0; }
|
||||||
|
.pitch-inner { margin-left: 0; border-radius: 0; }
|
||||||
|
.pitch-grid { grid-template-columns: 1fr 1fr; }
|
||||||
|
.pitch-item { padding: var(--space-xl) var(--space-stack-roomy); }
|
||||||
|
.pitch-item:nth-child(2) { border-right: none; }
|
||||||
|
.pitch-item:nth-child(3) { border-top: 1px solid var(--border); }
|
||||||
|
.pitch-item:nth-child(4) { border-top: 1px solid var(--border); border-right: none; }
|
||||||
|
.pitch-title { font-size: 0.875rem; }
|
||||||
|
.pitch-desc { font-size: 0.8125rem; }
|
||||||
|
.pitch-footer { padding: var(--space-stack) var(--space-stack-roomy); font-size: 0.75rem; }
|
||||||
|
|
||||||
|
/* Touch targets — ensure ≥44px on phone */
|
||||||
|
.collapsible-header {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
.section-toggle {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nudge banner */
|
||||||
|
.nudge-banner { padding: var(--space-stack) var(--space-lg); font-size: 0.8125rem; min-height: 0; }
|
||||||
|
.export-wrap { padding: var(--space-stack-roomy) var(--space-stack-roomy) var(--space-lg); }
|
||||||
|
.confirm-modal-card {
|
||||||
|
margin-top: 8vh;
|
||||||
|
padding: var(--space-xl) var(--space-lg) var(--space-lg);
|
||||||
|
}
|
||||||
|
.confirm-modal-title { font-size: 1.3125rem; }
|
||||||
|
.confirm-modal-actions { flex-direction: column-reverse; }
|
||||||
|
.confirm-btn { width: 100%; }
|
||||||
|
|
||||||
|
/* Fee table */
|
||||||
|
.fee-table td { padding: 7px 0; font-size: 0.8125rem; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── LANDSCAPE PHONE (≤ 780px, orientation: landscape) ── */
|
||||||
|
@media (max-width: 780px) and (orientation: landscape) {
|
||||||
|
.outer {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: var(--space-2xl);
|
||||||
|
padding: var(--space-xl) var(--space-xl) 40px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
.main-col { order: 1; }
|
||||||
|
.side-col {
|
||||||
|
order: 2;
|
||||||
|
position: sticky;
|
||||||
|
top: 60px;
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
.section {
|
||||||
|
margin-left: 0;
|
||||||
|
padding: var(--space-lg) var(--space-xl) 22px;
|
||||||
|
}
|
||||||
|
.client-bar { padding: var(--space-stack-roomy) 0 var(--space-stack-roomy) 0; }
|
||||||
|
.sidebar { margin-top: 0; }
|
||||||
|
.sidebar-mrr { font-size: 1.875rem; }
|
||||||
|
.pitch-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.pitch-inner { margin-left: 0; }
|
||||||
|
.pitch-wrap { padding: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── MOBILE-ONLY ELEMENTS — hidden at desktop baseline ─────────
|
||||||
|
MUST be display:none here (outside any media query) so that
|
||||||
|
the panel doesn't render on top of desktop layout.
|
||||||
|
The @media (max-width:1100px) block below overrides to display:flex.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
.mobile-quote-pill { display: none; }
|
||||||
|
.mobile-quote-panel { display: none; }
|
||||||
|
.mobile-panel-actions { display: none; }
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════
|
||||||
|
MOBILE QUOTE PILL + FULL-SCREEN PANEL
|
||||||
|
═══════════════════════════════════════ */
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.sidebar-focus-toggle,
|
||||||
|
.sidebar-focus-backdrop { display: none; }
|
||||||
|
|
||||||
|
/* Hide the static sidebar entirely on mobile/tablet */
|
||||||
|
.side-col { display: none; }
|
||||||
|
|
||||||
|
/* Show the floating pill */
|
||||||
|
.mobile-quote-pill {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-stack-tight);
|
||||||
|
position: fixed;
|
||||||
|
top: 82px;
|
||||||
|
right: max(14px, env(safe-area-inset-right, 0px));
|
||||||
|
z-index: 200;
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--btn-primary-fg);
|
||||||
|
border-radius: 50px;
|
||||||
|
padding: var(--space-stack-tight) var(--space-lg) var(--space-stack-tight) var(--space-stack);
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: var(--shadow-floating);
|
||||||
|
border: none;
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
transition: background var(--transition-fast), transform var(--transition-fast);
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
.mobile-quote-pill:active { transform: scale(0.96); }
|
||||||
|
.mobile-quote-pill:hover { background: var(--btn-primary-hover); }
|
||||||
|
.mobile-pill-icon {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background: var(--surface-pill-icon);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.mobile-pill-mrr {
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.mobile-pill-label {
|
||||||
|
font-size: 10px;
|
||||||
|
opacity: 0.75;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
line-height: 1;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full-screen overlay panel */
|
||||||
|
.mobile-quote-panel {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 300;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.25s ease;
|
||||||
|
}
|
||||||
|
.mobile-quote-panel.open {
|
||||||
|
pointer-events: all;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark backdrop */
|
||||||
|
.mobile-panel-backdrop {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: var(--surface-mobile-backdrop);
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
-webkit-backdrop-filter: blur(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slide-up sheet */
|
||||||
|
.mobile-panel-sheet {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
max-height: 100vh;
|
||||||
|
background: var(--surface-mobile-sheet);
|
||||||
|
border-radius: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
transform: translateY(100%);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
|
||||||
|
will-change: transform;
|
||||||
|
border-top: 1px solid var(--border-mobile-sheet);
|
||||||
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||||
|
}
|
||||||
|
.mobile-quote-panel.open .mobile-panel-sheet {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drag handle */
|
||||||
|
.mobile-panel-handle {
|
||||||
|
width: 40px;
|
||||||
|
height: 4px;
|
||||||
|
background: var(--border);
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: var(--space-stack) auto 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close button row */
|
||||||
|
.mobile-panel-close-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: var(--space-stack-roomy) var(--space-xl) var(--space-md);
|
||||||
|
border-bottom: 1px solid var(--border-mobile-row);
|
||||||
|
background: var(--surface-mobile-close-row);
|
||||||
|
}
|
||||||
|
.mobile-panel-actions {
|
||||||
|
display: block;
|
||||||
|
padding: 0 var(--space-xl) var(--space-md);
|
||||||
|
border-bottom: 1px solid var(--border-mobile-row);
|
||||||
|
background: var(--surface-mobile-actions);
|
||||||
|
}
|
||||||
|
.mobile-panel-actions .btn-export {
|
||||||
|
margin-top: var(--space-md);
|
||||||
|
}
|
||||||
|
.mobile-panel-actions .btn-export-secondary {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.mobile-panel-close-title {
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
.mobile-panel-close-btn {
|
||||||
|
background: var(--surface-mobile-close-btn);
|
||||||
|
border: none;
|
||||||
|
color: var(--ink);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
transition: background var(--transition-fast);
|
||||||
|
}
|
||||||
|
.mobile-panel-close-btn:active { background: var(--surface-mobile-close-btn-active); }
|
||||||
|
|
||||||
|
/* Touch targets — minimum 44px on mobile per WCAG */
|
||||||
|
.nudge-nav-btn {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar inside the mobile sheet — strip all desktop positioning */
|
||||||
|
.mobile-panel-sheet .sidebar {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
background: var(--surface-mobile-sidebar) !important;
|
||||||
|
}
|
||||||
|
/* Keep Live Quote header visible in responsive panel so
|
||||||
|
Insight can sit directly below it (matching desktop order). */
|
||||||
|
.mobile-panel-sheet .sidebar-header {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
.mobile-panel-sheet .sidebar-body {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
.mobile-panel-sheet .nudge-banner {
|
||||||
|
margin-bottom: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Landscape phone — restore the static sidebar and suppress the mobile sheet. */
|
||||||
|
@media (max-width: 780px) and (orientation: landscape) {
|
||||||
|
.side-col { display: block; }
|
||||||
|
.mobile-quote-pill,
|
||||||
|
.mobile-quote-panel,
|
||||||
|
.mobile-panel-actions {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.mobile-panel-sheet { max-height: 88vh; }
|
||||||
|
}
|
||||||
239
SVS-MSP-Calculator-tokens.css
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
/* SVS MSP Calculator - Tokens */
|
||||||
|
/* Extracted during Phase 5 to keep the HTML shell stable while splitting the monolithic stylesheet. */
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
/* ── THEME TRANSITION ─────────────────────────────────────────
|
||||||
|
Brief color fade when switching themes so the swap feels smooth
|
||||||
|
instead of a jarring flash. Applied to body so it cascades.
|
||||||
|
transition-duration kept short (0.25s) to feel snappy.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
body.theme-transitioning,
|
||||||
|
body.theme-transitioning *,
|
||||||
|
body.theme-transitioning *::before,
|
||||||
|
body.theme-transitioning *::after {
|
||||||
|
transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── FOCUS VISIBLE ──────────────────────────────────────────────
|
||||||
|
Single rule covers all interactive elements — native inputs,
|
||||||
|
custom div toggles (section headers, collapsible headers),
|
||||||
|
addon rows, tier segments, and the theme toggle button.
|
||||||
|
Uses :focus-visible so mouse clicks don't show the ring.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
:focus-visible {
|
||||||
|
outline: 2px solid var(--accent);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
/* Suppress the default outline on elements we've styled explicitly */
|
||||||
|
.num-input:focus-visible,
|
||||||
|
.client-input:focus-visible,
|
||||||
|
.qs-fee-input:focus-visible,
|
||||||
|
.savings-input-row input:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
box-shadow: 0 0 0 2px var(--focus-ring-soft);
|
||||||
|
}
|
||||||
|
/* ── DESIGN TOKENS ─────────────────────────────────────────────
|
||||||
|
Single source of truth for all colours. Edit here, not inline.
|
||||||
|
─────────────────────────────────────────────────────────────── */
|
||||||
|
html {
|
||||||
|
font-size: calc(16px * var(--font-scale, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--font-scale: 1.03;
|
||||||
|
--page-max-width: clamp(1200px, 92vw, 2400px);
|
||||||
|
--page-gutter-x: clamp(16px, 3vw, 80px);
|
||||||
|
--layout-main-col: minmax(0, 3fr);
|
||||||
|
--layout-side-col: minmax(360px, 2fr);
|
||||||
|
--layout-column-gap: clamp(24px, 3vw, 56px);
|
||||||
|
--section-offset: clamp(52px, 7vw, 104px);
|
||||||
|
--section-num-width: clamp(44px, 5.5vw, 84px);
|
||||||
|
--section-num-size: clamp(2.625rem, 3.4vw, 4.125rem);
|
||||||
|
--section-padding-x: clamp(18px, 2.5vw, 40px);
|
||||||
|
--section-padding-top: clamp(20px, 2vw, 28px);
|
||||||
|
--section-padding-bottom: clamp(24px, 2.2vw, 32px);
|
||||||
|
--space-xs: 4px;
|
||||||
|
--space-sm: 8px;
|
||||||
|
--space-stack-tight: 10px;
|
||||||
|
--space-md: 12px;
|
||||||
|
--space-stack: 14px;
|
||||||
|
--space-stack-roomy: 16px;
|
||||||
|
--space-lg: 18px;
|
||||||
|
--space-xl: 20px;
|
||||||
|
--space-2xl: 24px;
|
||||||
|
--space-3xl: 28px;
|
||||||
|
--space-4xl: 32px;
|
||||||
|
--radius-control: 6px;
|
||||||
|
--radius-card: 12px;
|
||||||
|
--control-min-height: 46px;
|
||||||
|
--control-pad-y: 10px;
|
||||||
|
--control-pad-x: 16px;
|
||||||
|
--control-pad-y-tight: 6px;
|
||||||
|
--control-pad-x-tight: 10px;
|
||||||
|
--content-measure: 68ch;
|
||||||
|
--text-body-size: 1.03125rem;
|
||||||
|
--text-body-line: 1.72;
|
||||||
|
--text-meta-size: 0.75rem;
|
||||||
|
--text-label-size: 0.75rem;
|
||||||
|
--text-copy-size: 0.9375rem;
|
||||||
|
--text-copy-line: 1.76;
|
||||||
|
--text-compact-line: 1.58;
|
||||||
|
--text-title-line: 1.24;
|
||||||
|
--ink: #e8e3da; /* warm beige-white — brighter for legibility */
|
||||||
|
--paper: #1c1a17; /* darker base — widens gap vs card for panel float */
|
||||||
|
--accent: #3d8aba; /* lifted blue — pops on dark backgrounds */
|
||||||
|
--muted: #9e9588; /* softer secondary — clearly subordinate but readable */
|
||||||
|
--border: #35322c; /* subtler dividers */
|
||||||
|
--border-soft: var(--border);
|
||||||
|
--card: #272420; /* elevated surface — clear separation from paper */
|
||||||
|
--green: #3ab870;
|
||||||
|
--amber: #e8920f;
|
||||||
|
--sky: #38bdf8;
|
||||||
|
--transition-fast: 0.15s;
|
||||||
|
--transition-medium: 0.25s;
|
||||||
|
--focus-ring-soft: rgba(45,122,168,0.25);
|
||||||
|
--top-bar-bg: var(--ink);
|
||||||
|
--top-bar-border: var(--accent);
|
||||||
|
--top-bar-meta: var(--muted);
|
||||||
|
--top-bar-shadow: 0 10px 24px rgba(0,0,0,0.08);
|
||||||
|
--theme-chip-bg: rgba(0, 0, 0, 0.1);
|
||||||
|
--theme-chip-hover: rgba(0, 0, 0, 0.17);
|
||||||
|
--theme-chip-active: rgba(0, 0, 0, 0.23);
|
||||||
|
--theme-chip-fg: #3a3632;
|
||||||
|
--theme-chip-border: transparent;
|
||||||
|
--theme-chip-shadow: none;
|
||||||
|
--surface-section: var(--card);
|
||||||
|
--surface-feature: var(--card);
|
||||||
|
--surface-settings: var(--card);
|
||||||
|
--surface-settings-divider: var(--border);
|
||||||
|
--surface-input: var(--card);
|
||||||
|
--surface-term-wrap: var(--surface-input);
|
||||||
|
--surface-term-tile: transparent;
|
||||||
|
--surface-term-tile-hover: var(--surface-accent-soft);
|
||||||
|
--surface-term-tile-active: linear-gradient(180deg, color-mix(in srgb, var(--accent) 60%, white 12%), color-mix(in srgb, var(--accent) 72%, black 28%));
|
||||||
|
--border-term-wrap: var(--border);
|
||||||
|
--border-term-tile-active: transparent;
|
||||||
|
--shadow-term-wrap: inset 0 1px 0 color-mix(in srgb, var(--ink) 5%, transparent);
|
||||||
|
--shadow-term-tile-active: inset 0 1px 0 color-mix(in srgb, white 14%, transparent);
|
||||||
|
--text-term-name: var(--muted);
|
||||||
|
--text-term-name-active: var(--text-on-accent);
|
||||||
|
--text-term-sub: var(--muted);
|
||||||
|
--text-term-sub-active: var(--text-on-accent-strong);
|
||||||
|
--text-term-discount: color-mix(in srgb, var(--ink) 84%, var(--muted));
|
||||||
|
--text-term-discount-active: #ffffff;
|
||||||
|
--surface-best-value: var(--surface-positive-badge-strong);
|
||||||
|
--border-best-value: var(--border-positive-badge-strong);
|
||||||
|
--text-best-value: var(--green);
|
||||||
|
--surface-best-value-active: var(--surface-on-accent-badge);
|
||||||
|
--border-best-value-active: var(--border-on-accent-badge);
|
||||||
|
--text-best-value-active: var(--text-on-accent);
|
||||||
|
--surface-sidebar: var(--card);
|
||||||
|
--border-sidebar: var(--border);
|
||||||
|
--surface-sidebar-body: transparent;
|
||||||
|
--surface-sidebar-header: #5c8097;
|
||||||
|
--surface-sidebar-utility: var(--card);
|
||||||
|
--surface-sidebar-utility-border: var(--border);
|
||||||
|
--surface-export: var(--card);
|
||||||
|
--border-export-top: transparent;
|
||||||
|
--surface-compare: rgba(255, 255, 255, 0.06);
|
||||||
|
--border-compare: var(--border);
|
||||||
|
--surface-modal: var(--card);
|
||||||
|
--surface-mobile-sheet: var(--card);
|
||||||
|
--border-mobile-sheet: var(--border-soft);
|
||||||
|
--surface-mobile-close-row: transparent;
|
||||||
|
--surface-mobile-actions: var(--card);
|
||||||
|
--border-mobile-row: var(--border-soft);
|
||||||
|
--surface-mobile-sidebar: var(--surface-sidebar);
|
||||||
|
--surface-mobile-backdrop: rgba(0,0,0,0.65);
|
||||||
|
--surface-accent-soft: rgba(45, 122, 168, 0.07);
|
||||||
|
--surface-summary-badge: rgba(45,122,168,0.12);
|
||||||
|
--border-summary-badge: rgba(45,122,168,0.3);
|
||||||
|
--surface-chevron: rgba(255,255,255,0.05);
|
||||||
|
--surface-chevron-active: rgba(255,255,255,0.08);
|
||||||
|
--surface-chevron-mobile: var(--surface-chevron-active);
|
||||||
|
--surface-ghost: rgba(255,255,255,0.08);
|
||||||
|
--surface-ghost-hover: rgba(255,255,255,0.15);
|
||||||
|
--surface-step: var(--card);
|
||||||
|
--surface-step-hover: var(--border);
|
||||||
|
--surface-step-active: var(--accent);
|
||||||
|
--surface-step-border: var(--border);
|
||||||
|
--text-step: var(--muted);
|
||||||
|
--surface-success: #162e22;
|
||||||
|
--surface-success-border: #245840;
|
||||||
|
--surface-danger: #2a1319;
|
||||||
|
--surface-danger-border: #5e2830;
|
||||||
|
--text-danger: #e87882;
|
||||||
|
--surface-warning: #2a1e06;
|
||||||
|
--surface-warning-panel: #2e1f08;
|
||||||
|
--surface-warning-border: #5a3a10;
|
||||||
|
--surface-compare-success: rgba(39, 174, 96, 0.16);
|
||||||
|
--surface-compare-warning: rgba(210, 120, 30, 0.16);
|
||||||
|
--surface-selected: #1d2d3a;
|
||||||
|
--text-selected-accent: #ccecff;
|
||||||
|
--surface-positive-pill: rgba(33,112,69,0.10);
|
||||||
|
--surface-positive-badge: rgba(33,112,69,0.12);
|
||||||
|
--border-positive-badge: rgba(33,112,69,0.28);
|
||||||
|
--surface-positive-badge-strong: rgba(33,112,69,0.13);
|
||||||
|
--border-positive-badge-strong: rgba(33,112,69,0.3);
|
||||||
|
--surface-positive-panel: rgba(33,112,69,0.08);
|
||||||
|
--border-positive-panel: rgba(33,112,69,0.22);
|
||||||
|
--surface-addon-hover: var(--surface-accent-soft);
|
||||||
|
--border-addon-hover: color-mix(in srgb, var(--accent) 24%, var(--border));
|
||||||
|
--text-sidebar-kicker: rgba(255,255,255,0.75);
|
||||||
|
--text-sidebar-heading: #fff;
|
||||||
|
--text-sidebar-placeholder: rgba(255,255,255,0.65);
|
||||||
|
--text-money: #f2ede4;
|
||||||
|
--text-money-hero: #f5f0e8;
|
||||||
|
--text-vs-heading: #f2ede4;
|
||||||
|
--text-vs-accent: #5aaedc;
|
||||||
|
--text-vs-muted: #b5ab9e;
|
||||||
|
--text-incentive: var(--green);
|
||||||
|
--text-on-accent: #fff;
|
||||||
|
--text-on-accent-strong: rgba(255,255,255,0.9);
|
||||||
|
--text-on-accent-soft: rgba(255,255,255,0.85);
|
||||||
|
--text-on-accent-muted: rgba(255,255,255,0.8);
|
||||||
|
--text-on-accent-subtle: rgba(255,255,255,0.7);
|
||||||
|
--surface-on-accent-badge: rgba(255,255,255,0.18);
|
||||||
|
--border-on-accent-badge: rgba(255,255,255,0.35);
|
||||||
|
--text-pill-savings-active: #86efac;
|
||||||
|
--surface-backdrop: rgba(0, 0, 0, 0.62);
|
||||||
|
--shadow-modal: 0 16px 50px rgba(0,0,0,0.35);
|
||||||
|
--shadow-switch-knob: 0 1px 3px rgba(0,0,0,0.3);
|
||||||
|
--shadow-floating: 0 4px 20px rgba(0,0,0,0.45);
|
||||||
|
--section-hover-border: rgba(45,122,168,0.35);
|
||||||
|
--section-hover-shadow: -3px 0 0 0 rgba(45,122,168,0.4);
|
||||||
|
--section-open-border: rgba(45,122,168,0.5);
|
||||||
|
--section-open-shadow: -3px 0 0 0 rgba(45,122,168,0.7);
|
||||||
|
--surface-switch-knob: #fff;
|
||||||
|
--surface-switch-off: #4a4540;
|
||||||
|
--surface-switch-on: var(--green);
|
||||||
|
--surface-mobile-close-btn: var(--border);
|
||||||
|
--surface-mobile-close-btn-active: var(--muted);
|
||||||
|
--btn-primary-fg: #fff;
|
||||||
|
--btn-primary-hover: #3a8fc4;
|
||||||
|
--surface-pill-icon: rgba(255,255,255,0.2);
|
||||||
|
--print-paper: #fff;
|
||||||
|
--print-ink: #1a1a1a;
|
||||||
|
--print-accent: #2d7aa8;
|
||||||
|
--print-muted: #555;
|
||||||
|
--print-border: #ccc;
|
||||||
|
--print-border-strong: #ddd;
|
||||||
|
--print-section-num: #bbb;
|
||||||
|
--print-sidebar-line: #444;
|
||||||
|
--print-save-green: #e8f5e9;
|
||||||
|
--print-save-amber: #fff3e0;
|
||||||
|
--print-save-amber-panel: #fff8e1;
|
||||||
|
--print-feature: #f9f9f9;
|
||||||
|
--print-addon-selected: #e8f4fb;
|
||||||
|
--print-callout-green: #f0faf4;
|
||||||
|
--print-callout-red: #fff0f0;
|
||||||
|
--print-callout-green-border: #3ab870;
|
||||||
|
--print-callout-red-border: #5e2830;
|
||||||
|
--print-footer-note: #888;
|
||||||
|
--sidebar-stack-gap: 14px;
|
||||||
|
--sidebar-top-gap: calc(var(--sidebar-stack-gap) + 14px);
|
||||||
|
--top-bar-sticky-offset: 62px;
|
||||||
|
--sidebar-sticky-top: calc(var(--top-bar-sticky-offset) + var(--sidebar-top-gap));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -17,11 +17,9 @@
|
|||||||
Shows live MRR from #mrrDisplay via update() wrapper.
|
Shows live MRR from #mrrDisplay via update() wrapper.
|
||||||
Click → openMobilePanel()
|
Click → openMobilePanel()
|
||||||
════════════════════════════════════════════════════════════ -->
|
════════════════════════════════════════════════════════════ -->
|
||||||
<button class="mobile-quote-pill" onclick="openMobilePanel()" aria-label="View quote summary">
|
<button class="mobile-quote-pill" onclick="openMobilePanel()" aria-label="View quote summary">
|
||||||
<div class="mobile-pill-icon">
|
<div class="mobile-pill-icon">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="12" height="14" fill="#fff">
|
<span class="fa-icon fa-icon-file-invoice fa-icon--white" style="--icon-size:14px;"></span>
|
||||||
<path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128h128L256 0zM112 256H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="mobile-pill-mrr" id="mobilePillMrr">—</div>
|
<div class="mobile-pill-mrr" id="mobilePillMrr">—</div>
|
||||||
@@ -52,7 +50,14 @@
|
|||||||
<button class="mobile-panel-close-btn" onclick="closeMobilePanel()" aria-label="Close">×</button>
|
<button class="mobile-panel-close-btn" onclick="closeMobilePanel()" aria-label="Close">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mobile-panel-actions">
|
<div class="mobile-panel-actions">
|
||||||
<button type="button" class="btn-reset-quote" onclick="openResetConfirm()">Reset Quote</button>
|
<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>
|
</div>
|
||||||
<!-- Sidebar content injected by JS from the desktop sidebar markup -->
|
<!-- Sidebar content injected by JS from the desktop sidebar markup -->
|
||||||
<div id="mobilePanelContent"></div>
|
<div id="mobilePanelContent"></div>
|
||||||
@@ -78,7 +83,7 @@
|
|||||||
<span id="headerDate">—</span>
|
<span id="headerDate">—</span>
|
||||||
</div>
|
</div>
|
||||||
<button id="themeToggle" class="theme-toggle-btn" onclick="toggleTheme()" aria-label="Toggle light/dark theme" title="Switch to light theme">
|
<button id="themeToggle" class="theme-toggle-btn" onclick="toggleTheme()" aria-label="Toggle light/dark theme" title="Switch to light theme">
|
||||||
<span id="themeToggleIcon"><svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></span>
|
<span id="themeToggleIcon"><span class="fa-icon fa-icon-moon-stars fa-icon--theme" style="--icon-size:15px;"></span></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -92,6 +97,18 @@
|
|||||||
<div class="client-bar">
|
<div class="client-bar">
|
||||||
<div class="client-label">Prepared for</div>
|
<div class="client-label">Prepared for</div>
|
||||||
<input class="client-input" id="clientName" type="text" placeholder="Client Name" oninput="update()">
|
<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>
|
</div>
|
||||||
|
|
||||||
<!-- ── QUOTE SETTINGS BAR ────────────────────────────────────────
|
<!-- ── QUOTE SETTINGS BAR ────────────────────────────────────────
|
||||||
@@ -102,59 +119,59 @@
|
|||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="quote-settings-bar">
|
<div class="quote-settings-bar">
|
||||||
<div class="qs-group">
|
<div class="qs-group">
|
||||||
<div class="qs-label">Contract Term</div>
|
<div class="qs-label-row">
|
||||||
|
<div class="qs-label">Contract Term & Incentives</div>
|
||||||
|
</div>
|
||||||
<div class="tier-seg-wrap qs-term-wrap">
|
<div class="tier-seg-wrap qs-term-wrap">
|
||||||
<input type="radio" name="contractTerm" id="termM2m" value="m2m" checked onchange="update()">
|
<input type="radio" name="contractTerm" id="termM2m" value="m2m" checked onchange="update()">
|
||||||
<label for="termM2m" class="tier-seg" id="seg-term-m2m">
|
<label for="termM2m" class="tier-seg" id="seg-term-m2m">
|
||||||
<div class="tier-name">Month-to-Month</div>
|
<div class="tier-name">Month-to-Month</div>
|
||||||
<div class="tier-sub">No discount</div>
|
<div class="tier-sub">Maximum flexibility</div>
|
||||||
</label>
|
</label>
|
||||||
<input type="radio" name="contractTerm" id="term12mo" value="12mo" onchange="update()">
|
<input type="radio" name="contractTerm" id="term12mo" value="12mo" onchange="update()">
|
||||||
<label for="term12mo" class="tier-seg" id="seg-term-12mo">
|
<label for="term12mo" class="tier-seg" id="seg-term-12mo">
|
||||||
<div class="tier-name">12-Month</div>
|
<div class="tier-name">12-Month</div>
|
||||||
<div class="tier-sub qs-discount-sub">3% off MRR</div>
|
<div class="tier-sub qs-discount-sub">3% off MRR + 50% off onboarding</div>
|
||||||
</label>
|
</label>
|
||||||
<input type="radio" name="contractTerm" id="term24mo" value="24mo" onchange="update()">
|
<input type="radio" name="contractTerm" id="term24mo" value="24mo" onchange="update()">
|
||||||
<label for="term24mo" class="tier-seg" id="seg-term-24mo">
|
<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-name">24-Month <span class="qs-best-badge">Best Value</span></div>
|
||||||
<div class="tier-sub qs-discount-sub">5% off MRR</div>
|
<div class="tier-sub qs-discount-sub">5% off MRR + complimentary onboarding</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="qs-savings-stack">
|
||||||
<div class="qs-savings-row hidden" id="qsSavingsDisplay">
|
<div class="qs-savings-row hidden" id="qsSavingsDisplay">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="11" height="11" fill="currentColor"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>
|
<span class="fa-icon fa-icon-square-check" style="--icon-size:15px;"></span>
|
||||||
Saving <span id="qsSavingsAmt">$0</span>/mo vs. month-to-month
|
<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>
|
</div>
|
||||||
<div class="qs-divider"></div>
|
<div class="qs-divider"></div>
|
||||||
<div class="qs-right">
|
<div class="qs-right">
|
||||||
<div class="qs-fee-row">
|
<div class="qs-fee-row">
|
||||||
<div class="qs-fee-header">
|
<div class="qs-fee-header">
|
||||||
<label class="qs-fee-label" for="oneTimeFee">Onboarding Fee</label>
|
<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">
|
<label class="qs-toggle-row qs-fee-waive">
|
||||||
<input type="checkbox" id="onboardingWaived" onchange="onWaiveToggle();">
|
<input type="checkbox" id="onboardingWaived" onchange="onWaiveToggle();">
|
||||||
<span class="qs-switch"></span>
|
<span class="qs-switch"></span>
|
||||||
<span class="qs-toggle-label">Waive</span>
|
<span class="qs-toggle-label">Waive</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="sections-toolbar">
|
|
||||||
<button class="btn-toggle-all" id="toggleAllBtn" onclick="toggleAllSections()">
|
|
||||||
<span class="toggle-all-collapse-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:5px;"><polyline points="6 9 12 15 18 9"/><polyline points="6 15 12 9 18 15"/></svg></span>
|
|
||||||
<span class="toggle-all-expand-icon" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:5px;"><polyline points="6 15 12 9 18 15"/><polyline points="6 9 12 15 18 9"/></svg></span>
|
|
||||||
<span class="toggle-all-label">Collapse All</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ────────────────────────────────────────────────────────────
|
<!-- ────────────────────────────────────────────────────────────
|
||||||
SECTION I — SITE ADMIN FEE
|
SECTION I — SITE ADMIN FEE
|
||||||
id="sec-01" sec-open = starts expanded
|
id="sec-01" starts collapsed
|
||||||
#adminFeeDisplay — rendered by update() → fmt(adminFeeNet)
|
#adminFeeDisplay — rendered by update() → fmt(adminFeeNet)
|
||||||
#floorBar — progress bar width%, turns green at 100%
|
#floorBar — progress bar width%, turns green at 100%
|
||||||
#floorProgress — "$X / $650" text label
|
#floorProgress — "$X / $650" text label
|
||||||
@@ -165,20 +182,33 @@
|
|||||||
#fb-total — adminFeeNet total
|
#fb-total — adminFeeNet total
|
||||||
#sec01-summary — badge shown when section collapsed
|
#sec01-summary — badge shown when section collapsed
|
||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="section sec-open" id="sec-01">
|
<div class="section" id="sec-01">
|
||||||
<div class="section-header section-toggle" onclick="toggleSection('sec-01')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-01');event.preventDefault();}">
|
<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">I</div>
|
<div class="section-num">III</div>
|
||||||
<div class="section-title-block">
|
<div class="section-title-block">
|
||||||
<div class="section-title">Environment Management Site Admin Fee</div>
|
<div class="section-title">Site Management</div>
|
||||||
<div class="section-subtitle">Flat monthly fee covering tenant, network, documentation & vendor management</div>
|
|
||||||
<span class="section-badge">Flat Monthly / Environment</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">
|
||||||
|
<span class="section-badge">Operational Oversight</span>
|
||||||
<span id="sec01-summary" class="sec-summary-badge"></span>
|
<span id="sec01-summary" class="sec-summary-badge"></span>
|
||||||
<div class="sec-chevron"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body" id="sec-01-body">
|
</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-content">
|
||||||
|
|
||||||
|
<div class="section-subtitle" style="margin-bottom:16px;">Ongoing oversight for your environment, vendors, and IT documentation so everything stays coordinated, accountable, and easier to support.</div>
|
||||||
|
|
||||||
<div class="admin-fee-header">
|
<div class="admin-fee-header">
|
||||||
<span class="admin-fee-title">Site Admin Fee</span>
|
<span class="admin-fee-title">Site Admin Fee</span>
|
||||||
<span id="adminFeeDisplay" class="admin-fee-val">$150/mo</span>
|
<span id="adminFeeDisplay" class="admin-fee-val">$150/mo</span>
|
||||||
@@ -188,18 +218,6 @@
|
|||||||
<span class="qs-toggle-label">Waive</span>
|
<span class="qs-toggle-label">Waive</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="admin-fee-sub">Calculated from services below · floor $150/mo</div>
|
|
||||||
|
|
||||||
<div class="progress-wrap">
|
|
||||||
<div class="progress-label">
|
|
||||||
<span>Minimum Engagement Threshold</span>
|
|
||||||
<span id="floorProgress">$0 / $650</span>
|
|
||||||
</div>
|
|
||||||
<div class="progress-track">
|
|
||||||
<div class="progress-fill" id="floorBar" style="width:0%"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="floorNote" class="floor-note"></div>
|
|
||||||
|
|
||||||
<table class="fee-table" id="feeBreakdown">
|
<table class="fee-table" id="feeBreakdown">
|
||||||
<tr><td>Base Site Admin</td><td id="fb-base">—</td></tr>
|
<tr><td>Base Site Admin</td><td id="fb-base">—</td></tr>
|
||||||
@@ -209,26 +227,26 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="admin-waive-savings hidden" id="adminWaivedSavings">
|
<div class="admin-waive-savings hidden" id="adminWaivedSavings">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="11" height="11" fill="currentColor"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>
|
<span class="fa-icon fa-icon-square-check" style="--icon-size:11px;"></span>
|
||||||
Site admin fee waived — saving <span id="adminWaivedAmt">$0</span>/mo on this quote
|
Site oversight fee waived on this quote — included by SVS on this proposal, saving <span id="adminWaivedAmt">$0</span>/mo
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- What's Covered collapsible -->
|
<!-- What's Covered collapsible -->
|
||||||
<div class="collapsible-header collapsible-header--mt16" onclick="toggleCollapsible('adminCovered')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('adminCovered');event.preventDefault();}">
|
<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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
<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's Covered by the Admin Fee</span>
|
<span class="collapsible-label">What This Fee Covers Behind the Scenes</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsible-body" id="adminCovered">
|
<div class="collapsible-body" id="adminCovered">
|
||||||
<div class="feature-card-grid">
|
<div class="feature-card-grid">
|
||||||
<div class="feature-card"><div class="feature-card-title"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" width="16" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M96 0C43 0 0 43 0 96V416c0 53 43 96 96 96H344.2c-1.5-9.5-2.2-19.2-2.2-29.1V384H96c-17.7 0-32-14.3-32-32V96c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32v84.2c19.4 6.7 37.3 17.3 52.9 30.6c5.3-2.7 11.3-4.8 17.1-4.8c23.7 0 42.9 19.2 42.9 42.9V272c16.8 10.4 32 23.4 44.8 38.8V176c0-53-43-96-96-96H416V96c0-53-43-96-96-96H96zM224 320a64 64 0 1 1 128 0 64 64 0 1 1 -128 0zm-32 64h192c17.7 0 32 14.3 32 32v32H160V416c0-17.7 14.3-32 32-32zM128 176c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H144c-8.8 0-16-7.2-16-16V176zm0 96c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H144c-8.8 0-16-7.2-16-16V272zm128-96c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H272c-8.8 0-16-7.2-16-16V176zm0 96c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H272c-8.8 0-16-7.2-16-16V272zm128-96c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H384c-8.8 0-16-7.2-16-16V176zM496 512a144 144 0 1 0 0-288 144 144 0 1 0 0 288zm0-96a48 48 0 1 1 0-96 48 48 0 1 1 0 96z"/></svg> Tenant & 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-users-gear fa-icon--accent" style="--icon-size:16px;margin-right:8px;"></span> Tenant & 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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" width="16" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M256 64H384v64H256V64zM240 0c-26.5 0-48 21.5-48 48v96c0 26.5 21.5 48 48 48h48v32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h176v32H160c-26.5 0-48 21.5-48 48v96c0 26.5 21.5 48 48 48h128c26.5 0 48-21.5 48-48V368c0-26.5-21.5-48-48-48H240V288H400v32H352c-26.5 0-48 21.5-48 48v96c0 26.5 21.5 48 48 48h128c26.5 0 48-21.5 48-48V368c0-26.5-21.5-48-48-48H432V288H608c17.7 0 32-14.3 32-32s-14.3-32-32-32H352V192h48c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48H240zM192 400H288v64H192V400zm256 0H544v64H448V400z"/></svg> Network & 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-network-wired fa-icon--accent" style="--icon-size:16px;margin-right:8px;"></span> Network & 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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="14" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128h128L256 0zM112 256H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16z"/></svg> Documentation & 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-book-open-cover fa-icon--accent" style="--icon-size:15px;margin-right:8px;"></span> Documentation & 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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" width="16" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M272.2 64.6l-51.1 51.1c-15.3 4.2-29.5 11.9-41.5 22.5L153 161.9C142.8 171 129.5 176 115.8 176H96V304c20.4 5.3 38.7 16.3 52.9 31.4l168.9 182.6c6.5 7 15.9 10.9 25.8 10.1C367.9 525.1 384 507.5 384 486.2v-25.9l-.1 .1c-16.4 16.4-43 16.4-59.4 0L234.5 370.5c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L370.1 415.5 416 369.6V316.3c0-18 7.2-35.3 19.9-48l38.1-38.1c-19.7-13.3-41.9-22.5-65.8-26.2l-42.8-6.8c-11.1-1.8-22.4-2.8-33.9-2.8c-19.7 0-38.9 2.8-57.2 7.8l-31.1-31.1c12.7-5.4 26.5-8.3 40.8-7.9C299 163.1 321 170 340 182l92.4-92.4C409.3 72.4 380.8 64 350.4 64c-27.8 0-54.7 8.3-77.8 23.1l-.4-22.5zM544 200.9c19.4 26.9 30.8 59.8 30.8 95.5c0 14.7-1.8 29.1-5.3 42.8l28.6 28.6c6.2 6.2 9.7 14.6 9.7 23.4V512h-96V461.3L412 358.3c-7.9-7.9-7.9-20.7 0-28.6l10.7-10.7 25.8-25.8c3.9-3.9 9.1-6.1 14.6-6.1H496c8.8 0 16-7.2 16-16V240c0-8.8-7.2-16-16-16H464.4l-53.5-53.5C428.6 164.4 452 160 476.8 160c24.2 0 47.5 5.4 67.2 14.9V200.9zM96 160H64c-17.7 0-32 14.3-32 32V448c0 17.7 14.3 32 32 32H96c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32z"/></svg> 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-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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M256 0c4.6 0 9.2 1 13.4 2.9L457.7 82.8c22 9.3 38.4 31 38.3 57.2c-.5 99.2-41.3 280.7-213.6 363.2c-16.7 8-36.1 8-52.8 0C57.3 420.7 16.5 239.2 16 140c-.1-26.2 16.3-47.9 38.3-57.2L242.7 2.9C246.8 1 251.4 0 256 0zm0 66.8V444.8C394 378 431.1 230.1 432 141.4L256 66.8z"/></svg> 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-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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M32 32c17.7 0 32 14.3 32 32V400c0 8.8 7.2 16 16 16H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H80c-44.2 0-80-35.8-80-80V64C0 46.3 14.3 32 32 32zm96 96c0-17.7 14.3-32 32-32l192 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-192 0c-17.7 0-32-14.3-32-32zm32 64H288c17.7 0 32 14.3 32 32s-14.3 32-32 32H160c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 96H352c17.7 0 32 14.3 32 32s-14.3 32-32 32H160c-17.7 0-32-14.3-32-32s14.3-32 32-32z"/></svg> Reporting & 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-chart-line-up fa-icon--accent" style="--icon-size:16px;margin-right:8px;"></span> Reporting & 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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" width="18" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M0 336c0 79.5 64.5 144 144 144H512c70.7 0 128-57.3 128-128c0-61.9-44-113.6-102.4-125.4c4.1-10.7 6.4-22.4 6.4-34.6c0-53-43-96-96-96c-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32C167.6 32 96 103.6 96 192c0 2.7 .1 5.4 .2 8.1C40.2 219.8 0 273.2 0 336z"/></svg> 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-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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg> 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-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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="18" height="16" fill="var(--accent)" style="margin-right:8px;flex-shrink:0;vertical-align:middle;"><path d="M0 80C0 53.5 21.5 32 48 32h96c26.5 0 48 21.5 48 48V96H384V80c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v96c0 26.5-21.5 48-48 48H432c-26.5 0-48-21.5-48-48v-16H192v16c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80zM48 336H144V320c0-26.5 21.5-48 48-48H384c26.5 0 48 21.5 48 48v16h96c26.5 0 48 21.5 48 48v96c0 26.5-21.5 48-48 48H432c-26.5 0-48-21.5-48-48V416H192v16c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V384c0-26.5 21.5-48 48-48z"/></svg> 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 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>
|
||||||
|
|
||||||
@@ -247,54 +265,90 @@
|
|||||||
Row highlights: #row-ext #row-pwm #row-inky #row-zt
|
Row highlights: #row-ext #row-pwm #row-inky #row-zt
|
||||||
#sec02-summary — badge shown when collapsed
|
#sec02-summary — badge shown when collapsed
|
||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="section sec-open" id="sec-02">
|
<div class="section" id="sec-02">
|
||||||
<div class="section-header section-toggle" onclick="toggleSection('sec-02')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-02');event.preventDefault();}">
|
<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">II</div>
|
<div class="section-num">I</div>
|
||||||
<div class="section-title-block">
|
<div class="section-title-block">
|
||||||
<div class="section-title">User Package</div>
|
<div class="section-title">User Package</div>
|
||||||
<div class="section-subtitle">Per-user monthly services — identity, email, security & helpdesk</div>
|
</div>
|
||||||
<span class="section-badge">Per User / Month</span>
|
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
|
||||||
<div class="sec-collapsed-counter" onclick="event.stopPropagation()">
|
<div class="sec-controls-row" onclick="event.stopPropagation()">
|
||||||
<div class="num-stepper">
|
<div class="num-stepper">
|
||||||
<button class="step-btn" onclick="stepInput('userCount',-1)">−</button>
|
<button class="step-btn" aria-label="Decrease users" onclick="stepInput('userCount',-1)">−</button>
|
||||||
<input class="num-input" id="userCount" type="number" min="0" value="1" oninput="update()">
|
<input class="num-input" id="userCount" type="number" min="0" value="1" oninput="update()">
|
||||||
<button class="step-btn" onclick="stepInput('userCount',1)">+</button>
|
<button class="step-btn" aria-label="Increase users" onclick="stepInput('userCount',1)">+</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span class="section-badge">Managed Users</span>
|
||||||
<span id="sec02-summary" class="sec-summary-badge"></span>
|
<span id="sec02-summary" class="sec-summary-badge"></span>
|
||||||
<div class="sec-chevron"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body" id="sec-02-body">
|
<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">
|
<div class="section-content">
|
||||||
|
|
||||||
<!-- Base Rate Toggle -->
|
<!-- Base Rate Toggle -->
|
||||||
<div class="pill-toggle">
|
<div class="pill-toggle">
|
||||||
<input type="radio" name="baseRate" id="rateM365" value="m365" checked onchange="update()">
|
<input type="radio" name="baseRate" id="rateM365" value="m365" checked onchange="update()">
|
||||||
<label for="rateM365">
|
<label for="rateM365">
|
||||||
<span class="pill-price">$130<small>/user/mo</small></span>
|
<span class="pill-price">
|
||||||
<span class="pill-desc">M365 Included — Identity, Email & Business Protection</span>
|
<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 & business protection in one seat</span>
|
||||||
|
<span class="pill-savings" id="m365SavingsLine"></span>
|
||||||
</label>
|
</label>
|
||||||
<input type="radio" name="baseRate" id="rateBYOL" value="byol" onchange="update()">
|
<input type="radio" name="baseRate" id="rateBYOL" value="byol" onchange="update()">
|
||||||
<label for="rateBYOL">
|
<label for="rateBYOL">
|
||||||
<span class="pill-price">$110<small>/user/mo</small></span>
|
<span class="pill-price">$110<small>/user/mo</small></span>
|
||||||
<span class="pill-desc">BYOL — Bring Your Own License (M365 or Google Workspace)</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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="byolCalloutGreen" class="callout-green">
|
<div id="byolCalloutGreen" class="callout-green">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14" height="14" fill="var(--green)" style="flex-shrink:0;margin-top:2px;"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg><span>Bundled M365 Business Premium saves up to $15/user/mo vs buying retail — runs ~$29–36/mo CAD on its own</span>
|
<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>
|
||||||
<div id="byolCalloutRed" class="callout-red hidden">
|
<div id="byolCalloutRed" class="callout-red hidden">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14" height="14" fill="#e06070" style="flex-shrink:0;margin-top:2px;"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zm-32 224a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg><span>Missed saving — switching to M365 Business Premium would save <span id="byolRedSavings">$150</span>/mo across your team vs retail pricing</span>
|
<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>
|
</div>
|
||||||
|
|
||||||
<!-- What's Included collapsible -->
|
<!-- What's Included collapsible -->
|
||||||
<div class="collapsible-header" onclick="toggleCollapsible('userIncluded')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('userIncluded');event.preventDefault();}">
|
<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 open" id="userIncluded-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
<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>
|
<span class="collapsible-label">What's Included in This Package</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsible-body open" id="userIncluded">
|
<div class="collapsible-body" id="userIncluded">
|
||||||
<ul class="feature-list">
|
<ul class="feature-list">
|
||||||
<li class="m365-feature">Microsoft 365 Business Premium</li>
|
<li class="m365-feature">Microsoft 365 Business Premium</li>
|
||||||
<li class="m365-feature">Word, Excel, PowerPoint, Teams, Exchange</li>
|
<li class="m365-feature">Word, Excel, PowerPoint, Teams, Exchange</li>
|
||||||
@@ -314,9 +368,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- Per-User Add-Ons collapsible -->
|
<!-- Per-User Add-Ons collapsible -->
|
||||||
<div class="collapsible-header collapsible-header--addon" onclick="toggleCollapsible('addonsA')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('addonsA');event.preventDefault();}">
|
<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 open" id="addonsA-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
<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">Per-User Add-Ons</span>
|
<span class="collapsible-label">WORKFORCE ADD-ONS</span>
|
||||||
<div id="addonsA-preview" class="addon-preview-wrap" style="display:none">
|
<div id="addonsA-preview" class="addon-preview-wrap" style="display:none">
|
||||||
<span class="addon-preview-pill" data-addon="addExtHours">Extended Hours</span>
|
<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="addPWM">1Password</span>
|
||||||
@@ -324,27 +378,27 @@
|
|||||||
<span class="addon-preview-pill" data-addon="addZT">Zero Trust</span>
|
<span class="addon-preview-pill" data-addon="addZT">Zero Trust</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsible-body open" id="addonsA">
|
<div class="collapsible-body" id="addonsA">
|
||||||
<div class="addon-grid">
|
<div class="addon-grid">
|
||||||
<label class="addon-row" id="row-ext" onclick="toggleAddon('addExtHours','row-ext');update()">
|
<label class="addon-row" id="row-ext" onclick="toggleAddon('addExtHours','row-ext');update()">
|
||||||
<input type="checkbox" id="addExtHours">
|
<input type="checkbox" id="addExtHours">
|
||||||
<div><div class="addon-name">Extended Help Desk Hours</div><div class="addon-desc">8am–8pm Mon–Fri coverage via staggered shifts</div></div>
|
<div><div class="addon-name">Extended Help Desk Hours</div><div class="addon-desc">8am–8pm Mon–Fri coverage via staggered shifts</div></div>
|
||||||
<span class="addon-price">+$25/user/mo</span>
|
<span class="addon-price" data-price-key="ADDON_EXT_HOURS" data-price-unit="user">+$25/user/mo</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="addon-row" id="row-pwm" onclick="toggleAddon('addPWM','row-pwm');update()">
|
<label class="addon-row" id="row-pwm" onclick="toggleAddon('addPWM','row-pwm');update()">
|
||||||
<input type="checkbox" id="addPWM">
|
<input type="checkbox" id="addPWM">
|
||||||
<div><div class="addon-name">1Password Management</div><div class="addon-desc">Business password vault, admin console & SSO integration</div></div>
|
<div><div class="addon-name">1Password Management</div><div class="addon-desc">Business password vault, admin console & SSO integration</div></div>
|
||||||
<span class="addon-price">+$9/user/mo</span>
|
<span class="addon-price" data-price-key="ADDON_1PASSWORD" data-price-unit="user">+$9/user/mo</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="addon-row" id="row-inky" onclick="toggleAddon('addINKY','row-inky');update()">
|
<label class="addon-row" id="row-inky" onclick="toggleAddon('addINKY','row-inky');update()">
|
||||||
<input type="checkbox" id="addINKY">
|
<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>
|
<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">+$5/user/mo</span>
|
<span class="addon-price" data-price-key="ADDON_INKY" data-price-unit="user">+$8/user/mo</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="addon-row" id="row-zt" onclick="toggleAddon('addZT','row-zt');update()">
|
<label class="addon-row" id="row-zt" onclick="toggleAddon('addZT','row-zt');update()">
|
||||||
<input type="checkbox" id="addZT">
|
<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>
|
<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">+$55/user/mo</span>
|
<span class="addon-price" data-price-key="ADDON_ZERO_TRUST_USER" data-price-unit="user">+$55/user/mo</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -360,33 +414,33 @@
|
|||||||
Row highlights: #row-bmb #row-usb
|
Row highlights: #row-bmb #row-usb
|
||||||
#sec03-summary — badge shown when collapsed
|
#sec03-summary — badge shown when collapsed
|
||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="section sec-open" id="sec-03">
|
<div class="section" id="sec-03">
|
||||||
<div class="section-header section-toggle" onclick="toggleSection('sec-03')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-03');event.preventDefault();}">
|
<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">III</div>
|
<div class="section-num">II</div>
|
||||||
<div class="section-title-block">
|
<div class="section-title-block">
|
||||||
<div class="section-title">Endpoint Package</div>
|
<div class="section-title">Endpoint Package</div>
|
||||||
<div class="section-subtitle">Per-device managed protection — workstations & laptops</div>
|
</div>
|
||||||
<span class="section-badge">$35 / Endpoint / Month</span>
|
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
|
||||||
<div class="sec-collapsed-counter" onclick="event.stopPropagation()">
|
<div class="sec-controls-row" onclick="event.stopPropagation()">
|
||||||
<div class="num-stepper">
|
<div class="num-stepper">
|
||||||
<button class="step-btn" onclick="stepInput('endpointCount',-1)">−</button>
|
<button class="step-btn" aria-label="Decrease endpoints" onclick="stepInput('endpointCount',-1)">−</button>
|
||||||
<input class="num-input" id="endpointCount" type="number" min="0" value="1" oninput="update()">
|
<input class="num-input" id="endpointCount" type="number" min="0" value="1" oninput="update()">
|
||||||
<button class="step-btn" onclick="stepInput('endpointCount',1)">+</button>
|
<button class="step-btn" aria-label="Increase endpoints" onclick="stepInput('endpointCount',1)">+</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span class="section-badge">Protected Endpoints</span>
|
||||||
<span id="sec03-summary" class="sec-summary-badge"></span>
|
<span id="sec03-summary" class="sec-summary-badge"></span>
|
||||||
<div class="sec-chevron"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body" id="sec-03-body">
|
<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">
|
<div class="section-content">
|
||||||
|
|
||||||
<!-- What's Included collapsible -->
|
<!-- What's Included collapsible -->
|
||||||
<div class="collapsible-header" onclick="toggleCollapsible('endpointIncluded')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('endpointIncluded');event.preventDefault();}">
|
<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 open" id="endpointIncluded-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
<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>
|
<span class="collapsible-label">What's Included in This Package</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsible-body open" id="endpointIncluded">
|
<div class="collapsible-body" id="endpointIncluded">
|
||||||
<ul class="feature-list">
|
<ul class="feature-list">
|
||||||
<li>Managed EDR threat protection</li>
|
<li>Managed EDR threat protection</li>
|
||||||
<li>1 full workstation backup included</li>
|
<li>1 full workstation backup included</li>
|
||||||
@@ -401,25 +455,25 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- Per-Endpoint Add-Ons collapsible -->
|
<!-- Per-Endpoint Add-Ons collapsible -->
|
||||||
<div class="collapsible-header collapsible-header--addon" onclick="toggleCollapsible('addonsB')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('addonsB');event.preventDefault();}">
|
<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 open" id="addonsB-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
<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">Per-Endpoint Add-Ons</span>
|
<span class="collapsible-label">ENDPOINT BUSINESSGUARD ADD-ONS</span>
|
||||||
<div id="addonsB-preview" class="addon-preview-wrap" style="display:none">
|
<div id="addonsB-preview" class="addon-preview-wrap" style="display:none">
|
||||||
<span class="addon-preview-pill" data-addon="addBMB">Bare Metal Backup</span>
|
<span class="addon-preview-pill" data-addon="addBMB">Bare Metal Backup</span>
|
||||||
<span class="addon-preview-pill" data-addon="addUSB">USB Blocking</span>
|
<span class="addon-preview-pill" data-addon="addUSB">USB Blocking</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsible-body open" id="addonsB">
|
<div class="collapsible-body" id="addonsB">
|
||||||
<div class="addon-grid">
|
<div class="addon-grid">
|
||||||
<label class="addon-row" id="row-bmb" onclick="toggleAddon('addBMB','row-bmb');update()">
|
<label class="addon-row" id="row-bmb" onclick="toggleAddon('addBMB','row-bmb');update()">
|
||||||
<input type="checkbox" id="addBMB">
|
<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 — local & cloud retention</div></div>
|
<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 & cloud retention</div></div>
|
||||||
<span class="addon-price">+$25/endpoint/mo</span>
|
<span class="addon-price" data-price-key="ADDON_BARE_METAL_BACKUP" data-price-unit="endpoint">+$25/endpoint/mo</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="addon-row" id="row-usb" onclick="toggleAddon('addUSB','row-usb');update()">
|
<label class="addon-row" id="row-usb" onclick="toggleAddon('addUSB','row-usb');update()">
|
||||||
<input type="checkbox" id="addUSB">
|
<input type="checkbox" id="addUSB">
|
||||||
<div><div class="addon-name">USB Device Blocking</div><div class="addon-desc">Policy-enforced USB control — block unauthorized removable media</div></div>
|
<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">+$4/endpoint/mo</span>
|
<span class="addon-price" data-price-key="ADDON_USB_BLOCKING" data-price-unit="endpoint">+$4/endpoint/mo</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -437,28 +491,28 @@
|
|||||||
#sec04-summary — badge shown when collapsed
|
#sec04-summary — badge shown when collapsed
|
||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="section" id="sec-04">
|
<div class="section" id="sec-04">
|
||||||
<div class="section-header section-toggle" onclick="toggleSection('sec-04')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-04');event.preventDefault();}">
|
<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-num">IV</div>
|
||||||
<div class="section-title-block">
|
<div class="section-title-block">
|
||||||
<div class="section-title">Server Management</div>
|
<div class="section-title">Server Management</div>
|
||||||
<div class="section-subtitle">Dedicated management for physical & virtual servers</div>
|
</div>
|
||||||
<span class="section-badge">$120 / Server / Month</span>
|
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
|
||||||
<div class="sec-collapsed-counter" onclick="event.stopPropagation()">
|
<div class="sec-controls-row" onclick="event.stopPropagation()">
|
||||||
<div class="num-stepper">
|
<div class="num-stepper">
|
||||||
<button class="step-btn" onclick="stepInput('serverCount',-1)">−</button>
|
<button class="step-btn" aria-label="Decrease servers" onclick="stepInput('serverCount',-1)">−</button>
|
||||||
<input class="num-input" id="serverCount" type="number" min="0" value="0" oninput="update()">
|
<input class="num-input" id="serverCount" type="number" min="0" value="0" oninput="update()">
|
||||||
<button class="step-btn" onclick="stepInput('serverCount',1)">+</button>
|
<button class="step-btn" aria-label="Increase servers" onclick="stepInput('serverCount',1)">+</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span class="section-badge">Managed Servers</span>
|
||||||
<span id="sec04-summary" class="sec-summary-badge"></span>
|
<span id="sec04-summary" class="sec-summary-badge"></span>
|
||||||
<div class="sec-chevron"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></div>
|
</div>
|
||||||
|
<div class="section-subtitle">Dedicated management for physical & virtual servers</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body" id="sec-04-body" style="display:none;">
|
<div class="section-body" id="sec-04-body" style="display:none;">
|
||||||
<div class="section-content">
|
<div class="section-content">
|
||||||
|
|
||||||
<div class="collapsible-header" onclick="toggleCollapsible('serverIncluded')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleCollapsible('serverIncluded');event.preventDefault();}">
|
<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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
<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>
|
<span class="collapsible-label">What's Included in Server Management</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsible-body" id="serverIncluded">
|
<div class="collapsible-body" id="serverIncluded">
|
||||||
@@ -488,29 +542,29 @@
|
|||||||
#sec05-summary — badge shown when collapsed
|
#sec05-summary — badge shown when collapsed
|
||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="section" id="sec-05">
|
<div class="section" id="sec-05">
|
||||||
<div class="section-header section-toggle" onclick="toggleSection('sec-05')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-05');event.preventDefault();}">
|
<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-num">V</div>
|
||||||
<div class="section-title-block">
|
<div class="section-title-block">
|
||||||
<div class="section-title">Zero Trust Networking <span class="section-title-tag">HaaS</span></div>
|
<div class="section-title">Zero Trust Networking <span class="section-title-tag">HaaS</span></div>
|
||||||
<div class="section-subtitle">Cytracom-powered ZT network access — seats & managed hardware as a service</div>
|
</div>
|
||||||
<span class="section-badge">Per User + Per Device / Month</span>
|
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
|
||||||
<div class="sec-collapsed-counter" onclick="event.stopPropagation()">
|
<div class="sec-controls-row" onclick="event.stopPropagation()">
|
||||||
<div class="num-stepper">
|
<div class="num-stepper">
|
||||||
<button class="step-btn" onclick="stepInput('ztNetSeats',-1)">−</button>
|
<button class="step-btn" aria-label="Decrease ZT seats" onclick="stepInput('ztNetSeats',-1)">−</button>
|
||||||
<input class="num-input" id="ztNetSeats" type="number" min="0" value="0" oninput="update()">
|
<input class="num-input" id="ztNetSeats" type="number" min="0" value="0" oninput="update()">
|
||||||
<button class="step-btn" onclick="stepInput('ztNetSeats',1)">+</button>
|
<button class="step-btn" aria-label="Increase ZT seats" onclick="stepInput('ztNetSeats',1)">+</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span class="section-badge">ZT Seats</span>
|
||||||
<span id="sec05-summary" class="sec-summary-badge"></span>
|
<span id="sec05-summary" class="sec-summary-badge"></span>
|
||||||
<div class="sec-chevron"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></div>
|
</div>
|
||||||
|
<div class="section-subtitle">Cytracom-powered ZT network access — seats & managed hardware as a service</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body" id="sec-05-body" style="display:none;">
|
<div class="section-body" id="sec-05-body" style="display:none;">
|
||||||
<div class="section-content">
|
<div class="section-content">
|
||||||
|
|
||||||
<!-- ZT relationship explainer -->
|
<!-- ZT relationship explainer -->
|
||||||
<div class="callout-green" style="margin-bottom:20px;">
|
<div class="callout-green" style="margin-bottom:20px;">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14" height="14" fill="var(--green)" style="flex-shrink:0;margin-top:2px;"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zm-32 224a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
|
<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>
|
<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 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>
|
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>
|
||||||
@@ -522,9 +576,9 @@
|
|||||||
<div class="input-sublabel">Managed router/firewall hardware · $100/device/mo</div>
|
<div class="input-sublabel">Managed router/firewall hardware · $100/device/mo</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="num-stepper">
|
<div class="num-stepper">
|
||||||
<button class="step-btn" onclick="stepInput('ztNetRouters',-1)">−</button>
|
<button class="step-btn" aria-label="Decrease ZT routers" onclick="stepInput('ztNetRouters',-1)">−</button>
|
||||||
<input class="num-input" id="ztNetRouters" type="number" min="0" value="0" oninput="update()">
|
<input class="num-input" id="ztNetRouters" type="number" min="0" value="0" oninput="update()">
|
||||||
<button class="step-btn" onclick="stepInput('ztNetRouters',1)">+</button>
|
<button class="step-btn" aria-label="Increase ZT routers" onclick="stepInput('ztNetRouters',1)">+</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -546,22 +600,22 @@
|
|||||||
#sec06-summary — badge shown when collapsed
|
#sec06-summary — badge shown when collapsed
|
||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="section" id="sec-06">
|
<div class="section" id="sec-06">
|
||||||
<div class="section-header section-toggle" onclick="toggleSection('sec-06')" tabindex="0" role="button" onkeydown="if(event.key==='Enter'||event.key===' '){toggleSection('sec-06');event.preventDefault();}">
|
<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-num">VI</div>
|
||||||
<div class="section-title-block">
|
<div class="section-title-block">
|
||||||
<div class="section-title">VoIP / Unified Communications <span class="section-title-tag">UCaaS</span></div>
|
<div class="section-title">VoIP / Unified Communications <span class="section-title-tag">UCaaS</span></div>
|
||||||
<div class="section-subtitle">United Cloud-powered business phone — seats, features & optional desk phones</div>
|
</div>
|
||||||
<span class="section-badge">Per Seat / Month</span>
|
<div class="sec-chevron"><span class="fa-icon fa-icon-chevron-down" style="--icon-size:14px;"></span></div>
|
||||||
<div class="sec-collapsed-counter" onclick="event.stopPropagation()">
|
<div class="sec-controls-row" onclick="event.stopPropagation()">
|
||||||
<div class="num-stepper">
|
<div class="num-stepper">
|
||||||
<button class="step-btn" onclick="stepInput('voipSeats',-1)">−</button>
|
<button class="step-btn" aria-label="Decrease VoIP seats" onclick="stepInput('voipSeats',-1)">−</button>
|
||||||
<input class="num-input" id="voipSeats" type="number" min="0" value="0" oninput="update()">
|
<input class="num-input" id="voipSeats" type="number" min="0" value="0" oninput="update()">
|
||||||
<button class="step-btn" onclick="stepInput('voipSeats',1)">+</button>
|
<button class="step-btn" aria-label="Increase VoIP seats" onclick="stepInput('voipSeats',1)">+</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span class="section-badge">VoIP Seats</span>
|
||||||
<span id="sec06-summary" class="sec-summary-badge"></span>
|
<span id="sec06-summary" class="sec-summary-badge"></span>
|
||||||
<div class="sec-chevron"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></div>
|
</div>
|
||||||
|
<div class="section-subtitle">United Cloud-powered business phone — seats, features & optional desk phones</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body" id="sec-06-body" style="display:none;">
|
<div class="section-body" id="sec-06-body" style="display:none;">
|
||||||
<div class="section-content">
|
<div class="section-content">
|
||||||
@@ -593,12 +647,12 @@
|
|||||||
<label class="addon-row" id="row-vphone" onclick="toggleAddon('addVoipPhone','row-vphone');update()">
|
<label class="addon-row" id="row-vphone" onclick="toggleAddon('addVoipPhone','row-vphone');update()">
|
||||||
<input type="checkbox" id="addVoipPhone">
|
<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>
|
<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">+$15/seat/mo</span>
|
<span class="addon-price" data-price-key="VOIP_PHONE_RATE" data-price-unit="seat">+$15/seat/mo</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="addon-row" id="row-vfax" onclick="toggleAddon('addVoipFax','row-vfax');update()">
|
<label class="addon-row" id="row-vfax" onclick="toggleAddon('addVoipFax','row-vfax');update()">
|
||||||
<input type="checkbox" id="addVoipFax">
|
<input type="checkbox" id="addVoipFax">
|
||||||
<div><div class="addon-name">eFax Line</div><div class="addon-desc">Digital fax number — send & receive via email or portal</div></div>
|
<div><div class="addon-name">eFax Line</div><div class="addon-desc">Digital fax number — send & receive via email or portal</div></div>
|
||||||
<span class="addon-price">+$10/line/mo</span>
|
<span class="addon-price" data-price-key="VOIP_FAX_RATE" data-price-unit="line">+$10/line/mo</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -625,13 +679,46 @@
|
|||||||
nudgeBanner must stay INSIDE .sidebar-body or it gets clipped.
|
nudgeBanner must stay INSIDE .sidebar-body or it gets clipped.
|
||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="side-col">
|
<div class="side-col">
|
||||||
|
<div class="sidebar-focus-backdrop" id="sidebarFocusBackdrop" onclick="closeSidebarFocus()" aria-hidden="true"></div>
|
||||||
<div class="sidebar-utility">
|
<div class="sidebar-utility">
|
||||||
<button type="button" class="btn-reset-quote" onclick="openResetConfirm()">Reset Quote</button>
|
<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>
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
|
<div class="sidebar-header-row">
|
||||||
<div class="sidebar-title">SVS MSP — Live Quote</div>
|
<div class="sidebar-title">SVS MSP — Live Quote</div>
|
||||||
<div class="sidebar-client" id="clientNameDisplay">Client Name</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>
|
</div>
|
||||||
<!-- ── INSIGHT NUDGE BANNER ──────────────────────────────────────
|
<!-- ── INSIGHT NUDGE BANNER ──────────────────────────────────────
|
||||||
Sits flush under .sidebar-header, full width of .sidebar.
|
Sits flush under .sidebar-header, full width of .sidebar.
|
||||||
@@ -641,16 +728,29 @@
|
|||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div id="nudgeBanner" class="nudge-banner amber hidden">
|
<div id="nudgeBanner" class="nudge-banner amber hidden">
|
||||||
<div class="nudge-header-row">
|
<div class="nudge-header-row">
|
||||||
<span class="nudge-banner-label" style="margin-bottom:0;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="12" height="14" fill="currentColor" style="margin-right:6px;vertical-align:middle;"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 352H272c0 0 0 0 0 0H112c0 0 0 0 0 0z"/></svg> Insight <span id="nudgeCounter" class="nudge-counter"></span></span>
|
<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">
|
<div class="nudge-nav-group">
|
||||||
<button onclick="cycleNudge(-1)" class="nudge-nav-btn" title="Previous"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg></button>
|
<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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></button>
|
<button onclick="cycleNudge(1)" class="nudge-nav-btn" title="Next"><span class="fa-icon fa-icon-chevron-right"></span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span id="nudgeText"></span>
|
<span id="nudgeText"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-body">
|
<div class="sidebar-body">
|
||||||
<!-- ── SIDEBAR SERVICE LINES ──────────────────────────────────
|
<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.
|
Each .sidebar-line hidden by default.
|
||||||
update() calls show(id, condition) to toggle .hidden.
|
update() calls show(id, condition) to toggle .hidden.
|
||||||
sl-users / sl-endpoints / sl-servers / sl-zt / sl-voip
|
sl-users / sl-endpoints / sl-servers / sl-zt / sl-voip
|
||||||
@@ -658,83 +758,126 @@
|
|||||||
sl-admin is always visible (never hidden).
|
sl-admin is always visible (never hidden).
|
||||||
sl-*-sub rows are sub-labels (e.g. "5 × $130/user")
|
sl-*-sub rows are sub-labels (e.g. "5 × $130/user")
|
||||||
toggled via style.display not .hidden class.
|
toggled via style.display not .hidden class.
|
||||||
──────────────────────────────────────────────────────────── -->
|
────────────────────────────────────────────────────────── -->
|
||||||
<div id="sidebarLines">
|
<div id="sidebarLines">
|
||||||
<div class="sidebar-note hidden" id="sideNote-m365"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14" height="14" fill="var(--green)" class="note-icon"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg> Bundled M365 saves client up to <strong id="m365SaveAmt" class="savings-amount">—</strong>/mo vs retail licensing</div>
|
|
||||||
<div class="sidebar-note hidden" id="sideNote-byol"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14" height="14" fill="var(--amber)" class="note-icon"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zm-32 224a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg> BYOL selected — client handles their own Microsoft or Google licensing</div>
|
|
||||||
<div class="sidebar-line hidden" id="sl-users">
|
<div class="sidebar-line hidden" id="sl-users">
|
||||||
<span><span class="lbl-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" width="14" height="13" fill="currentColor" style="vertical-align:middle;"><path d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192h42.7c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0H21.3C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7h42.7C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3H405.3zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352H378.7C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7H154.7c-14.7 0-26.7-11.9-26.7-26.7z"/></svg></span> Users</span>
|
<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>
|
<span class="val" id="sl-users-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sl-sub hidden" id="sl-users-sub"></div>
|
<div class="sl-sub hidden" id="sl-users-sub"></div>
|
||||||
<div class="sidebar-line hidden" id="sl-endpoints">
|
<div class="sidebar-line hidden" id="sl-endpoints">
|
||||||
<span><span class="lbl-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="14" height="13" fill="currentColor" style="vertical-align:middle;"><path d="M64 0C28.7 0 0 28.7 0 64V352c0 35.3 28.7 64 64 64H240l-10.7 32H160c-17.7 0-32 14.3-32 32s14.3 32 32 32H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H346.7L336 416H512c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64H64zM512 64V352H64V64H512z"/></svg></span> Endpoints</span>
|
<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>
|
<span class="val" id="sl-endpoints-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sl-sub hidden" id="sl-endpoints-sub"></div>
|
<div class="sl-sub hidden" id="sl-endpoints-sub"></div>
|
||||||
<div class="sidebar-line hidden" id="sl-servers">
|
<div class="sidebar-line hidden" id="sl-servers">
|
||||||
<span><span class="lbl-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="13" height="13" fill="currentColor" style="vertical-align:middle;"><path d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"/></svg></span> Servers</span>
|
<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>
|
<span class="val" id="sl-servers-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-line hidden" id="sl-zt">
|
<div class="sidebar-line hidden" id="sl-zt">
|
||||||
<span><span class="lbl-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="11" height="13" fill="currentColor" style="vertical-align:middle;"><path d="M144 144v48H304V144c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192V144C80 64.5 144.5 0 224 0s144 64.5 144 144v48h16c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V256c0-35.3 28.7-64 64-64H80z"/></svg></span> Zero Trust</span>
|
<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>
|
<span class="val" id="sl-zt-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-line hidden" id="sl-voip">
|
<div class="sidebar-line hidden" id="sl-voip">
|
||||||
<span><span class="lbl-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="13" height="13" fill="currentColor" style="vertical-align:middle;"><path d="M164.9 24.6c-7.7-18.6-28-28.5-47.4-23.2l-88 24C11.7 30.3 0 46.7 0 64C0 311.4 200.6 512 448 512c17.3 0 33.7-11.7 38.6-29.5l24-88c5.3-19.4-4.6-39.7-23.2-47.4l-96-40c-16.3-6.8-35.2-2.1-46.3 11.6L304.7 368C234.3 334.7 177.3 277.7 144 207.3L193.3 167c13.7-11.2 18.4-30 11.6-46.3l-40-96z"/></svg></span> VoIP</span>
|
<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>
|
<span class="val" id="sl-voip-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-line" id="sl-admin">
|
<div class="sidebar-line" id="sl-admin">
|
||||||
<span><span class="lbl-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="12" height="13" fill="currentColor" style="vertical-align:middle;"><path d="M48 0C21.5 0 0 21.5 0 48V464c0 26.5 21.5 48 48 48h96V432c0-26.5 21.5-48 48-48s48 21.5 48 48v80h96c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48H48zM64 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V240zm112-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H176c-8.8 0-16-7.2-16-16V240c0-8.8 7.2-16 16-16zm48-80v32c0 8.8-7.2 16-16 16H176c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16zm-144-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zm144 208h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H272c-8.8 0-16-7.2-16-16V352c0-8.8 7.2-16 16-16zm-144-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V352c0-8.8 7.2-16 16-16z"/></svg></span> Site Admin Fee</span>
|
<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>
|
<span class="val" id="sl-admin-val">$150</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sl-sub hidden" id="sl-admin-sub"></div>
|
<div class="sl-sub hidden" id="sl-admin-sub"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sidebar-line sidebar-line-total" id="sl-monthly-total-row">
|
||||||
<!-- Discount line — hidden when no term discount -->
|
<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">
|
<div class="sidebar-line sidebar-line-discount hidden" id="sl-base-mrr-row">
|
||||||
<span class="sl-muted">Base MRR</span>
|
<span class="sl-muted">Before Term Incentive</span>
|
||||||
<span class="val sl-muted" id="sl-base-mrr-val">—</span>
|
<span class="val sl-muted" id="sl-base-mrr-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-line sidebar-line-discount hidden" id="sl-discount-row">
|
<div class="sidebar-line sidebar-line-discount hidden" id="sl-discount-row">
|
||||||
<span class="sl-muted">Term Discount</span>
|
<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>
|
<span class="val sl-discount-val" id="sl-discount-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="sidebar-mrr-label">Monthly Recurring (MRR)</div>
|
<div class="sidebar-group sidebar-group--tax">
|
||||||
<div class="sidebar-mrr" id="mrrDisplay">$150</div>
|
<label class="sl-hst-toggle qs-toggle-row">
|
||||||
|
|
||||||
<label class="sl-hst-toggle">
|
|
||||||
<input type="checkbox" id="hstToggle" onchange="update()">
|
<input type="checkbox" id="hstToggle" onchange="update()">
|
||||||
<span>Include Ontario HST (13%)</span>
|
<span class="qs-switch"></span>
|
||||||
|
<span class="qs-toggle-label">Include Tax</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- HST line — hidden unless HST toggle enabled -->
|
|
||||||
<div class="sidebar-line sidebar-line-hst hidden" id="sl-hst-row">
|
<div class="sidebar-line sidebar-line-hst hidden" id="sl-hst-row">
|
||||||
<span class="sl-muted">HST (13%)</span>
|
<span class="sl-muted">+ HST 13%</span>
|
||||||
<span class="val sl-hst-val" id="sl-hst-val">—</span>
|
<span class="val sl-hst-val" id="sl-hst-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Total inc. HST — hidden unless HST enabled -->
|
|
||||||
<div class="sidebar-line sidebar-line-total hidden" id="sl-hst-total-row">
|
<div class="sidebar-line sidebar-line-total hidden" id="sl-hst-total-row">
|
||||||
<span>Total (inc. HST)</span>
|
<span>Monthly Total</span>
|
||||||
<span class="val" id="sl-hst-total-val">—</span>
|
<span class="val" id="sl-hst-total-val">—</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Onboarding fee line — hidden unless set -->
|
|
||||||
<div class="sidebar-line hidden" id="sl-otf-row">
|
|
||||||
<span>Onboarding Fee</span>
|
|
||||||
<span class="val" id="sl-otf-val">—</span>
|
|
||||||
</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 & 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">
|
<div class="sidebar-line">
|
||||||
<span>Annual Projection</span>
|
<span>Estimated Annual Managed Spend</span>
|
||||||
<span class="val" id="annualDisplay">$1,800</span>
|
<span class="val" id="annualDisplay">$1,800</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-line hidden" id="perUserRow">
|
<div class="sidebar-line hidden" id="perUserRow">
|
||||||
<span>Avg. Cost Per User<br><small id="perUserBreakdown" class="per-user-cost-sub sidebar-note-mono hidden"></small></span>
|
<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>
|
<span class="val" id="perUserDisplay">—</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ── VS HIRING IN-HOUSE ─────────────────────────────────────
|
<!-- ── VS HIRING IN-HOUSE ─────────────────────────────────────
|
||||||
Hidden (.hidden) until users>0 OR endpoints>0.
|
Hidden (.hidden) until users>0 OR endpoints>0.
|
||||||
@@ -746,25 +889,41 @@
|
|||||||
selector in updateVsComparison().
|
selector in updateVsComparison().
|
||||||
──────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────── -->
|
||||||
<div id="vsComparison" class="hidden vs-comparison-wrap">
|
<div id="vsComparison" class="hidden vs-comparison-wrap">
|
||||||
<div class="vs-label">VS. Hiring In-House</div>
|
<div class="vs-header">
|
||||||
<table class="vs-table">
|
<div class="vs-brand-row">
|
||||||
<tr>
|
<svg width="18" height="24" viewBox="0 0 72 98" class="vs-brand-logo" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||||
<td>
|
|
||||||
<svg width="14" height="14" viewBox="0 0 72 98" class="vs-inline-icon" 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="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"/>
|
<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>
|
</svg>
|
||||||
<span class="vs-svs-label">SVS MSP</span>
|
<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>
|
||||||
<td class="vs-val-accent" id="vs-svs-annual">—</td>
|
<td class="vs-val-accent" id="vs-svs-annual">—</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr><td class="vs-td-muted"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="12" height="13" fill="currentColor" class="vs-td-icon"><path d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z"/></svg> 1 IT person + tools</td><td class="vs-td-muted" id="vs-1man-cost">—</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 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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" width="14" height="13" fill="currentColor" class="vs-td-icon"><path d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192h42.7c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0H21.3C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7h42.7C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3H405.3zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352H378.7C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7H154.7c-14.7 0-26.7-11.9-26.7-26.7z"/></svg> 5-person team</td><td class="vs-td-muted" id="vs-5man-cost">—</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>
|
<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>
|
</table>
|
||||||
<div class="vs-footnote" id="vs-footnote"></div>
|
<div class="vs-footnote" id="vs-footnote"></div>
|
||||||
</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 BUTTONS ───────────────────────────────────────────
|
||||||
Export A: window.print() — triggers browser print/save-as-PDF.
|
Export A: window.print() — triggers browser print/save-as-PDF.
|
||||||
@@ -772,14 +931,8 @@
|
|||||||
exportQuote() — plain-text .txt (original, kept for reference).
|
exportQuote() — plain-text .txt (original, kept for reference).
|
||||||
──────────────────────────────────────────────────────────────── -->
|
──────────────────────────────────────────────────────────────── -->
|
||||||
<div class="export-wrap">
|
<div class="export-wrap">
|
||||||
<button class="btn-export" onclick="printInvoice()">
|
<button type="button" class="btn-reset-quote" onclick="openResetConfirm()">Reset Quote</button>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14" height="14" fill="currentColor" style="margin-right:7px;vertical-align:middle;"><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88a24 24 0 1 1 0 48 24 24 0 1 1 0-48z"/></svg>
|
<button type="button" class="btn-import-quote" id="btnImportQuote" onclick="importQuoteJSON()">Import Quote</button>
|
||||||
Print / Save PDF
|
|
||||||
</button>
|
|
||||||
<button class="btn-export btn-export-secondary" id="btnExportJSON" onclick="exportQuoteJSON()">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="12" height="14" fill="currentColor" style="margin-right:7px;vertical-align:middle;"><path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128h128L256 0zM216 232l-96 96 96 96 22.6-22.6L169.3 328l69.3-69.4L216 232zM168 232l-22.6 22.6 69.3 69.4-69.3 69.4L168 416l96-96-96-96z"/></svg>
|
|
||||||
Export JSON + Copy
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div><!-- END sidebar-body -->
|
</div><!-- END sidebar-body -->
|
||||||
</div><!-- END sidebar -->
|
</div><!-- END sidebar -->
|
||||||
@@ -793,35 +946,35 @@
|
|||||||
<div class="pitch-grid">
|
<div class="pitch-grid">
|
||||||
<div class="pitch-item">
|
<div class="pitch-item">
|
||||||
<div class="pitch-head">
|
<div class="pitch-head">
|
||||||
<div class="pitch-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="18" height="20" fill="var(--accent)" style="vertical-align:middle;"><path d="M256 0c4.6 0 9.2 1 13.4 2.9L457.7 82.8c22 9.3 38.4 31 38.3 57.2c-.5 99.2-41.3 280.7-213.6 363.2c-16.7 8-36.1 8-52.8 0C57.3 420.7 16.5 239.2 16 140c-.1-26.2 16.3-47.9 38.3-57.2L242.7 2.9C246.8 1 251.4 0 256 0zm0 66.8V444.8C394 378 431.1 230.1 432 141.4L256 66.8z"/></svg></div>
|
<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 class="pitch-title">Security-First MSP</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pitch-desc">Every engagement is built on a security baseline — EDR, MFA, patch management, and cyber warranty included.</div>
|
<div class="pitch-desc">Every engagement is built on a security baseline — EDR, MFA, patch management, and cyber warranty included.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pitch-item">
|
<div class="pitch-item">
|
||||||
<div class="pitch-head">
|
<div class="pitch-head">
|
||||||
<div class="pitch-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="14" height="20" fill="var(--accent)" style="vertical-align:middle;"><path d="M215.7 499.2C267 435 384 279.4 384 192C384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2c12.3 15.3 35.1 15.3 47.4 0zM192 128a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/></svg></div>
|
<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 class="pitch-title">Ottawa-Based Team</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pitch-desc">Local presence, Canadian data sovereignty, and an account team that knows your business and your region.</div>
|
<div class="pitch-desc">Local presence, Canadian data sovereignty, and an account team that knows your business and your region.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pitch-item">
|
<div class="pitch-item">
|
||||||
<div class="pitch-head">
|
<div class="pitch-head">
|
||||||
<div class="pitch-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="20" height="20" fill="var(--accent)" style="vertical-align:middle;"><path d="M78.6 5C69.1-2.4 55.6-1.5 47 7L7 47c-8.5 8.5-9.4 22-2.1 31.6l80 104c4.5 5.9 11.6 9.4 19 9.4h54.1l109 109c-14.7 29-10 65.4 14.3 89.6l112 112c12.5 12.5 32.8 12.5 45.3 0l64-64c12.5-12.5 12.5-32.8 0-45.3l-112-112c-24.2-24.2-60.6-29-89.6-14.3l-109-109V104c0-7.5-3.5-14.5-9.4-19L78.6 5zM19.9 396.1C7.2 408.8 0 426.1 0 444.1C0 481.6 30.4 512 67.9 512c18 0 35.3-7.2 48-19.9L233.7 374.3c-7.8-20.9-9-43.6-3.6-65.1l-61.7-61.7L19.9 396.1zM512 144c0-10.5-1.1-20.7-3.2-30.5c-2.4-11.2-16.1-14.1-24.2-6l-63.9 63.9c-3 3-7.1 4.7-11.3 4.7H352c-8.8 0-16-7.2-16-16V102.6c0-4.2 1.7-8.3 4.7-11.3l63.9-63.9c8.1-8.1 5.2-21.8-6-24.2C388.7 1.1 378.5 0 368 0C288.5 0 224 64.5 224 144l0 .8 85.3 85.3c36-9.1 75.8 .5 104 28.7L429 274.5c49-23 83-72.8 83-130.5zM56 432a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"/></svg></div>
|
<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 class="pitch-title">Flat-Rate, No Surprises</div>
|
||||||
</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 class="pitch-desc">Predictable monthly billing with no per-ticket charges — aligned incentives mean we fix things right the first time.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pitch-item">
|
<div class="pitch-item">
|
||||||
<div class="pitch-head">
|
<div class="pitch-head">
|
||||||
<div class="pitch-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="20" height="20" fill="var(--accent)" style="vertical-align:middle;"><path d="M64 64c0-17.7-14.3-32-32-32S0 46.3 0 64V400c0 44.2 35.8 80 80 80H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H80c-8.8 0-16-7.2-16-16V64zm406.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L320 210.7l-57.4-57.4c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L240 221.3l57.4 57.4c12.5 12.5 32.8 12.5 45.3 0l128-128z"/></svg></div>
|
<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 class="pitch-title">Scales With You</div>
|
||||||
</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 class="pitch-desc">Add users, endpoints, servers, ZT networking, or VoIP as you grow — one vendor, one invoice, one relationship.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pitch-footer">
|
<div class="pitch-footer">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="13" height="13" fill="currentColor" style="vertical-align:middle;margin-right:5px;flex-shrink:0;"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg> Canadian company, Canadian support
|
<span class="fa-icon fa-icon-square-check" style="--icon-size:13px;margin-right:5px;"></span> Canadian company, Canadian support
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -840,7 +993,16 @@
|
|||||||
</div>
|
</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="SVS-MSP-Calculator.js"></script>
|
||||||
|
<script src="mobile-sync.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
400
docs/CHECKPOINT.md
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
# SVS MSP CALC — Beta Build Checkpoint
|
||||||
|
|
||||||
|
**Date:** 2026-03-15
|
||||||
|
**Status:** Phases 1–8 + Stage 8 complete. Beta + a11y/perf audit + code quality passes I & II + test expansion + print enhancements done.
|
||||||
|
**Tests:** 254/254 passing
|
||||||
|
**Build Prompt:** .claude/plans/STAGE2-BUILD-PROMPT.md
|
||||||
|
**Previous Stage Prompt:** docs/STAGE3-SESSION-PROMPT.md
|
||||||
|
**Previous Stage Prompt:** docs/STAGE5-SESSION-PROMPT.md
|
||||||
|
**Previous Stage Prompt:** docs/STAGE6-SESSION-PROMPT.md
|
||||||
|
**Previous Stage Prompt:** docs/STAGE7-SESSION-PROMPT.md
|
||||||
|
**Previous Stage Prompt:** docs/STAGE8-SESSION-PROMPT.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Completed
|
||||||
|
|
||||||
|
### Phase 1: Bug Fixes (6/6)
|
||||||
|
|
||||||
|
| # | Issue | File | Change |
|
||||||
|
|---|-------|------|--------|
|
||||||
|
| 1.1 | ADDON_INKY default $5 → $8 | quote-pricing.js:12 | `ADDON_INKY: 5` → `8` |
|
||||||
|
| 1.2 | Onboarding fee loses manual override on term switch | SVS-MSP-Calculator.js:41-70 | Store manual value in `data-manual-value` before 24mo clears it; restore on switch back to m2m/12mo |
|
||||||
|
| 1.3 | VoIP fax CSV comment misleading | package-prices.csv:18 | "Flat/mo" → "Per seat/mo" |
|
||||||
|
| 1.4 | Print forces HST on regardless of user toggle | quote-export.js:12 | Removed `state.hstEnabled = true;` — print now respects user's HST toggle |
|
||||||
|
| 1.5 | JSON export missing schema version | quote-export.js:229 | Added `version: '1.0'` as first field in payload |
|
||||||
|
| 1.6 | ZT admin supplement triggers with no warning | quote-render.js:494-499 | New amber nudge when `ztActive` warns about $250 admin supplement |
|
||||||
|
|
||||||
|
Test expectations updated in test-quote-engine.js for INKY $8 (4 values changed).
|
||||||
|
|
||||||
|
### Phase 2: Visual Polish (Sections I–III)
|
||||||
|
|
||||||
|
| # | Issue | File | Change |
|
||||||
|
|---|-------|------|--------|
|
||||||
|
| 2.1a | Hardcoded `#e06070` danger icon | components.css:41 | → `var(--text-danger)` — adapts per theme |
|
||||||
|
| 2.1b | Hardcoded `#86efac` pill-savings on checked state | components.css:442 | → `var(--text-pill-savings-active)` — new token |
|
||||||
|
| — | Token added to all 4 themes | tokens.css, light.css, glass.css, 70retro.css | Dark: `#86efac`, Light: `#d4f5e0`, Glass: `#a8f0c8`, Retro: `#e0f0d0` |
|
||||||
|
| 2.1c | QUICK-REF.md outdated | docs/QUICK-REF.md | Updated INKY $8, export desc, theme list, test count |
|
||||||
|
|
||||||
|
**Audit findings (no action needed):**
|
||||||
|
- All 4 themes fully token-covered for Sections I–III
|
||||||
|
- Glass theme uses `!important` selector overrides (valid for glassmorphism effects)
|
||||||
|
- Sidebar focus-toggle white rgba values sit on colored header — correct everywhere
|
||||||
|
- No remaining hardcoded colors in Sections I–III component CSS
|
||||||
|
- Sidebar renders correctly across all 4 themes at all breakpoints
|
||||||
|
|
||||||
|
### Phase 3: UX Hardening (Sections I–III)
|
||||||
|
|
||||||
|
#### 3.1 Interaction Refinements
|
||||||
|
|
||||||
|
| # | Change | Files | Details |
|
||||||
|
|---|--------|-------|---------|
|
||||||
|
| 3.1a | Smooth theme-switch transition | tokens.css, theme-manager.js | `body.theme-transitioning` class enables 0.25s color/bg/border fade; applied for 300ms during `toggleTheme()` |
|
||||||
|
| 3.1b | Nudge crossfade on rotation/nav | components.css, quote-render.js | `.nudge-fading` class fades opacity to 0; `cycleNudge()` does fade-out → swap → fade-in (180ms); auto-rotation now uses `cycleNudge(1)` for consistency |
|
||||||
|
| 3.1c | Summary badge fade-in on collapse | components.css | `@keyframes badgeFadeIn` — 0.25s opacity + translateY animation on `.sec-summary-badge` |
|
||||||
|
| 3.1d | Addon toggle micro-feedback | components.css | `@keyframes addonPulse` — 0.2s scale(1.015) pulse on `.addon-row.selected` |
|
||||||
|
|
||||||
|
#### 3.2 Responsive Edge Cases
|
||||||
|
|
||||||
|
| # | Change | Files | Details |
|
||||||
|
|---|--------|-------|---------|
|
||||||
|
| 3.2a | Touch targets ≥44px on mobile | responsive.css | `.mobile-panel-close-btn` 36→44px, `.nudge-nav-btn` 34→44px at ≤1100px; `.collapsible-header` and `.section-toggle` min-height 44px at ≤600px |
|
||||||
|
| 3.2b | Container query fallback verified | — | `@container (max-width: 760px)` for addon rows has adequate fallback via ≤600px media query; no change needed |
|
||||||
|
|
||||||
|
#### 3.3 Mobile Experience Completeness
|
||||||
|
|
||||||
|
| # | Change | Files | Details |
|
||||||
|
|---|--------|-------|---------|
|
||||||
|
| 3.3a | Focus trap in mobile panel | mobile-sync.js | `trapFocus()` function keeps Tab cycling within open panel; focus moves to close button on open, returns to pill on close |
|
||||||
|
| 3.3b | Safe-area insets for notch phones | responsive.css | `padding-bottom: env(safe-area-inset-bottom)` on `.mobile-panel-sheet`; `right: max(14px, env(safe-area-inset-right))` on `.mobile-quote-pill` |
|
||||||
|
|
||||||
|
**GATE: 88/88 tests pass. All JS syntax-checked. CSS brace balance verified.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 4: Documentation & QA
|
||||||
|
|
||||||
|
| # | Task | Status |
|
||||||
|
|---|------|--------|
|
||||||
|
| 4.1 | Update all docs (README, code-verification, quote-rules, phase-roadmap, QUICK-REF, MASTER-SESSION-PROMPT, ai-session-brief) | COMPLETE |
|
||||||
|
| 4.2 | Full regression checklist walkthrough | COMPLETE — 88/88 automated, 15/15 manual items verified in code |
|
||||||
|
| 4.3 | Beta definition of done verification | COMPLETE — all 13 criteria pass |
|
||||||
|
|
||||||
|
**Docs updated:**
|
||||||
|
- README.md — phase status, 88 tests, 4 themes, export description, file map (70retro.css added)
|
||||||
|
- code-verification.md — date, test count, all Phase 1-3 changes as known-good baseline
|
||||||
|
- quote-rules.md — onboarding manual override persistence, HST print behavior, JSON export rules, admin nudge
|
||||||
|
- phase-roadmap.md — Phases 1-4 status, 88 tests
|
||||||
|
- QUICK-REF.md — test count in "Remind User", 70retro.css in CSS file map
|
||||||
|
- MASTER-SESSION-PROMPT.md — 88 tests (3 occurrences), 4 themes (6 occurrences), 70retro.css in tree, 4 theme override layers
|
||||||
|
- ai-session-brief.md — test count updated
|
||||||
|
|
||||||
|
**Regression checklist results:**
|
||||||
|
- Automated: 88/88 pass
|
||||||
|
- Manual: All 15 items verified via source code review (admin waive displays, term/onboarding logic, manual override persistence, sidebar sync, mobile panel sync, persistence round-trip, reset behavior, print HST, JSON export, section headers, theme transitions, nudge crossfade, focus trap, safe-area insets, touch targets)
|
||||||
|
|
||||||
|
**Beta Definition of Done: ALL 13 CRITERIA PASS**
|
||||||
|
|
||||||
|
**GATE: PASSED — Beta build for Sections I–III is complete.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 5: Performance & Accessibility Audit
|
||||||
|
|
||||||
|
| # | Fix | File(s) | Details |
|
||||||
|
|---|-----|---------|---------|
|
||||||
|
| A2 | `aria-expanded` on section & collapsible toggles | HTML, SVS-MSP-Calculator.js | Added `aria-expanded="false"` to 12 toggle elements; JS updates dynamically on toggle |
|
||||||
|
| A3 | Focus trap on reset confirm modal | quote-persistence.js | `trapFocusInModal()` — Tab cycles within modal when open |
|
||||||
|
| A4 | `aria-label` on stepper buttons | HTML | All 12 step-btn elements have descriptive labels (e.g. "Decrease users") |
|
||||||
|
| P1 | Glass theme scroll jank on mobile | glass.css | `background-attachment: scroll` at ≤1100px — avoids fixed-bg repaint on iOS |
|
||||||
|
| P2 | Skip mobile sync on desktop | mobile-sync.js | Guard skips 35+ element sync when panel closed on desktop; forces full sync on `openMobilePanel()` |
|
||||||
|
| M1 | `sidebarFocusClientName` not in sync map | mobile-sync.js | Added to html sync list — client name now updates in mobile panel |
|
||||||
|
| M2 | `sl-discount-detail` + `sl-value-onboarding-label` not in sync map | mobile-sync.js | Added to html sync list — contract term label and onboarding label now sync |
|
||||||
|
|
||||||
|
**Not flagged (clean):** Token coverage, `:focus-visible`, mobile focus trap, escape handling, touch targets, `will-change` usage, print CSS isolation, no unused JS.
|
||||||
|
|
||||||
|
**GATE: 88/88 tests pass. All fixes verified.**
|
||||||
|
|
||||||
|
### Font Awesome Icon Fix
|
||||||
|
|
||||||
|
| # | Fix | File | Details |
|
||||||
|
|---|-----|------|---------|
|
||||||
|
| FA1 | Icons invisible on `file://` protocol | components.css:44-79 | All 36 FA Sharp Solid SVG file references converted to inline `data:image/svg+xml` URIs — eliminates CORS/`file://` restriction on `mask-image: url()` |
|
||||||
|
|
||||||
|
**Root cause:** CSS `mask-image: url("fontawesomekit/svgs/...")` is blocked by browser security on the `file://` protocol. Inline data URIs bypass this completely.
|
||||||
|
|
||||||
|
**GATE: 88/88 tests pass. Icons render on local file open.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 6: Code Quality Pass (Stage 3)
|
||||||
|
|
||||||
|
| # | Fix | File(s) | Details |
|
||||||
|
|---|-----|---------|---------|
|
||||||
|
| CQ1 | New `--sky` color token | tokens.css, light.css, glass.css, 70retro.css | Per-theme sky/info accent: Dark `#38bdf8`, Light `#0e7490`, Glass `#7dd3fc`, Retro `#a34a14` |
|
||||||
|
| CQ2 | New `--transition-fast` token | tokens.css | `0.15s` — replaces hardcoded timing in layout.css button transitions |
|
||||||
|
| CQ3 | Consolidated duplicate button CSS | layout.css:25-47 | `.btn-reset-quote` and `.btn-import-quote` shared 10 identical properties → merged into grouped selector |
|
||||||
|
| CQ4 | Hardcoded amber hover → token-derived | layout.css:43-46 | `rgba(232,146,15,…)` → `color-mix(in srgb, var(--amber) …%, transparent)` |
|
||||||
|
| CQ5 | Hardcoded sky blue hover → token-derived | layout.css:47-50, light.css, glass.css, 70retro.css | All `rgba(56,189,248,…)` / `#38bdf8` / `#7dd3fc` / `#a34a14` → `var(--sky)` + `color-mix()` |
|
||||||
|
| CQ6 | Dead null-check removed | quote-render.js:533 | `nudgeIndex == null ||` removed — `nudgeIndex` is always initialized to `0` |
|
||||||
|
|
||||||
|
**Audit findings (no action taken — documented for future):**
|
||||||
|
- `fmt()` duplicated in quote-render.js and quote-export.js (both inside IIFEs — intentional isolation, one-liner)
|
||||||
|
- Spacing magic numbers (14px/16px/20px) used 95+ times — too many touchpoints for surgical migration
|
||||||
|
- `console.warn()` statements in pricing/persistence/import are intentional error reporting
|
||||||
|
- No dead functions, no unreachable code, no unused exports across all 8 JS modules
|
||||||
|
|
||||||
|
**GATE: 88/88 tests pass. All 4 themes verified tokenized.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 7: Test Coverage Expansion (Stage 4)
|
||||||
|
|
||||||
|
| # | Test Group | Count | Details |
|
||||||
|
|---|-----------|-------|---------|
|
||||||
|
| T1 | Pricing DEFAULTS integrity | 34 | All required keys exist, types correct, values match spec, frozen, ordering invariants |
|
||||||
|
| T2 | Engine edge cases & boundaries | 55 | Admin fee thresholds, large counts (100u/100ep), string coercion, invalid inputs (NaN/null/empty), servers-only, VoIP-only, VoIP edge cases, ZT without user addon, admin waived, all addons combined, BYOL term independence, discount rounding |
|
||||||
|
| T3 | Export JSON schema validation | 18 | Payload structure, field types, version field, contract term labels, licensing labels, pricing sub-object, voip tier null handling |
|
||||||
|
| T4 | Persistence state shape | 6 | JSON round-trip for strings/numbers/booleans, engine compatibility, zero-state |
|
||||||
|
| T5 | Import payload mapping | 12 | Contract term reverse-map, full export→import→engine round-trip (MRR, effectiveMrr, mrrWithHst, userTotal, endpointTotal, voipTotal, adminFeeNet, effectiveAnnual) |
|
||||||
|
| T6 | Quote output invariants | 24 | 6 configs × 4 invariants (effectiveMrr, effectiveAnnual, mrrWithHst, non-negative values) |
|
||||||
|
|
||||||
|
**Total: 88 → 250 tests (162 new). All passing.**
|
||||||
|
|
||||||
|
**GATE: 250/250 tests pass.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 8: Enhanced Print/PDF (Stage 4)
|
||||||
|
|
||||||
|
| # | Enhancement | File(s) | Details |
|
||||||
|
|---|------------|---------|---------|
|
||||||
|
| P1 | Quote notes field | HTML:920, components.css, quote-persistence.js, quote-export.js, quote-import.js | `<textarea id="quoteNotes">` in sidebar, persisted in localStorage, included in JSON export/import, rendered on print invoice |
|
||||||
|
| P2 | Explicit validity date | quote-export.js | Computes 30-day expiry: "Valid until [date]" in print footer instead of generic "30 days" |
|
||||||
|
| P3 | Page break control | quote-export.js (inline CSS) | `page-break-inside:avoid` on table rows + `.tots-wrap`; `break-inside:avoid` on notes section |
|
||||||
|
| P4 | Rep name field | HTML:100, layout.css, quote-persistence.js, quote-export.js, quote-import.js | `<input id="repName">` below client name, persisted, in JSON export/import, shown in print header + footer |
|
||||||
|
| P5 | CYA "Not Included" section | quote-export.js | Print splits config into "Your Service Configuration" (active) + "Services Not Included in This Quote" (excluded, muted, smaller) |
|
||||||
|
|
||||||
|
**Additional changes:**
|
||||||
|
- JSON export schema version bumped to `1.1` (new `repName`, `quoteNotes` fields)
|
||||||
|
- JSON import handles new fields gracefully (backward-compatible with `1.0` exports)
|
||||||
|
- Print CSS hides notes + rep inputs on `@media print` (main page path)
|
||||||
|
- 4 new tests added (repName/quoteNotes in export schema + persistence)
|
||||||
|
|
||||||
|
**GATE: 254/254 tests pass.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Files to Read on Resume
|
||||||
|
|
||||||
|
1. `docs/MASTER-SESSION-PROMPT.md` — full architecture and constraints
|
||||||
|
2. `docs/QUICK-REF.md` — compact file map, IDs, pricing
|
||||||
|
3. `docs/regression-checklist.md` — test procedures
|
||||||
|
4. `.claude/plans/STAGE2-BUILD-PROMPT.md` — the build prompt driving this work
|
||||||
|
5. This file — checkpoint status
|
||||||
|
|
||||||
|
### Stage 5 / Phase 9: Visual QA + Retro Theme Overhaul
|
||||||
|
|
||||||
|
**Visual QA:** 3 breakpoints (mobile ~375px, desktop ~1100-1400px, wide ~1800px+) × 4 themes.
|
||||||
|
|
||||||
|
| Theme | Mobile | Desktop | Wide | Result |
|
||||||
|
|-------|--------|---------|------|--------|
|
||||||
|
| Dark | Clean | Clean | Clean | PASS |
|
||||||
|
| Light | Clean | Clean | Clean | PASS |
|
||||||
|
| Glass | Clean | Clean | Clean | PASS |
|
||||||
|
| Retro | Overhauled | — | — | REWORKED |
|
||||||
|
|
||||||
|
**Retro theme overhaul:**
|
||||||
|
- **Problem:** Original 70s wood-panel brown palette had low contrast, muddy colors, invisible logo (black SVG on brown header)
|
||||||
|
- **Solution:** Warm paper base + neon-warm cyberpunk accents
|
||||||
|
- Accent: hot rose `#e11d48` (warm neon, harmonizes with cream)
|
||||||
|
- Green/Sky: warm teal `#0d9488`
|
||||||
|
- Header: warm charcoal `#1c1317` with rose neon border
|
||||||
|
- Logo: `.top-bar-logo path { fill: #f0e4d0 }` — overrides hardcoded `#0c0c0c` SVG fills
|
||||||
|
- Progress bar: rose → teal gradient
|
||||||
|
- Paper texture: warm brown scanlines (unchanged from original)
|
||||||
|
- **Status:** Functional, user notes full design pass deferred to later
|
||||||
|
|
||||||
|
**Remaining QA not yet done:** Retro theme at all viewport widths, landscape orientation.
|
||||||
|
|
||||||
|
**GATE: 254/254 tests pass. No visual bugs found on Dark/Light/Glass.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Stage 6 / Phase 10: Elastic Responsive Foundation
|
||||||
|
|
||||||
|
**Problem:** 5 fixed breakpoints (1350, 1100, 900, 600, 780px landscape) with hardcoded px overrides at each step. Max width capped at 1800px — wasted space on 1440p+ monitors.
|
||||||
|
|
||||||
|
**Solution:** Fluid `clamp()` tokens replace discrete breakpoint steps. Only structural breakpoints remain.
|
||||||
|
|
||||||
|
| # | Change | File(s) | Details |
|
||||||
|
|---|--------|---------|---------|
|
||||||
|
| E1 | Fluid layout tokens | tokens.css | `--page-max-width: clamp(1200px, 92vw, 2400px)`, `--page-gutter-x: clamp(16px, 3vw, 80px)`, `--layout-column-gap: clamp(24px, 3vw, 56px)`, sidebar min 400→360px |
|
||||||
|
| E2 | Fluid section tokens | tokens.css | `--section-offset: clamp(52px, 7vw, 104px)`, `--section-num-width/size` fluid, `--section-padding-*` fluid |
|
||||||
|
| E3 | Eliminated 1350px breakpoint | responsive.css | Removed — fluid tokens handle narrow desktop scaling |
|
||||||
|
| E4 | Eliminated 900px breakpoint | responsive.css | Removed — fluid tokens handle tablet spacing/numerals |
|
||||||
|
| E5 | Fluid logo margin | base.css | `margin-left: clamp(26px, 5.2vw, 78px)` replaces hardcoded 78px + breakpoint overrides |
|
||||||
|
| E6 | Fluid main-col gap | layout.css | `gap: clamp(16px, 1.5vw, 24px)` replaces hardcoded 24px + breakpoint override |
|
||||||
|
| E7 | Fluid client-bar padding | layout.css | `clamp()` on vertical padding, `var(--section-offset)` for left |
|
||||||
|
|
||||||
|
**Breakpoint reduction:** 5 → 3 (1100px structural, 600px phone layout, 780px landscape orientation)
|
||||||
|
|
||||||
|
**Width scaling:**
|
||||||
|
- 1080p (1920px): content fills ~1766px (92vw)
|
||||||
|
- 1440p (2560px): content fills ~2355px (92vw)
|
||||||
|
- 4K (3840px): content caps at 2400px max
|
||||||
|
|
||||||
|
**GATE: 254/254 tests pass.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Stage 7 / Phase 11: Feature Work (Option A)
|
||||||
|
|
||||||
|
#### 11.1 Keyboard Shortcuts
|
||||||
|
|
||||||
|
| Shortcut | Action | File | Details |
|
||||||
|
|----------|--------|------|---------|
|
||||||
|
| Ctrl+P | Print invoice | SVS-MSP-Calculator.js | `preventDefault()` blocks browser print dialog; calls `printInvoice()` |
|
||||||
|
| Ctrl+E | Export JSON | SVS-MSP-Calculator.js | Calls `exportQuoteJSON()` |
|
||||||
|
| Ctrl+R | Reset quote | SVS-MSP-Calculator.js | Opens confirm modal via `openResetConfirm()` — not a hard reset |
|
||||||
|
| Escape | Close overlays | mobile-sync.js (existing) | Already handled — closes sidebar focus + mobile panel |
|
||||||
|
|
||||||
|
All shortcuts are suppressed when focus is in an `<input>`, `<textarea>`, or `<select>` to avoid hijacking normal typing.
|
||||||
|
|
||||||
|
#### 11.2 New Contextual Nudges
|
||||||
|
|
||||||
|
| # | Nudge | Color | Trigger |
|
||||||
|
|---|-------|-------|---------|
|
||||||
|
| N1 | Users set but no endpoints | amber | `users > 0 && endpoints === 0` |
|
||||||
|
| N2 | VoIP seats ≠ user count | amber | `voipSeats > 0 && users > 0 && voipSeats !== users` |
|
||||||
|
| N3 | High admin-to-MRR ratio | amber | `adminFeeNet > MRR * 0.25` (and not waived) |
|
||||||
|
| N4 | Extended Hours upsell | green | `!addExtHours && users > 0` |
|
||||||
|
|
||||||
|
Added after existing nudges in `buildNudges()` in quote-render.js (lines 524–551).
|
||||||
|
|
||||||
|
**GATE: 254/254 tests pass.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Stage 8 / Phase 12: Code Quality Pass II
|
||||||
|
|
||||||
|
#### 8.1 `--transition-fast` / `--transition-medium` Token Adoption
|
||||||
|
|
||||||
|
| # | Change | File(s) | Details |
|
||||||
|
|---|--------|---------|---------|
|
||||||
|
| T1 | New `--transition-medium` token | tokens.css | `0.25s` — for chevron/collapsible/nudge transforms |
|
||||||
|
| T2 | 10× `0.15s` → `var(--transition-fast)` | components.css | pill-toggle, tier-seg, addon-preview-pill, addon checkbox, sidebar-focus-toggle, nudge-nav-btn, btn-toggle-all, quote-notes-input, btn-export |
|
||||||
|
| T3 | 3× `0.25s` → `var(--transition-medium)` | components.css | sec-chevron transform, collapsible-toggle transform, nudge-banner bg/border |
|
||||||
|
| T4 | 1× `0.15s` → `var(--transition-fast)` | base.css | theme-toggle-btn |
|
||||||
|
| T5 | 2× `0.15s` → `var(--transition-fast)` | responsive.css | mobile-quote-pill, mobile-panel-close-btn |
|
||||||
|
|
||||||
|
**Left as-is:** 0.12s (stepper/addon micro-interactions), 0.18s (term tile tuned), 0.2s (switch/section/overlay), 0.3s (progress bar/accordion), 0.34s (section-body tuned bezier). No `0.15s` hardcodes remain outside the token definition.
|
||||||
|
|
||||||
|
#### 8.2 CSS Selector Specificity Audit
|
||||||
|
|
||||||
|
| # | Change | File(s) | Details |
|
||||||
|
|---|--------|---------|---------|
|
||||||
|
| S1 | `.sec-open` → `.section.sec-open` | components.css | Removed 2× `!important` — specificity now beats `.section:hover` via class count |
|
||||||
|
| S2 | Documented intentional `!important` | components.css | Added comments to `.qs-discount-sub`, sidebar utility classes (`.sl-muted`, `.sl-discount-val`, `.sl-hst-val`), and VS value classes |
|
||||||
|
|
||||||
|
**Audit findings (no action — all legitimate):**
|
||||||
|
- components.css: 13 remaining `!important` — all utility `display: none` or color overrides that must beat compound parent selectors
|
||||||
|
- 70retro.css: 37 `!important` — theme override pattern (same as glass.css with 97)
|
||||||
|
- responsive.css: 8 `!important` — mobile sidebar embedding
|
||||||
|
- tokens.css: 1 `!important` — `body.theme-transitioning` (intentional, per spec)
|
||||||
|
- print.css: All `!important` — standard `@media print` override pattern
|
||||||
|
- No overly-qualified selectors found (element-qualified patterns are all necessary)
|
||||||
|
|
||||||
|
#### 8.3 Print CSS Hardening
|
||||||
|
|
||||||
|
| # | Change | File(s) | Details |
|
||||||
|
|---|--------|---------|---------|
|
||||||
|
| P1 | Hide 4 missing interactive elements | print.css | Added `display: none !important` for `.sidebar-focus-toggle`, `.sidebar-utility`, `.qs-switch`, `.confirm-modal` |
|
||||||
|
| P2 | Theme-independent callout borders | tokens.css, print.css | New `--print-callout-green-border` and `--print-callout-red-border` tokens replace theme-variable `var(--green)` and `var(--surface-danger-border)` in print context |
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
- All `--print-*` tokens defined only in `:root` (tokens.css) — no theme overrides
|
||||||
|
- Page-break rules unaffected by fluid layout tokens (`.outer` forced to `display: block; max-width: 100%` in print)
|
||||||
|
- Print invoice (separate window) uses inline CSS — not affected by main page changes
|
||||||
|
|
||||||
|
#### 8.4 (Stretch) Spacing Token Consolidation — Deferred
|
||||||
|
|
||||||
|
**Assessment:** 150+ magic-number spacing values across components.css (10px: 36, 12px: 35, 14px: 36, 16px: 24, 20px: 19). Existing `--space-stack-*` tokens used only 4× out of 150+. Migration scope too broad for surgical approach. Deferred to a dedicated spacing-focused stage.
|
||||||
|
|
||||||
|
**GATE: 254/254 tests pass.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Stage 8 Feature Fixes
|
||||||
|
|
||||||
|
#### F1: Fullscreen Live Quote View — Print Only
|
||||||
|
|
||||||
|
| # | Change | File(s) | Details |
|
||||||
|
|---|--------|---------|---------|
|
||||||
|
| F1a | Hide Reset + Import in focus mode | components.css | `.export-wrap` and `.sidebar-utility` now `display: none` in sidebar-focus-open |
|
||||||
|
| F1b | Print button inside sidebar header | HTML, components.css | New `.sidebar-focus-print-btn` in `.sidebar-header-row` — hidden by default, `display: inline-flex` in focus mode |
|
||||||
|
| F1c | Print button hidden in print/mobile | print.css, components.css | `display: none !important` in `@media print` and `.mobile-panel-sheet` |
|
||||||
|
|
||||||
|
**Before:** Focus mode hid Print/Export JSON, showed Reset/Import
|
||||||
|
**After:** Focus mode shows a Print button in the header bar (next to collapse icon), hides all other action buttons
|
||||||
|
|
||||||
|
#### F2: Toggle Switch 2-State Theme Colors
|
||||||
|
|
||||||
|
| # | Change | File(s) | Details |
|
||||||
|
|---|--------|---------|---------|
|
||||||
|
| F2a | New `--surface-switch-off` / `--surface-switch-on` tokens | tokens.css | Dark: off `#4a4540`, on `var(--green)` |
|
||||||
|
| F2b | Light theme switch tokens | light.css | Off `#b5ad9f`, on `var(--green)` |
|
||||||
|
| F2c | Glass theme switch tokens | glass.css | Off `rgba(255,255,255,0.15)`, on `var(--green)` |
|
||||||
|
| F2d | Retro theme switch tokens | 70retro.css | Off `#c0b4a0`, on `var(--green)` |
|
||||||
|
| F2e | Component CSS uses tokens | components.css | `.qs-switch` bg → `var(--surface-switch-off)`, checked → `var(--surface-switch-on)` |
|
||||||
|
| F2f | Glass checked override | glass.css | Added `.qs-toggle-row input:checked ~ .qs-switch { background: var(--surface-switch-on) }` |
|
||||||
|
|
||||||
|
**Before:** Off = `--border` (barely visible), On = `--accent` (theme accent)
|
||||||
|
**After:** Off = distinct muted track per theme, On = `--green` (universally "enabled")
|
||||||
|
|
||||||
|
**GATE: 254/254 tests pass.**
|
||||||
|
|
||||||
|
#### F1 Fix: Print Button Visibility in Focus Mode
|
||||||
|
|
||||||
|
| # | Change | File(s) | Details |
|
||||||
|
|---|--------|---------|---------|
|
||||||
|
| F1d | Print button inside sidebar header | HTML:697 | New `.sidebar-focus-print-btn` button in `.sidebar-header-row`, between title and collapse icon |
|
||||||
|
| F1e | Focus-only visibility | components.css | `display: none` by default; `display: inline-flex` when `body.sidebar-focus-open` |
|
||||||
|
| F1f | Hidden in print + mobile | print.css, components.css | `display: none !important` in `@media print` and `.mobile-panel-sheet` |
|
||||||
|
|
||||||
|
**Root cause:** `.sidebar-utility` is a sibling of `.sidebar`, not inside it. When `.sidebar` becomes `position: fixed`, the utility div is left behind the backdrop.
|
||||||
|
|
||||||
|
#### F3: Pricing CSV → JSON Migration
|
||||||
|
|
||||||
|
| # | Change | File(s) | Details |
|
||||||
|
|---|--------|---------|---------|
|
||||||
|
| F3a | New JSON pricing file | package-prices.json | Structured by category with `{ key: { value, description } }` format — human-readable + machine-parseable |
|
||||||
|
| F3b | Script-loaded pricing | package-prices-data.js, HTML | `window.SVS_PRICING_DATA` set via `<script>` tag — works on `file://` protocol, no web server needed |
|
||||||
|
| F3c | Loader updated | quote-pricing.js | `loadPricing()` checks `SVS_PRICING_DATA` global first (script path), then `fetch()` fallback (web server), then built-in defaults |
|
||||||
|
| F3d | CSV retained | package-prices.csv | Original CSV kept for reference; no longer loaded at runtime |
|
||||||
|
|
||||||
|
**How to update pricing:** Edit `package-prices-data.js` — change the `value` field for any key. No web server needed. The file is loaded via `<script>` tag before the pricing engine initializes.
|
||||||
|
|
||||||
|
**JSON format example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_packages": {
|
||||||
|
"RATE_M365": { "value": 130, "description": "Per-user/mo rate — M365 included" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**GATE: 254/254 tests pass.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hard Constraints (reminder)
|
||||||
|
|
||||||
|
1. DOM IDs are a contract — no renaming
|
||||||
|
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
3. localStorage keys unchanged
|
||||||
|
4. All 4 themes must work after every change
|
||||||
|
5. Mobile parity maintained
|
||||||
|
6. No frameworks, no npm — vanilla only
|
||||||
|
7. Surgical changes only
|
||||||
|
8. Sections IV–VI unchanged (deferred)
|
||||||
416
docs/MASTER-SESSION-PROMPT.md
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
# SVS MSP CALC — Master Session Prompt
|
||||||
|
### Pre-Alpha → Beta Optimization Brief
|
||||||
|
**Version:** 1.0 | **Date:** 2026-03-14 | **Audience:** Senior Engineers + UI/UX Leads
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHO YOU ARE (Team Persona)
|
||||||
|
|
||||||
|
You are a cross-functional senior engineering and design team operating with full production standards:
|
||||||
|
|
||||||
|
- **Senior Frontend Engineer** — Deep HTML/CSS/JS expertise. Minimal safe changes. Surgical precision. Strong regression awareness. No casual hacks, no premature abstractions, no over-engineering.
|
||||||
|
- **UI/UX Architect** — Masters-level understanding of design systems, visual hierarchy, information architecture, interaction design, and accessibility. Fluent in tokenized CSS design systems. Makes layouts feel inevitable, not assembled.
|
||||||
|
- **Sales Enablement Lead** — Understands this tool is used live on sales calls with prospects. Every UX decision must serve the sales conversation. Copy must be confident, concise, and client-facing.
|
||||||
|
- **QA Engineer** — Regression-aware. Knows where the landmines are (mobile sync, quote math, persistence, print/PDF). Validates behavior before marking work done.
|
||||||
|
|
||||||
|
You do not introduce change for its own sake. You improve with purpose, validate your work, and leave the codebase healthier than you found it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROJECT OVERVIEW
|
||||||
|
|
||||||
|
**App:** SVS MSP CALC — A live quote/pricing calculator for SVS Managed Services.
|
||||||
|
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
|
||||||
|
**Used by:** SVS sales team, live on screen with prospects during discovery calls
|
||||||
|
**Goal of this session:** Advance from pre-alpha to a solid, ship-ready **beta build**
|
||||||
|
|
||||||
|
### What "Beta" Means Here
|
||||||
|
- All active sections (I–III) are visually polished and production-quality
|
||||||
|
- All four themes (Dark, Light, Glass, 70s Retro) are consistent, tested, and professional
|
||||||
|
- Mobile experience is smooth, reliable, and parity-complete with desktop
|
||||||
|
- Print/PDF invoice is clean, branded, and pixel-accurate
|
||||||
|
- Quote math, persistence, and export are verified and regression-safe
|
||||||
|
- UX hierarchy is clear: users know exactly what to do without instructions
|
||||||
|
- No known bugs in active scope
|
||||||
|
- Sections IV–VI (Zero Trust Networking, VoIP) are ready to activate — code is solid and UX frameworks are in place, even if content is still gated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ARCHITECTURE SNAPSHOT
|
||||||
|
|
||||||
|
```
|
||||||
|
svsmspcalc/
|
||||||
|
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB, inline handlers)
|
||||||
|
├── SVS-MSP-Calculator.js # Master orchestration (310 lines)
|
||||||
|
├── quote-engine.js # Pure quote math (196 lines)
|
||||||
|
├── quote-pricing.js # Pricing defaults + JSON override (134 lines)
|
||||||
|
├── quote-render.js # DOM rendering + nudge engine (662 lines)
|
||||||
|
├── quote-persistence.js # localStorage save/restore (212 lines)
|
||||||
|
├── quote-export.js # Print/PDF + JSON export (295 lines)
|
||||||
|
├── theme-manager.js # Dark/Light/Glass/70s Retro switching (110 lines)
|
||||||
|
├── mobile-sync.js # Mobile panel + 100+ element sync (236 lines)
|
||||||
|
├── SVS-MSP-Calculator.css # Manifest: @imports all CSS files
|
||||||
|
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
|
||||||
|
├── SVS-MSP-Calculator-base.css # Global chrome
|
||||||
|
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar split
|
||||||
|
├── SVS-MSP-Calculator-components.css # All section cards, controls, sidebar (66KB)
|
||||||
|
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
|
||||||
|
├── SVS-MSP-Calculator-print.css # Print-specific rules
|
||||||
|
├── SVS-MSP-Calculator-light.css # Light theme overrides
|
||||||
|
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
|
||||||
|
├── SVS-MSP-Calculator-70retro.css # 70s Retro theme overrides
|
||||||
|
├── package-prices.json # Overrideable pricing (JSON, categorized with descriptions)
|
||||||
|
├── package-prices.csv # Legacy pricing reference (no longer loaded at runtime)
|
||||||
|
├── tests/
|
||||||
|
│ └── test-quote-engine.js # Automated quote engine tests (88 tests, Node.js)
|
||||||
|
└── docs/
|
||||||
|
├── README.md
|
||||||
|
├── ai-session-brief.md
|
||||||
|
├── phase-roadmap.md
|
||||||
|
├── code-verification.md
|
||||||
|
├── quote-rules.md
|
||||||
|
├── regression-checklist.md
|
||||||
|
└── MASTER-SESSION-PROMPT.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tech Stack Facts
|
||||||
|
- **No frameworks.** Vanilla JS (ES5-compatible), HTML5, CSS3.
|
||||||
|
- **No build tools.** Open the HTML in a browser — it runs.
|
||||||
|
- **No npm.** No webpack, Vite, Rollup, Parcel, or transpilation.
|
||||||
|
- **No TypeScript.** Plain `.js` files.
|
||||||
|
- **CSS architecture:** Tokenized custom properties. Modular files. Four theme override layers.
|
||||||
|
- **State:** localStorage only. Key: `svs-msp-quote-v1`.
|
||||||
|
- **Fonts:** Google Fonts (Cinzel, Poppins, Lato, DM Mono).
|
||||||
|
- **Icons:** Font Awesome 7 Sharp (local SVGs), M365 icon set (local).
|
||||||
|
|
||||||
|
### Initialization Flow
|
||||||
|
```
|
||||||
|
initTheme() → restore saved theme (dark/light/glass)
|
||||||
|
initQuote() → load JSON pricing, set quote ref, restore localStorage, call update()
|
||||||
|
update() → calcQuote() → renderQuoteUi() → renderSidebar() → nudges → savings → summaries
|
||||||
|
debouncedSave() → auto-save to localStorage (debounced 400ms)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automated Testing
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
88 tests, zero dependencies. Tests the pure `calculateQuote(state, pricing)` function against known-good expected values using default pricing. Covers: rates, add-ons, admin fee logic, discounts, HST, VoIP, ZT networking, edge cases, and MRR integrity.
|
||||||
|
|
||||||
|
Run after any change to `quote-engine.js`, `quote-pricing.js`, or pricing JSON values.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ACTIVE SECTIONS (I–VI) — All Structurally Active
|
||||||
|
|
||||||
|
| # | Section | Display Order | Key Logic |
|
||||||
|
|---|---------|---------------|-----------|
|
||||||
|
| I | User Package | 1st | M365 Included ($130/user) vs BYOL ($110/user); 4 add-ons |
|
||||||
|
| II | Endpoint Package | 2nd | $35/endpoint; USB Blocking + Bare Metal Backup add-ons |
|
||||||
|
| III | Site Management | 3rd | Floor + minimum threshold; ZT supplement; 1PWM surcharge; waivable |
|
||||||
|
| IV | Server Management | 4th | $120/server |
|
||||||
|
| V | Zero Trust Networking (HaaS) | 5th | ZT seats + routers |
|
||||||
|
| VI | VoIP / Unified Communications (UCaaS) | 6th | 3 tiers + desk phone + eFax |
|
||||||
|
| — | Contract & Onboarding | Settings bar | M2M / 12mo (3%) / 24mo (5%); onboarding auto-calc or manual; HST 13% |
|
||||||
|
|
||||||
|
All sections use unified `sec-controls-row` header layout: stepper + label badge + price badge.
|
||||||
|
Sections default to collapsed. Inner collapsibles default to collapsed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## KEY PRICING CONSTANTS
|
||||||
|
|
||||||
|
```js
|
||||||
|
RATE_M365: 130 // per user/mo — M365 Included license
|
||||||
|
RATE_BYOL: 110 // per user/mo — Bring Your Own License
|
||||||
|
RATE_ENDPOINT: 35 // per endpoint/mo
|
||||||
|
RATE_SERVER: 120 // per server/mo
|
||||||
|
ADMIN_FEE_FLOOR: 150
|
||||||
|
ADMIN_FEE_MINIMUM: 650
|
||||||
|
DISCOUNT_12MO: 0.03 // 3%
|
||||||
|
DISCOUNT_24MO: 0.05 // 5%
|
||||||
|
HST_RATE: 0.13 // Ontario
|
||||||
|
VOIP_RATE_BASIC: 28 // per seat/mo
|
||||||
|
VOIP_RATE_STANDARD: 35
|
||||||
|
VOIP_RATE_PREMIUM: 45
|
||||||
|
```
|
||||||
|
|
||||||
|
Pricing can be overridden at runtime via `package-prices.json`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NON-NEGOTIABLES (Hard Constraints)
|
||||||
|
|
||||||
|
These are inviolable. Every change must preserve them:
|
||||||
|
|
||||||
|
1. **HTML shell is stable.** DOM IDs are a contract. Mobile sync maps 100+ ID pairs (desktop ↔ `_m` suffix). Renaming an ID breaks sync silently and catastrophically.
|
||||||
|
2. **Quote math is correct.** Any change to `quote-engine.js` requires before/after validation of all outputs. Do not "clean up" math without proving equivalence.
|
||||||
|
3. **localStorage persistence is intact.** `saveState()` and `restoreState()` must round-trip cleanly. Verify after any form/state changes.
|
||||||
|
4. **All four themes must work.** Dark (default), Light (soft khaki), Glass (glassmorphism), 70s Retro. Changes to tokens or components cascade to all four.
|
||||||
|
5. **Mobile parity is maintained.** The sidebar clone in the mobile panel must stay in sync. Mobile UX must be usable on a 375px viewport.
|
||||||
|
6. **Print/PDF export must be tested after CSS changes.** Print CSS lives in a separate file but is sensitive to component class changes.
|
||||||
|
7. **No framework or build-tool migration.** This is vanilla JS by design.
|
||||||
|
8. **No broad rewrites.** Surgical, approved changes only.
|
||||||
|
9. **Sections IV–VI remain deferred** unless explicitly reopened.
|
||||||
|
10. **Read before editing.** Always inspect the current code before making changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BETA WORK PRIORITIES
|
||||||
|
|
||||||
|
Work in this order unless directed otherwise. Each priority includes UX, code, and QA dimensions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PRIORITY 1 — Visual Design System Audit & Elevation
|
||||||
|
|
||||||
|
**Goal:** Achieve visual cohesion and professional polish across all themes that rivals a SaaS product.
|
||||||
|
|
||||||
|
**UI/UX Audit Checklist:**
|
||||||
|
- [ ] Typography hierarchy — Is Cinzel/Poppins/Lato used consistently? Are font sizes, weights, and line-heights harmonious across sections?
|
||||||
|
- [ ] Spacing system — Is padding/margin using tokens consistently? Are section cards visually balanced?
|
||||||
|
- [ ] Color usage — Are `--accent`, `--green`, `--amber`, `--muted` used purposefully, not decoratively?
|
||||||
|
- [ ] Interactive states — Do all buttons, inputs, toggles, and checkboxes have clear hover/focus/active states in all four themes?
|
||||||
|
- [ ] Card hierarchy — Is there a clear visual distinction between level-1 containers, level-2 cards, and level-3 controls?
|
||||||
|
- [ ] Icon consistency — Are Font Awesome icons used at consistent sizes, weights, and optical alignment?
|
||||||
|
- [ ] Section header design — Are the numbered section headers (I, II, III) visually strong and scannable?
|
||||||
|
- [ ] Sidebar layout — Does the sidebar feel like a polished financial summary panel, not a DOM dump?
|
||||||
|
- [ ] Progress bars — Are the progress bars in Section I legible and purposeful?
|
||||||
|
- [ ] Nudge panel — Does the nudge carousel feel like a smart sales assistant, not a popup?
|
||||||
|
|
||||||
|
**CSS Architecture Health:**
|
||||||
|
- [ ] Are there redundant/conflicting rules in `components.css` vs `responsive.css`?
|
||||||
|
- [ ] Are design tokens being used everywhere they should be, or are magic numbers scattered?
|
||||||
|
- [ ] Is the Glass theme consistent and not broken at any viewport?
|
||||||
|
- [ ] Is the Light theme soft and readable without feeling washed out?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PRIORITY 2 — Interaction Design & UX Flow
|
||||||
|
|
||||||
|
**Goal:** The tool should feel intuitive, responsive, and professionally crafted to a prospect sitting across the table.
|
||||||
|
|
||||||
|
**Interaction Audit:**
|
||||||
|
- [ ] **Onboarding flow clarity** — When a user opens the tool fresh, is the first action obvious?
|
||||||
|
- [ ] **Section collapse/expand** — Is the animation smooth? Are collapsed section summaries accurate and helpful?
|
||||||
|
- [ ] **Add-on toggle behavior** — Does toggling add-ons give clear feedback (visual state + sidebar update)?
|
||||||
|
- [ ] **Stepper inputs** — Do +/- steppers feel snappy? Is there clear min/max clamping behavior?
|
||||||
|
- [ ] **Contract term selection** — Is the 3-option selector clearly communicating the discount impact?
|
||||||
|
- [ ] **Onboarding fee override** — Is the manual override UX clear (placeholder, lock icon, restore-to-auto)?
|
||||||
|
- [ ] **Admin fee waiver** — Is the waiver checkbox prominent enough? Is the consequence visible immediately?
|
||||||
|
- [ ] **Sidebar update speed** — Is the live update fast enough to feel real-time?
|
||||||
|
- [ ] **Nudge carousel** — Is the 30-second rotation appropriate? Does it feel helpful or distracting?
|
||||||
|
- [ ] **Theme toggle** — Is the toggle clearly labeled for current/next state? Is the transition smooth?
|
||||||
|
- [ ] **Reset confirmation modal** — Is it clear what gets destroyed? Is the secondary action (cancel) prominent?
|
||||||
|
- [ ] **Export buttons** — Are Print and JSON export clearly labeled and discoverable?
|
||||||
|
|
||||||
|
**Keyboard & Accessibility:**
|
||||||
|
- [ ] All interactive elements reachable via Tab in logical order
|
||||||
|
- [ ] Focus ring visible in all themes
|
||||||
|
- [ ] ARIA labels accurate and present on all form controls
|
||||||
|
- [ ] Color contrast ratios pass WCAG AA in Light theme (most at-risk)
|
||||||
|
- [ ] Screen reader order matches visual order
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PRIORITY 3 — Responsive Design Hardening
|
||||||
|
|
||||||
|
**Goal:** The layout should feel elastically fluid at every breakpoint, not brittle or hacked together.
|
||||||
|
|
||||||
|
**Breakpoint Audit:**
|
||||||
|
- [ ] 1800px+ — Does the max-width container feel appropriately constrained?
|
||||||
|
- [ ] 1400-1800px — Desktop sweet spot. Is the 3:2 main/sidebar split balanced?
|
||||||
|
- [ ] 1100-1400px — Narrower desktop. Do section cards reflow without overlapping?
|
||||||
|
- [ ] 780-1100px — Tablet. Does the layout gracefully switch to sidebar-as-panel?
|
||||||
|
- [ ] 480-780px — Mobile-landscape/small tablet. Single column, all controls accessible?
|
||||||
|
- [ ] 375-480px — Small mobile. Is the floating MRR pill positioned correctly? Does the bottom sheet open cleanly?
|
||||||
|
- [ ] 320px — Edge case. Does anything catastrophically break?
|
||||||
|
|
||||||
|
**Responsive Rules:**
|
||||||
|
- Prefer `clamp()`, container queries, and token-based adjustments over stacked `@media` hacks
|
||||||
|
- Section cards should never clip or overflow their container
|
||||||
|
- Contract/onboarding settings must adapt before content gets squeezed
|
||||||
|
- Touch targets must be ≥44px on mobile
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PRIORITY 4 — Quote Engine & Business Logic Verification
|
||||||
|
|
||||||
|
**Goal:** Every line-item and total in the sidebar is mathematically correct and matches what the invoice shows.
|
||||||
|
|
||||||
|
**Verification Matrix (test each combination):**
|
||||||
|
|
||||||
|
| Test | What to Verify |
|
||||||
|
|------|---------------|
|
||||||
|
| 0 users, 1 endpoint | Admin fee floor triggers correctly |
|
||||||
|
| 5 users M365, 3 endpoints, no add-ons | Base MRR matches manual calculation |
|
||||||
|
| 5 users BYOL + 1Password + Zero Trust | Add-on stacking is correct |
|
||||||
|
| 12-month term | 3% discount applied to base MRR only (not admin or HST) |
|
||||||
|
| 24-month term | 5% discount; onboarding auto-waived |
|
||||||
|
| Manual onboarding override | Auto-calc disabled; manual value preserved on save/restore |
|
||||||
|
| HST toggle | 13% applied only to MRR+onboarding (not before); first invoice total correct |
|
||||||
|
| Admin fee waiver | Admin fee = $0; "Value Unlocked" reflects waived amount |
|
||||||
|
| VoIP Basic 3 seats + 2 desk phones | VoIP cost correct; eFax add-on stacks |
|
||||||
|
| Full configuration (all add-ons, 24mo, HST) | All components sum correctly; no double-counting |
|
||||||
|
| Save → Reload | All values restore exactly; no drift |
|
||||||
|
| Export JSON → parse | All keys present; math cross-checks against UI |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PRIORITY 5 — Mobile Experience Completeness
|
||||||
|
|
||||||
|
**Goal:** A salesperson can hand their phone to a prospect and the quote tool is fully usable.
|
||||||
|
|
||||||
|
**Mobile Audit:**
|
||||||
|
- [ ] Floating MRR pill — correct value, correct position, visible in all themes
|
||||||
|
- [ ] Bottom sheet panel opens smoothly (slide-up animation clean)
|
||||||
|
- [ ] Scroll lock applied correctly when panel open (body doesn't scroll behind)
|
||||||
|
- [ ] All sidebar elements clone correctly into mobile panel (100+ pairs)
|
||||||
|
- [ ] HST toggle syncs bidirectionally (desktop ↔ mobile)
|
||||||
|
- [ ] Quote values in mobile panel match desktop exactly
|
||||||
|
- [ ] Close gestures (× button, Escape key, backdrop tap) all work
|
||||||
|
- [ ] Print/Export buttons accessible from mobile panel
|
||||||
|
- [ ] No clipped text or overflowing elements in mobile panel
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PRIORITY 6 — Print/PDF Invoice Quality
|
||||||
|
|
||||||
|
**Goal:** The printed invoice looks like it came from a professional MSP's billing system, not an HTML form.
|
||||||
|
|
||||||
|
**Print Audit:**
|
||||||
|
- [ ] Logo renders correctly (embedded SVG, not broken)
|
||||||
|
- [ ] Client name, quote ref, and date are on every page header
|
||||||
|
- [ ] All line items appear with correct labels and amounts
|
||||||
|
- [ ] Feature checklist is legible and cleanly formatted
|
||||||
|
- [ ] Totals section is visually prominent
|
||||||
|
- [ ] Value unlocked section is formatted correctly
|
||||||
|
- [ ] Comparison (vs. in-house IT) is present and readable
|
||||||
|
- [ ] No phantom scrollbars or UI chrome bleeds into print
|
||||||
|
- [ ] Page breaks don't split line-item rows awkwardly
|
||||||
|
- [ ] Fonts render in print (or fallback gracefully)
|
||||||
|
- [ ] All themes produce same print output (print CSS is theme-independent)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PRIORITY 7 — Sections IV–VI: Beta-Ready Scaffolding
|
||||||
|
|
||||||
|
**Goal:** Even if Sections IV–VI remain content-gated, their code, UX framework, and CSS should be clean enough to activate with minimal effort.
|
||||||
|
|
||||||
|
**Audit:**
|
||||||
|
- [ ] Section IV (Zero Trust Networking) — Is the HTML structure complete? Are inputs wired to `readFormState()`? Does the engine include ZT costs?
|
||||||
|
- [ ] Section V (VoIP/UCaaS) — Are all 3 tier selectors functional? Desk phone HaaS wired? eFax add-on wired? Savings comparison functional?
|
||||||
|
- [ ] Section VI — Is it a clean placeholder with no broken references?
|
||||||
|
- [ ] Deferred section CSS — Are styles isolated so activating them doesn't cause layout contamination?
|
||||||
|
- [ ] Quote engine — Does it calculate ZT/VoIP costs when those inputs are present, even if sections are hidden?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PRIORITY 8 — Code Quality & Documentation Sync
|
||||||
|
|
||||||
|
**Goal:** Any engineer should be able to pick up this codebase in 10 minutes and understand what's happening.
|
||||||
|
|
||||||
|
**Code Quality Checklist:**
|
||||||
|
- [ ] No dead code in active modules (check for unused functions, unreachable branches)
|
||||||
|
- [ ] No magic numbers — pricing constants should reference `DEFAULTS` keys, not hardcoded values
|
||||||
|
- [ ] No duplicate logic between `quote-engine.js` and `quote-render.js`
|
||||||
|
- [ ] `mobile-sync.js` sync map is clean — no stale ID pairs for removed elements
|
||||||
|
- [ ] CSS custom properties used consistently — no redundant fallback values where tokens are sufficient
|
||||||
|
- [ ] `quote-export.js` HTML template is clean — no CSS class references that no longer exist
|
||||||
|
- [ ] `SVS-MSP-Calculator.js` `update()` function is linear and readable — no side effects that aren't obvious
|
||||||
|
|
||||||
|
**Documentation Sync:**
|
||||||
|
- [ ] `docs/README.md` reflects current file structure and phase status
|
||||||
|
- [ ] `docs/code-verification.md` has been updated with latest known-good state
|
||||||
|
- [ ] `docs/regression-checklist.md` covers all active sections and export paths
|
||||||
|
- [ ] `docs/quote-rules.md` reflects current pricing and business rule implementation
|
||||||
|
- [ ] `docs/phase-roadmap.md` is updated to reflect beta completion criteria
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WORKING PROTOCOL FOR THIS SESSION
|
||||||
|
|
||||||
|
### Before Making Any Change
|
||||||
|
1. Read the relevant file(s) first — do not edit from memory
|
||||||
|
2. Identify the minimal change that achieves the goal
|
||||||
|
3. Check if the change touches any of the regression hotspots (see below)
|
||||||
|
4. Confirm the change won't break mobile sync, theme cascade, or persistence
|
||||||
|
|
||||||
|
### Regression Hotspots — Extra Caution Required
|
||||||
|
| Area | Risk | Why |
|
||||||
|
|------|------|-----|
|
||||||
|
| `quote-engine.js` math | Critical | Business-critical; errors generate wrong quotes in real sales calls |
|
||||||
|
| localStorage round-trip | High | Silent failures; user loses configured quote |
|
||||||
|
| Mobile sync ID map | High | 100+ pairs; silently desyncs if IDs change |
|
||||||
|
| Print/PDF CSS | Medium | Separate cascade; component class changes cascade here |
|
||||||
|
| Theme switching | Medium | All four themes affected by token/component changes |
|
||||||
|
| `update()` call chain | Medium | Side effects in render sequence can cascade silently |
|
||||||
|
|
||||||
|
### After Making Changes
|
||||||
|
1. Verify syntax (especially JS — no console errors on load)
|
||||||
|
2. Check all four themes render correctly
|
||||||
|
3. Check mobile panel renders correctly at ≤780px
|
||||||
|
4. Verify sidebar totals are mathematically correct for a test quote
|
||||||
|
5. If CSS touched: verify print output is unaffected
|
||||||
|
6. Update `docs/code-verification.md` if a known-good state changes
|
||||||
|
|
||||||
|
### Commit Protocol
|
||||||
|
- Commits should be small and focused (one concern per commit)
|
||||||
|
- Commit messages should state what changed and why (not just "fix css")
|
||||||
|
- Do not combine unrelated changes in one commit
|
||||||
|
- Do not commit `.bak-focusmode` files
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UX/UI DESIGN PRINCIPLES FOR THIS PROJECT
|
||||||
|
|
||||||
|
These principles guide every UI decision:
|
||||||
|
|
||||||
|
1. **Sales clarity over visual novelty.** If a prospect can't read a number at a glance, the design failed.
|
||||||
|
2. **Trust through polish.** A janky tooltip or misaligned input erodes prospect confidence. Every pixel matters.
|
||||||
|
3. **Progressive disclosure.** Lead with the total MRR. Let detail unfold as needed (collapsed sections, sidebar).
|
||||||
|
4. **Reduce cognitive load.** Group related controls. Use spacing and color to indicate hierarchy, not decoration.
|
||||||
|
5. **Feedback immediacy.** Every input change must visibly update the sidebar within 1 frame. No lag.
|
||||||
|
6. **Consistency as reliability.** Spacing, typography, and color behavior should be predictable. Surprises feel like bugs.
|
||||||
|
7. **Dark theme is the flagship.** Light and Glass must meet the same bar. No theme should feel like a degraded experience.
|
||||||
|
8. **Mobile is a first-class use case.** A sales rep on a tablet or phone must be able to run a full quote.
|
||||||
|
9. **The sidebar is the hero.** It's where the value proposition lives. Design it with the weight of a financial summary.
|
||||||
|
10. **Copy is UI.** Labels, nudges, section headers, and button text are all UX. Make them purposeful and confident.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FILE READ ORDER FOR NEW SESSIONS
|
||||||
|
|
||||||
|
To resume efficiently, read in this order:
|
||||||
|
1. `docs/README.md` — current project state
|
||||||
|
2. `docs/phase-roadmap.md` — approved work and constraints
|
||||||
|
3. `docs/code-verification.md` — known-good baseline
|
||||||
|
4. `docs/MASTER-SESSION-PROMPT.md` — this file (if not already loaded)
|
||||||
|
5. Then only the source files relevant to the specific task
|
||||||
|
|
||||||
|
For business logic questions: `docs/quote-rules.md`
|
||||||
|
For manual QA: `docs/regression-checklist.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BETA DEFINITION OF DONE
|
||||||
|
|
||||||
|
The build is ready to call "beta" when:
|
||||||
|
|
||||||
|
- [ ] All Sections I–III are visually polished and functionally complete
|
||||||
|
- [ ] All four themes pass a full visual review at all major breakpoints
|
||||||
|
- [ ] Print/PDF invoice is clean and professionally branded
|
||||||
|
- [ ] Mobile panel is fully synced and usable at 375px
|
||||||
|
- [ ] Quote math passes all combinations in the verification matrix
|
||||||
|
- [ ] localStorage save/restore is tested and clean
|
||||||
|
- [ ] JSON export is valid and complete
|
||||||
|
- [ ] All nudges fire correctly for their trigger conditions
|
||||||
|
- [ ] Section collapse/expand summaries are accurate
|
||||||
|
- [ ] Comparison (vs. in-house IT) and VoIP savings panels show correct values
|
||||||
|
- [ ] No console errors on fresh load in any theme
|
||||||
|
- [ ] All docs are updated and accurate
|
||||||
|
- [ ] Sections IV–VI are scaffolded cleanly, ready to activate on demand
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This document is the canonical session brief for the SVS MSP CALC beta push. Update it when constraints, priorities, or decisions change.*
|
||||||
85
docs/QUICK-REF.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Quick Reference — SVS MSP CALC
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
Vanilla HTML5/CSS3/JS (ES5-compatible). No frameworks, no npm, no build tools. Open HTML in browser to run.
|
||||||
|
|
||||||
|
## File Map
|
||||||
|
|
||||||
|
### JS Runtime
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `SVS-MSP-Calculator.js` | Orchestration: `update()`, `calcQuote()`, section toggles, `initQuote()` |
|
||||||
|
| `quote-engine.js` | Pure math: `calculateQuote(state, pricing)`, `readFormState()`, `getPricingConfig()` |
|
||||||
|
| `quote-pricing.js` | Defaults (34 keys), JSON loader, `getSnapshot()`, globals |
|
||||||
|
| `quote-render.js` | DOM rendering: sidebar, summaries, nudges, `setSummary()`, `renderNudge()` |
|
||||||
|
| `quote-persistence.js` | `saveState()` / `restoreState()` / `resetState()` via localStorage |
|
||||||
|
| `quote-export.js` | Print/PDF (respects HST toggle) + JSON export (schema v1.0) |
|
||||||
|
| `theme-manager.js` | Dark/Light/Glass/70s Retro toggle + persistence |
|
||||||
|
| `mobile-sync.js` | Clones sidebar to mobile panel, wraps `update()` for `_m` ID sync |
|
||||||
|
|
||||||
|
### CSS (load order via manifest)
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `*-tokens.css` | Design tokens — single source for spacing, colors, radii, typography |
|
||||||
|
| `*-base.css` | Global chrome, top bar, resets |
|
||||||
|
| `*-layout.css` | Grid: `.outer`, `.main-col`, `.sidebar`, `.client-bar` |
|
||||||
|
| `*-components.css` | Sections, headers, `sec-controls-row`, badges, steppers, sidebar, VS comparison, export |
|
||||||
|
| `*-responsive.css` | Media queries: ≤1350, ≤1100, ≤900, ≤600 + landscape |
|
||||||
|
| `*-print.css` | Print overrides — hides controls, forces expand |
|
||||||
|
| `*-light.css` | Light theme color overrides |
|
||||||
|
| `*-glass.css` | Glass theme (glassmorphism) overrides |
|
||||||
|
| `*-70retro.css` | 70s Retro theme overrides |
|
||||||
|
|
||||||
|
### HTML Structure (section display order)
|
||||||
|
| Order | ID | Numeral | Title | Has Stepper |
|
||||||
|
|-------|----|---------|-------|-------------|
|
||||||
|
| 1 | sec-02 | I | User Package | userCount |
|
||||||
|
| 2 | sec-03 | II | Endpoint Package | endpointCount |
|
||||||
|
| 3 | sec-01 | III | Site Management | — (badges only) |
|
||||||
|
| 4 | sec-04 | IV | Server Management | serverCount |
|
||||||
|
| 5 | sec-05 | V | Zero Trust Networking (HaaS) | ztNetSeats |
|
||||||
|
| 6 | sec-06 | VI | VoIP / Unified Comms (UCaaS) | voipSeats |
|
||||||
|
|
||||||
|
### Section Header Layout (all 6 sections)
|
||||||
|
```
|
||||||
|
Grid Row 1: [numeral] [title-block] [chevron]
|
||||||
|
Grid Row 2: [sec-controls-row: stepper + badge + price]
|
||||||
|
Grid Row 3: [section-subtitle — expanded only]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key DOM IDs (do not rename — mobile sync depends on these)
|
||||||
|
- Inputs: `userCount`, `endpointCount`, `serverCount`, `ztNetSeats`, `ztNetRouters`, `voipSeats`
|
||||||
|
- Summaries: `sec01-summary` through `sec06-summary`
|
||||||
|
- Admin: `adminFeeDisplay`, `adminWaived`, `feeBreakdown`
|
||||||
|
- Sidebar: `sidebarMRR`, `sidebarAnnual`, `sidebarDiscount`, etc.
|
||||||
|
- Progress: `floorBar`, `floorNote`
|
||||||
|
|
||||||
|
### Pricing Defaults (from quote-pricing.js)
|
||||||
|
```
|
||||||
|
Users: M365 $140 (m2m) / $130 (12mo/24mo) | BYOL $110 | ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
|
||||||
|
Endpoints: $35/ea | USB +$4 | BMB +$25
|
||||||
|
Servers: $120/ea
|
||||||
|
ZT Net: $25/seat | $100/router
|
||||||
|
Admin: Floor $150 | Min $650 | ZT premium +$250 | 1PWM markup 10%
|
||||||
|
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
|
||||||
|
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary onboarding
|
||||||
|
HST: 13% (Ontario)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
254 tests, zero dependencies. Run after any pricing/engine changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Remind User
|
||||||
|
After reading docs, always say:
|
||||||
|
> You have an automated test suite (254 tests). I can run it anytime, or modify it for new scenarios.
|
||||||
|
|
||||||
|
## Danger Zones
|
||||||
|
- DOM IDs → mobile sync breaks silently if renamed
|
||||||
|
- `quote-engine.js` math → run tests after any change
|
||||||
|
- Print CSS → sensitive to component class changes
|
||||||
|
- `update()` call chain → side effects cascade
|
||||||
|
- localStorage key: `svs-msp-quote-v1`
|
||||||
62
docs/README.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# SVS MSP Calculator
|
||||||
|
|
||||||
|
Static sales-facing quote builder for SVS Managed Services.
|
||||||
|
Plain HTML/CSS/JS — no frameworks, no build tools, no npm.
|
||||||
|
|
||||||
|
**Status:** Beta complete (Stages 1–9). 254/254 tests passing.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. Open `SVS-MSP-Calculator.html` in a browser (works on `file://`)
|
||||||
|
2. Edit pricing in `package-prices-data.js`
|
||||||
|
3. Run tests: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
|
||||||
|
## What It Does
|
||||||
|
|
||||||
|
- Configures managed service packages (Users, Endpoints, Servers, ZT, VoIP)
|
||||||
|
- Contract terms with auto-calculated onboarding and discounts
|
||||||
|
- Live quote sidebar with MRR breakdown, VS Hiring comparison
|
||||||
|
- 4 themes: Dark, Light, Glass, Retro Cyberpunk
|
||||||
|
- Print/PDF export, JSON export/import, localStorage persistence
|
||||||
|
- Mobile-responsive with full-screen quote panel
|
||||||
|
|
||||||
|
## File Map
|
||||||
|
|
||||||
|
### Runtime JS
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `SVS-MSP-Calculator.js` | Orchestration, init, keyboard shortcuts |
|
||||||
|
| `quote-engine.js` | Pure quote math (no DOM) |
|
||||||
|
| `quote-pricing.js` | Pricing defaults + `package-prices-data.js` loader |
|
||||||
|
| `quote-render.js` | DOM rendering, nudges, summaries |
|
||||||
|
| `quote-persistence.js` | localStorage save/restore/reset |
|
||||||
|
| `quote-export.js` | Print/PDF + JSON export |
|
||||||
|
| `quote-import.js` | JSON quote import |
|
||||||
|
| `theme-manager.js` | 4-theme switching |
|
||||||
|
| `mobile-sync.js` | Mobile panel + 100+ element sync |
|
||||||
|
| `package-prices-data.js` | Pricing data (edit this to change prices) |
|
||||||
|
|
||||||
|
### CSS
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `SVS-MSP-Calculator.css` | Manifest (@imports all CSS) |
|
||||||
|
| `*-tokens.css` | Design tokens, spacing, colors |
|
||||||
|
| `*-base.css` | Global chrome |
|
||||||
|
| `*-layout.css` | Grid, header, main/sidebar |
|
||||||
|
| `*-components.css` | Section cards, controls, sidebar |
|
||||||
|
| `*-responsive.css` | 3 structural breakpoints |
|
||||||
|
| `*-print.css` | Print-specific rules |
|
||||||
|
| `*-light.css` | Light theme |
|
||||||
|
| `*-glass.css` | Glass theme |
|
||||||
|
| `*-70retro.css` | Retro Cyberpunk theme |
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `QUICK-REF.md` | Compact file map, DOM IDs, pricing, danger zones |
|
||||||
|
| `MASTER-SESSION-PROMPT.md` | Full architecture brief + constraints |
|
||||||
|
| `CHECKPOINT.md` | Build status, all completed work |
|
||||||
|
| `quote-rules.md` | Business logic / pricing rules |
|
||||||
|
| `regression-checklist.md` | QA validation procedures |
|
||||||
|
| `phase-roadmap.md` | Approved work boundaries |
|
||||||
|
| `code-verification.md` | Known-good baseline |
|
||||||
89
docs/STAGE10-UI-SESSION-PROMPT.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# SVS MSP CALC — STAGE 10: UI & THEME POLISH
|
||||||
|
# Generated: 2026-03-15
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHERE WE ARE
|
||||||
|
|
||||||
|
**Stages 1–9: COMPLETE.** Beta build is production-quality for Sections I–III.
|
||||||
|
**Tests:** 254/254 passing.
|
||||||
|
**Spacing tokens:** Consolidated (150+ magic numbers → CSS custom properties).
|
||||||
|
**Retro theme visual QA:** Passed at all viewports (375/1100/1400/2500/2800px).
|
||||||
|
**Pricing:** Single source in `package-prices-data.js`, addon labels sync dynamically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STAGE 10 FOCUS: UI / THEME / COLOR CONSISTENCY
|
||||||
|
|
||||||
|
This stage is about visual polish, color consistency, and interface usability across all 4 themes.
|
||||||
|
|
||||||
|
### Known Issues to Investigate
|
||||||
|
- **Too many similar-but-slightly-off colors** within individual themes — need audit and consolidation
|
||||||
|
- **Color consistency per theme** — some elements use raw hex where they should use tokens
|
||||||
|
- **Usability polish** — any controls, labels, or interactions that could feel tighter or more intuitive
|
||||||
|
- **Cross-theme consistency** — ensure the same UI elements feel equivalent across Dark/Light/Glass/Retro
|
||||||
|
|
||||||
|
### Approach
|
||||||
|
|
||||||
|
1. **Color Audit** — For each theme, extract all unique colors actually used. Group by purpose (text, surface, border, accent). Flag duplicates that are close but not identical (e.g., two slightly different grays serving the same role).
|
||||||
|
|
||||||
|
2. **Token Consolidation** — Where colors are inconsistent, consolidate to existing tokens or define new ones. Goal: fewer unique colors, each with a clear semantic role.
|
||||||
|
|
||||||
|
3. **Per-Theme Walkthrough** — User provides screenshots. AI audits for:
|
||||||
|
- Color contrast issues (text readability)
|
||||||
|
- Inconsistent element styling across themes
|
||||||
|
- Visual weight imbalances
|
||||||
|
- Elements that "don't belong" visually
|
||||||
|
|
||||||
|
4. **Surgical Fixes** — Each fix is small, tested, and verified across all 4 themes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SESSION WORKFLOW
|
||||||
|
|
||||||
|
1. **Start with a color audit** — Use Explore agent to scan all theme CSS files for unique color values, group by semantic role
|
||||||
|
2. **User provides screenshots** of specific areas that feel "off"
|
||||||
|
3. **AI proposes fixes** with before/after reasoning
|
||||||
|
4. **Apply fixes surgically** — one logical change at a time
|
||||||
|
5. **User verifies visually** — confirms or adjusts
|
||||||
|
6. **Run tests** after any render/engine changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HARD CONSTRAINTS
|
||||||
|
|
||||||
|
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
|
||||||
|
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
3. All 4 themes must work after every change
|
||||||
|
4. No frameworks, no npm — vanilla only
|
||||||
|
5. Surgical changes only — read before editing
|
||||||
|
6. Sections IV–VI are placeholders — do not activate
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## THEME FILES
|
||||||
|
|
||||||
|
| Theme | File | Base |
|
||||||
|
|-------|------|------|
|
||||||
|
| Dark (default) | `SVS-MSP-Calculator-tokens.css` | All colors defined here |
|
||||||
|
| Light | `SVS-MSP-Calculator-light.css` | Overrides dark tokens |
|
||||||
|
| Glass | `SVS-MSP-Calculator-glass.css` | Glassmorphism overrides |
|
||||||
|
| Retro | `SVS-MSP-Calculator-70retro.css` | Cyberpunk overrides |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VERIFICATION
|
||||||
|
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
|
||||||
|
254 tests, zero dependencies. Run after any pricing/engine/render changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CONTEXT MANAGEMENT
|
||||||
|
|
||||||
|
After completing work:
|
||||||
|
- Update `docs/CHECKPOINT.md` with results
|
||||||
|
- If context is heavy, create `docs/STAGE11-SESSION-PROMPT.md` for the next chat
|
||||||
176
docs/STAGE3-SESSION-PROMPT.md
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
# SVS MSP CALC — STAGE 3 SESSION PROMPT
|
||||||
|
# Post-Beta Hardening & Manual QA
|
||||||
|
# Generated: 2026-03-15
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHERE WE ARE
|
||||||
|
|
||||||
|
**Beta build: COMPLETE.** Sections I–III are production-quality.
|
||||||
|
**Accessibility/performance audit: COMPLETE.** Phase 5 fixes applied.
|
||||||
|
**Tests:** 88/88 passing.
|
||||||
|
**Sections IV–VI:** Intentionally deferred as placeholders — do not activate.
|
||||||
|
|
||||||
|
### Completed Stages
|
||||||
|
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
|
||||||
|
- **Stage 2** — Beta build (Phases 1–4: bug fixes, visual polish, UX hardening, docs/QA)
|
||||||
|
- **Phase 5** — Accessibility/performance audit (aria-expanded, focus traps, stepper labels, glass scroll fix, mobile sync optimization)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## START EVERY SESSION BY READING
|
||||||
|
|
||||||
|
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
|
||||||
|
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
|
||||||
|
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
|
||||||
|
4. This file — session goals and context
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROJECT SNAPSHOT
|
||||||
|
|
||||||
|
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
|
||||||
|
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
|
||||||
|
**Used by:** SVS sales team, live on screen during prospect calls
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
```
|
||||||
|
svsmspcalc/
|
||||||
|
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
|
||||||
|
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
|
||||||
|
├── quote-engine.js # Pure quote math (197 lines)
|
||||||
|
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
|
||||||
|
├── quote-render.js # DOM rendering + nudges (729 lines)
|
||||||
|
├── quote-persistence.js # localStorage save/restore (225 lines)
|
||||||
|
├── quote-export.js # Print/PDF + JSON export (299 lines)
|
||||||
|
├── theme-manager.js # 4-theme switching (121 lines)
|
||||||
|
├── mobile-sync.js # Mobile panel sync (275 lines)
|
||||||
|
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
|
||||||
|
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
|
||||||
|
├── SVS-MSP-Calculator-base.css # Global chrome
|
||||||
|
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
|
||||||
|
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (66KB)
|
||||||
|
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
|
||||||
|
├── SVS-MSP-Calculator-print.css # Print-specific rules
|
||||||
|
├── SVS-MSP-Calculator-light.css # Light theme overrides
|
||||||
|
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
|
||||||
|
├── SVS-MSP-Calculator-70retro.css # 70s Retro theme overrides
|
||||||
|
├── package-prices.csv # Overrideable pricing (31 rows)
|
||||||
|
├── tests/
|
||||||
|
│ └── test-quote-engine.js # 88 tests (Node.js, zero deps)
|
||||||
|
└── docs/
|
||||||
|
├── CHECKPOINT.md # Build status checkpoint
|
||||||
|
├── MASTER-SESSION-PROMPT.md # Full architecture brief
|
||||||
|
├── QUICK-REF.md # Compact reference
|
||||||
|
├── README.md
|
||||||
|
├── code-verification.md # Known-good baseline
|
||||||
|
├── phase-roadmap.md # Phase status
|
||||||
|
├── quote-rules.md # Business logic rules
|
||||||
|
├── regression-checklist.md # Test procedures
|
||||||
|
└── STAGE3-SESSION-PROMPT.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HARD CONSTRAINTS (NON-NEGOTIABLE)
|
||||||
|
|
||||||
|
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
|
||||||
|
2. 88 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
|
||||||
|
4. All 4 themes must work after every change
|
||||||
|
5. Mobile parity maintained
|
||||||
|
6. Print/PDF tested after CSS changes
|
||||||
|
7. No frameworks, no npm — vanilla only
|
||||||
|
8. Surgical changes only — read before editing
|
||||||
|
9. Sections IV–VI are placeholders — do not activate or build out
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STAGE 3 GOALS — Choose priorities from this menu:
|
||||||
|
|
||||||
|
### Option A: Browser-Based Manual QA
|
||||||
|
Full visual/functional walkthrough at all breakpoints × 4 themes.
|
||||||
|
Requires user to open the app in a browser and share screenshots or describe issues.
|
||||||
|
|
||||||
|
**Breakpoint matrix:**
|
||||||
|
| Width | Context | Key checks |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| 1800px+ | Wide desktop | Max-width constraint, sidebar positioning |
|
||||||
|
| 1400px | Standard desktop | 3:2 main/sidebar balance |
|
||||||
|
| 1100px | Tablet/narrow | Single column transition, pill appears |
|
||||||
|
| 900px | Small tablet | Tighter spacing, smaller numerals |
|
||||||
|
| 600px | Phone portrait | Stacked layout, no numeral gutter |
|
||||||
|
| 375px | Small phone | MRR pill position, bottom sheet |
|
||||||
|
| 780px landscape | Phone landscape | 2-col restored, sidebar visible |
|
||||||
|
|
||||||
|
**Theme matrix:** Dark (default), Light, Glass, 70s Retro
|
||||||
|
**Total test grid:** 7 breakpoints × 4 themes = 28 combinations
|
||||||
|
|
||||||
|
### Option B: Feature Work
|
||||||
|
- JSON quote import (load a previously exported .json file)
|
||||||
|
- Enhanced print/PDF with more layout control
|
||||||
|
- Sidebar focus mode refinements
|
||||||
|
- Additional nudge logic
|
||||||
|
- Any specific feature requests
|
||||||
|
|
||||||
|
### Option C: Code Quality Pass
|
||||||
|
- Dead code removal across all modules
|
||||||
|
- CSS deduplication audit
|
||||||
|
- Magic number → token migration in components.css
|
||||||
|
- Mobile sync map completeness verification
|
||||||
|
- Export template class reference audit
|
||||||
|
|
||||||
|
### Option D: Test Coverage Expansion
|
||||||
|
- Add DOM rendering tests (sidebar values match calculations)
|
||||||
|
- Add persistence round-trip tests
|
||||||
|
- Add export schema validation tests
|
||||||
|
- Increase from 88 to 120+ tests
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PRICING REFERENCE
|
||||||
|
|
||||||
|
```
|
||||||
|
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
|
||||||
|
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
|
||||||
|
Endpoints: $35/ea | USB +$4 | BMB +$25
|
||||||
|
Servers: $120/ea
|
||||||
|
ZT Net: $25/seat | $100/router
|
||||||
|
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
|
||||||
|
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
|
||||||
|
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
|
||||||
|
HST: 13% (Ontario)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHAT WAS DONE IN PHASE 5 (for context)
|
||||||
|
|
||||||
|
| Fix | Change |
|
||||||
|
|-----|--------|
|
||||||
|
| `aria-expanded` on toggles | 12 section/collapsible headers in HTML + JS dynamic update |
|
||||||
|
| Focus trap on reset modal | Tab cycles within modal when open |
|
||||||
|
| `aria-label` on steppers | All 12 +/- buttons have descriptive labels |
|
||||||
|
| Glass mobile scroll | `background-attachment: scroll` at ≤1100px |
|
||||||
|
| Mobile sync guard | Skips sync on desktop when panel closed; forces sync on open |
|
||||||
|
| Sync map gaps | Added `sidebarFocusClientName`, `sl-discount-detail`, `sl-value-onboarding-label` |
|
||||||
|
| FA icons invisible on `file://` | All 36 Sharp Solid SVG file refs → inline `data:image/svg+xml` URIs in components.css |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VERIFICATION COMMAND
|
||||||
|
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
|
||||||
|
88 tests, zero dependencies. Run after any pricing/engine/render changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CONTEXT MANAGEMENT
|
||||||
|
|
||||||
|
After completing work:
|
||||||
|
- Update `docs/CHECKPOINT.md` with results
|
||||||
|
- If context is heavy, create `docs/STAGE4-SESSION-PROMPT.md` for the next chat
|
||||||
|
- Keep this document chain as the canonical handoff mechanism
|
||||||
186
docs/STAGE4-SESSION-PROMPT.md
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
# SVS MSP CALC — STAGE 4 SESSION PROMPT
|
||||||
|
# Post-Beta Feature & QA Sprint
|
||||||
|
# Generated: 2026-03-15
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHERE WE ARE
|
||||||
|
|
||||||
|
**Beta build: COMPLETE.** Sections I–III are production-quality.
|
||||||
|
**Accessibility/performance audit: COMPLETE.** Phase 5 fixes applied.
|
||||||
|
**Code quality pass: COMPLETE.** Phase 6 — hardcoded colors tokenized, CSS deduped, dead code removed.
|
||||||
|
**Tests:** 254/254 passing.
|
||||||
|
**Sections IV–VI:** Intentionally deferred as placeholders — do not activate.
|
||||||
|
|
||||||
|
### Completed Stages
|
||||||
|
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
|
||||||
|
- **Stage 2** — Beta build (Phases 1–4: bug fixes, visual polish, UX hardening, docs/QA)
|
||||||
|
- **Phase 5** — Accessibility/performance audit (aria-expanded, focus traps, stepper labels, glass scroll fix, mobile sync optimization)
|
||||||
|
- **Phase 5b** — Font Awesome icon fix (inline data URIs for file:// protocol)
|
||||||
|
- **Stage 3 / Phase 6** — Code quality pass (new `--sky` + `--transition-fast` tokens, button CSS dedup, hardcoded colors → color-mix(), dead null-check removed)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## START EVERY SESSION BY READING
|
||||||
|
|
||||||
|
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
|
||||||
|
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
|
||||||
|
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
|
||||||
|
4. This file — session goals and context
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROJECT SNAPSHOT
|
||||||
|
|
||||||
|
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
|
||||||
|
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
|
||||||
|
**Used by:** SVS sales team, live on screen during prospect calls
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
```
|
||||||
|
svsmspcalc/
|
||||||
|
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
|
||||||
|
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
|
||||||
|
├── quote-engine.js # Pure quote math (197 lines)
|
||||||
|
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
|
||||||
|
├── quote-render.js # DOM rendering + nudges (729 lines)
|
||||||
|
├── quote-persistence.js # localStorage save/restore (225 lines)
|
||||||
|
├── quote-export.js # Print/PDF + JSON export (299 lines)
|
||||||
|
├── quote-import.js # JSON quote import (50 lines)
|
||||||
|
├── theme-manager.js # 4-theme switching (121 lines)
|
||||||
|
├── mobile-sync.js # Mobile panel sync (275 lines)
|
||||||
|
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
|
||||||
|
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
|
||||||
|
├── SVS-MSP-Calculator-base.css # Global chrome
|
||||||
|
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
|
||||||
|
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (66KB)
|
||||||
|
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
|
||||||
|
├── SVS-MSP-Calculator-print.css # Print-specific rules
|
||||||
|
├── SVS-MSP-Calculator-light.css # Light theme overrides
|
||||||
|
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
|
||||||
|
├── SVS-MSP-Calculator-70retro.css # 70s Retro theme overrides
|
||||||
|
├── package-prices.csv # Overrideable pricing (31 rows)
|
||||||
|
├── tests/
|
||||||
|
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
|
||||||
|
└── docs/
|
||||||
|
├── CHECKPOINT.md # Build status checkpoint
|
||||||
|
├── MASTER-SESSION-PROMPT.md # Full architecture brief
|
||||||
|
├── QUICK-REF.md # Compact reference
|
||||||
|
├── README.md
|
||||||
|
├── code-verification.md # Known-good baseline
|
||||||
|
├── phase-roadmap.md # Phase status
|
||||||
|
├── quote-rules.md # Business logic rules
|
||||||
|
├── regression-checklist.md # Test procedures
|
||||||
|
├── STAGE3-SESSION-PROMPT.md # Previous stage prompt
|
||||||
|
└── STAGE4-SESSION-PROMPT.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HARD CONSTRAINTS (NON-NEGOTIABLE)
|
||||||
|
|
||||||
|
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
|
||||||
|
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
|
||||||
|
4. All 4 themes must work after every change
|
||||||
|
5. Mobile parity maintained
|
||||||
|
6. Print/PDF tested after CSS changes
|
||||||
|
7. No frameworks, no npm — vanilla only
|
||||||
|
8. Surgical changes only — read before editing
|
||||||
|
9. Sections IV–VI are placeholders — do not activate or build out
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STAGE 4 GOALS — Choose priorities from this menu:
|
||||||
|
|
||||||
|
### Option A: Browser-Based Manual QA
|
||||||
|
Full visual/functional walkthrough at all breakpoints × 4 themes.
|
||||||
|
Requires user to open the app in a browser and share screenshots or describe issues.
|
||||||
|
|
||||||
|
**Breakpoint matrix:**
|
||||||
|
| Width | Context | Key checks |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| 1800px+ | Wide desktop | Max-width constraint, sidebar positioning |
|
||||||
|
| 1400px | Standard desktop | 3:2 main/sidebar balance |
|
||||||
|
| 1100px | Tablet/narrow | Single column transition, pill appears |
|
||||||
|
| 900px | Small tablet | Tighter spacing, smaller numerals |
|
||||||
|
| 600px | Phone portrait | Stacked layout, no numeral gutter |
|
||||||
|
| 375px | Small phone | MRR pill position, bottom sheet |
|
||||||
|
| 780px landscape | Phone landscape | 2-col restored, sidebar visible |
|
||||||
|
|
||||||
|
**Theme matrix:** Dark (default), Light, Glass, 70s Retro
|
||||||
|
**Total test grid:** 7 breakpoints × 4 themes = 28 combinations
|
||||||
|
|
||||||
|
### Option B: Feature Work
|
||||||
|
- **JSON quote import** — load a previously exported .json file back into the calculator
|
||||||
|
- **Enhanced print/PDF** — more layout control, optional cover page, save-as-PDF flow
|
||||||
|
- **Sidebar focus mode refinements** — expand/collapse behavior, keyboard shortcuts
|
||||||
|
- **Additional nudge logic** — new contextual nudges based on quote configuration
|
||||||
|
- **Any specific feature requests**
|
||||||
|
|
||||||
|
### Option C: Test Coverage Expansion — DONE
|
||||||
|
- ~~Add persistence round-trip tests~~ ✓ (6 tests)
|
||||||
|
- ~~Add export schema validation tests~~ ✓ (18 tests)
|
||||||
|
- ~~Add pricing integrity + engine edge cases~~ ✓ (89 tests)
|
||||||
|
- ~~Add import mapping + invariant tests~~ ✓ (36 tests)
|
||||||
|
- **Result: 88 → 250 tests (162 new)**
|
||||||
|
- Remaining: DOM rendering tests (requires JSDOM), theme token tests
|
||||||
|
|
||||||
|
### Option D: Further Code Quality
|
||||||
|
- Spacing magic numbers → token migration (95+ instances of 14px/16px/20px)
|
||||||
|
- `--transition-fast` token adoption across components.css (remaining 0.15s instances)
|
||||||
|
- CSS selector specificity audit
|
||||||
|
- Print CSS hardening
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHAT WAS DONE IN PHASE 6 (for context)
|
||||||
|
|
||||||
|
| Fix | Change |
|
||||||
|
|-----|--------|
|
||||||
|
| `--sky` token | New per-theme accent for import button: Dark `#38bdf8`, Light `#0e7490`, Glass `#7dd3fc`, Retro `#a34a14` |
|
||||||
|
| `--transition-fast` token | `0.15s` — used in layout.css button transitions |
|
||||||
|
| Button CSS dedup | `.btn-reset-quote` + `.btn-import-quote` merged into grouped selector (10 duplicate properties removed) |
|
||||||
|
| Hardcoded colors → tokens | 8 hardcoded rgba/hex values replaced with `var(--sky)`, `var(--amber)`, and `color-mix()` across 4 theme files |
|
||||||
|
| Dead null-check | `nudgeIndex == null ||` removed from quote-render.js (variable always initialized) |
|
||||||
|
|
||||||
|
**Audit documented (not acted on):**
|
||||||
|
- `fmt()` duplication in render/export (intentional IIFE isolation)
|
||||||
|
- Spacing magic numbers (too many touchpoints)
|
||||||
|
- `console.warn()` statements (intentional error reporting)
|
||||||
|
- No dead functions/exports found across all 8 JS modules
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PRICING REFERENCE
|
||||||
|
|
||||||
|
```
|
||||||
|
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
|
||||||
|
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
|
||||||
|
Endpoints: $35/ea | USB +$4 | BMB +$25
|
||||||
|
Servers: $120/ea
|
||||||
|
ZT Net: $25/seat | $100/router
|
||||||
|
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
|
||||||
|
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
|
||||||
|
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
|
||||||
|
HST: 13% (Ontario)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VERIFICATION COMMAND
|
||||||
|
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
|
||||||
|
254 tests, zero dependencies. Run after any pricing/engine/render changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CONTEXT MANAGEMENT
|
||||||
|
|
||||||
|
After completing work:
|
||||||
|
- Update `docs/CHECKPOINT.md` with results
|
||||||
|
- If context is heavy, create `docs/STAGE5-SESSION-PROMPT.md` for the next chat
|
||||||
|
- Keep this document chain as the canonical handoff mechanism
|
||||||
193
docs/STAGE5-SESSION-PROMPT.md
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# SVS MSP CALC — STAGE 5 SESSION PROMPT
|
||||||
|
# Post-Feature Sprint — QA, Polish & Next Features
|
||||||
|
# Generated: 2026-03-15
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHERE WE ARE
|
||||||
|
|
||||||
|
**Beta build: COMPLETE.** Sections I–III are production-quality.
|
||||||
|
**Accessibility/performance audit: COMPLETE.** Phase 5 fixes applied.
|
||||||
|
**Code quality pass: COMPLETE.** Phase 6 — hardcoded colors tokenized, CSS deduped, dead code removed.
|
||||||
|
**Test expansion: COMPLETE.** Phase 7 — 88 → 254 tests (pricing integrity, edge cases, export schema, persistence, import round-trip, invariants).
|
||||||
|
**Enhanced Print/PDF: COMPLETE.** Phase 8 — rep name, notes field, validity date, page breaks, CYA "Not Included" section.
|
||||||
|
**Tests:** 254/254 passing.
|
||||||
|
**Sections IV–VI:** Intentionally deferred as placeholders — do not activate.
|
||||||
|
|
||||||
|
### Completed Stages
|
||||||
|
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
|
||||||
|
- **Stage 2** — Beta build (Phases 1–4: bug fixes, visual polish, UX hardening, docs/QA)
|
||||||
|
- **Phase 5** — Accessibility/performance audit (aria-expanded, focus traps, stepper labels, glass scroll fix, mobile sync optimization)
|
||||||
|
- **Phase 5b** — Font Awesome icon fix (inline data URIs for file:// protocol)
|
||||||
|
- **Stage 3 / Phase 6** — Code quality pass (new `--sky` + `--transition-fast` tokens, button CSS dedup, hardcoded colors → color-mix(), dead null-check removed)
|
||||||
|
- **Stage 4 / Phase 7** — Test expansion (88 → 254 tests across 6 categories)
|
||||||
|
- **Stage 4 / Phase 8** — Enhanced Print/PDF (rep name, notes, validity date, page breaks, CYA section, JSON schema v1.1)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## START EVERY SESSION BY READING
|
||||||
|
|
||||||
|
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
|
||||||
|
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
|
||||||
|
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
|
||||||
|
4. This file — session goals and context
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROJECT SNAPSHOT
|
||||||
|
|
||||||
|
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
|
||||||
|
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
|
||||||
|
**Used by:** SVS sales team, live on screen during prospect calls
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
```
|
||||||
|
svsmspcalc/
|
||||||
|
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
|
||||||
|
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
|
||||||
|
├── quote-engine.js # Pure quote math (197 lines)
|
||||||
|
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
|
||||||
|
├── quote-render.js # DOM rendering + nudges (729 lines)
|
||||||
|
├── quote-persistence.js # localStorage save/restore (237 lines)
|
||||||
|
├── quote-export.js # Print/PDF + JSON export (320 lines)
|
||||||
|
├── quote-import.js # JSON quote import (166 lines)
|
||||||
|
├── theme-manager.js # 4-theme switching (121 lines)
|
||||||
|
├── mobile-sync.js # Mobile panel sync (275 lines)
|
||||||
|
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
|
||||||
|
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
|
||||||
|
├── SVS-MSP-Calculator-base.css # Global chrome
|
||||||
|
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
|
||||||
|
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (67KB)
|
||||||
|
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
|
||||||
|
├── SVS-MSP-Calculator-print.css # Print-specific rules
|
||||||
|
├── SVS-MSP-Calculator-light.css # Light theme overrides
|
||||||
|
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
|
||||||
|
├── SVS-MSP-Calculator-70retro.css # 70s Retro theme overrides
|
||||||
|
├── package-prices.csv # Overrideable pricing (31 rows)
|
||||||
|
├── tests/
|
||||||
|
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
|
||||||
|
└── docs/
|
||||||
|
├── CHECKPOINT.md # Build status checkpoint
|
||||||
|
├── MASTER-SESSION-PROMPT.md # Full architecture brief
|
||||||
|
├── QUICK-REF.md # Compact reference
|
||||||
|
├── README.md
|
||||||
|
├── code-verification.md # Known-good baseline
|
||||||
|
├── phase-roadmap.md # Phase status
|
||||||
|
├── quote-rules.md # Business logic rules
|
||||||
|
├── regression-checklist.md # Test procedures
|
||||||
|
├── STAGE3-SESSION-PROMPT.md # Stage 3 prompt
|
||||||
|
├── STAGE4-SESSION-PROMPT.md # Stage 4 prompt
|
||||||
|
└── STAGE5-SESSION-PROMPT.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
### New in Phase 8 (Print/PDF Enhancements)
|
||||||
|
- **`id="repName"`** — text input below client name ("Prepared by"), persisted + exported
|
||||||
|
- **`id="quoteNotes"`** — textarea in sidebar before export buttons, persisted + exported
|
||||||
|
- **Print invoice** now includes: rep name in header/footer, notes section, computed "Valid until" date, page-break-safe tables, split config (included vs. "Not Included" CYA section)
|
||||||
|
- **JSON export schema** bumped to `v1.1` — new `repName` and `quoteNotes` fields
|
||||||
|
- **JSON import** handles new fields (backward-compatible with v1.0)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HARD CONSTRAINTS (NON-NEGOTIABLE)
|
||||||
|
|
||||||
|
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
|
||||||
|
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
|
||||||
|
4. All 4 themes must work after every change
|
||||||
|
5. Mobile parity maintained
|
||||||
|
6. Print/PDF tested after CSS changes
|
||||||
|
7. No frameworks, no npm — vanilla only
|
||||||
|
8. Surgical changes only — read before editing
|
||||||
|
9. Sections IV–VI are placeholders — do not activate or build out
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STAGE 5 GOALS — Choose priorities from this menu:
|
||||||
|
|
||||||
|
### Option A: Browser-Based Manual QA
|
||||||
|
Full visual/functional walkthrough at all breakpoints × 4 themes.
|
||||||
|
Requires user to open the app in a browser and share screenshots or describe issues.
|
||||||
|
|
||||||
|
**Breakpoint matrix:**
|
||||||
|
| Width | Context | Key checks |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| 1800px+ | Wide desktop | Max-width constraint, sidebar positioning |
|
||||||
|
| 1400px | Standard desktop | 3:2 main/sidebar balance |
|
||||||
|
| 1100px | Tablet/narrow | Single column transition, pill appears |
|
||||||
|
| 900px | Small tablet | Tighter spacing, smaller numerals |
|
||||||
|
| 600px | Phone portrait | Stacked layout, no numeral gutter |
|
||||||
|
| 375px | Small phone | MRR pill position, bottom sheet |
|
||||||
|
| 780px landscape | Phone landscape | 2-col restored, sidebar visible |
|
||||||
|
|
||||||
|
**Theme matrix:** Dark (default), Light, Glass, 70s Retro
|
||||||
|
**Total test grid:** 7 breakpoints × 4 themes = 28 combinations
|
||||||
|
|
||||||
|
### Option B: Feature Work
|
||||||
|
- **Sidebar keyboard shortcuts** — Ctrl+P print, Ctrl+E export, Ctrl+R reset, Escape close focus mode
|
||||||
|
- **Additional nudge logic** — new contextual nudges (e.g. "no endpoints but users set", "VoIP seats ≠ user count", high admin-to-MRR ratio)
|
||||||
|
- **Any specific feature requests**
|
||||||
|
|
||||||
|
### Option C: Further Code Quality
|
||||||
|
- Spacing magic numbers → token migration (95+ instances of 14px/16px/20px)
|
||||||
|
- `--transition-fast` token adoption across components.css (remaining 0.15s instances)
|
||||||
|
- CSS selector specificity audit
|
||||||
|
- Print CSS hardening
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHAT WAS DONE IN PHASES 7–8 (for context)
|
||||||
|
|
||||||
|
### Phase 7: Test Expansion
|
||||||
|
| Group | Tests | Coverage |
|
||||||
|
|-------|-------|----------|
|
||||||
|
| Pricing DEFAULTS integrity | 34 | Key existence, types, spec values, frozen, ordering |
|
||||||
|
| Engine edge cases | 55 | Admin thresholds, 100-user scale, string coercion, invalid inputs, VoIP/ZT edge cases |
|
||||||
|
| Export JSON schema | 22 | Payload structure, field types, version, labels, pricing sub-object, new fields |
|
||||||
|
| Persistence state shape | 6 | JSON round-trip, engine compatibility, zero-state |
|
||||||
|
| Import payload mapping | 12 | Term reverse-map, export→import→engine round-trip |
|
||||||
|
| Quote output invariants | 24 | 6 configs × 4 invariants |
|
||||||
|
|
||||||
|
### Phase 8: Enhanced Print/PDF
|
||||||
|
| # | Enhancement | Details |
|
||||||
|
|---|------------|---------|
|
||||||
|
| P1 | Notes field | `<textarea id="quoteNotes">` — sidebar, persisted, exported, printed |
|
||||||
|
| P2 | Validity date | "Valid until [computed date]" in print footer |
|
||||||
|
| P3 | Page breaks | `page-break-inside:avoid` on tables, totals, notes |
|
||||||
|
| P4 | Rep name | `<input id="repName">` — persisted, exported, in print header + footer |
|
||||||
|
| P5 | CYA section | Active features prominent; excluded in muted "Services Not Included in This Quote" section |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PRICING REFERENCE
|
||||||
|
|
||||||
|
```
|
||||||
|
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
|
||||||
|
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
|
||||||
|
Endpoints: $35/ea | USB +$4 | BMB +$25
|
||||||
|
Servers: $120/ea
|
||||||
|
ZT Net: $25/seat | $100/router
|
||||||
|
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
|
||||||
|
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
|
||||||
|
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
|
||||||
|
HST: 13% (Ontario)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VERIFICATION COMMAND
|
||||||
|
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
|
||||||
|
254 tests, zero dependencies. Run after any pricing/engine/render changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CONTEXT MANAGEMENT
|
||||||
|
|
||||||
|
After completing work:
|
||||||
|
- Update `docs/CHECKPOINT.md` with results
|
||||||
|
- If context is heavy, create `docs/STAGE6-SESSION-PROMPT.md` for the next chat
|
||||||
|
- Keep this document chain as the canonical handoff mechanism
|
||||||
162
docs/STAGE6-SESSION-PROMPT.md
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# SVS MSP CALC — STAGE 6 SESSION PROMPT
|
||||||
|
# Post-QA — Feature Work & Remaining Polish
|
||||||
|
# Generated: 2026-03-15
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHERE WE ARE
|
||||||
|
|
||||||
|
**Beta build: COMPLETE.** Sections I–III are production-quality.
|
||||||
|
**Phases 1–8: COMPLETE.** Bug fixes, visual polish, UX hardening, docs/QA, a11y/perf, code quality, test expansion, print enhancements.
|
||||||
|
**Phase 9 / Stage 5: COMPLETE.** Visual QA across 3 breakpoints × 4 themes — Dark/Light/Glass all clean. Retro theme overhauled from muddy brown → warm paper + hot rose/teal cyberpunk accents.
|
||||||
|
**Tests:** 254/254 passing.
|
||||||
|
**Sections IV–VI:** Intentionally deferred as placeholders — do not activate.
|
||||||
|
|
||||||
|
### Completed Stages
|
||||||
|
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
|
||||||
|
- **Stage 2** — Beta build (Phases 1–4: bug fixes, visual polish, UX hardening, docs/QA)
|
||||||
|
- **Phase 5** — Accessibility/performance audit + Font Awesome icon fix
|
||||||
|
- **Stage 3 / Phase 6** — Code quality pass (tokens, CSS dedup, dead code)
|
||||||
|
- **Stage 4 / Phase 7** — Test expansion (88 → 254 tests)
|
||||||
|
- **Stage 4 / Phase 8** — Enhanced Print/PDF (rep name, notes, validity date, page breaks, CYA section)
|
||||||
|
- **Stage 5 / Phase 9** — Visual QA (3 breakpoints × 4 themes) + Retro theme overhaul
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## START EVERY SESSION BY READING
|
||||||
|
|
||||||
|
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
|
||||||
|
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
|
||||||
|
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
|
||||||
|
4. This file — session goals and context
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROJECT SNAPSHOT
|
||||||
|
|
||||||
|
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
|
||||||
|
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
|
||||||
|
**Used by:** SVS sales team, live on screen during prospect calls
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
```
|
||||||
|
svsmspcalc/
|
||||||
|
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
|
||||||
|
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
|
||||||
|
├── quote-engine.js # Pure quote math (197 lines)
|
||||||
|
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
|
||||||
|
├── quote-render.js # DOM rendering + nudges (729 lines)
|
||||||
|
├── quote-persistence.js # localStorage save/restore (237 lines)
|
||||||
|
├── quote-export.js # Print/PDF + JSON export (320 lines)
|
||||||
|
├── quote-import.js # JSON quote import (166 lines)
|
||||||
|
├── theme-manager.js # 4-theme switching (121 lines)
|
||||||
|
├── mobile-sync.js # Mobile panel sync (275 lines)
|
||||||
|
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
|
||||||
|
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
|
||||||
|
├── SVS-MSP-Calculator-base.css # Global chrome
|
||||||
|
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
|
||||||
|
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (67KB)
|
||||||
|
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
|
||||||
|
├── SVS-MSP-Calculator-print.css # Print-specific rules
|
||||||
|
├── SVS-MSP-Calculator-light.css # Light theme overrides
|
||||||
|
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
|
||||||
|
├── SVS-MSP-Calculator-70retro.css # Retro Cyberpunk theme (paper + hot rose/teal)
|
||||||
|
├── package-prices.csv # Overrideable pricing (31 rows)
|
||||||
|
├── tests/
|
||||||
|
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
|
||||||
|
└── docs/
|
||||||
|
├── CHECKPOINT.md # Build status checkpoint
|
||||||
|
├── MASTER-SESSION-PROMPT.md # Full architecture brief
|
||||||
|
├── QUICK-REF.md # Compact reference
|
||||||
|
├── README.md
|
||||||
|
├── code-verification.md # Known-good baseline
|
||||||
|
├── phase-roadmap.md # Phase status
|
||||||
|
├── quote-rules.md # Business logic rules
|
||||||
|
├── regression-checklist.md # Test procedures
|
||||||
|
└── STAGE6-SESSION-PROMPT.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Retro Theme Status (Phase 9)
|
||||||
|
- Overhauled from 70s wood-panel brown → warm paper + cyberpunk neon-warm accents
|
||||||
|
- Accent: hot rose `#e11d48` | Teal: `#0d9488` | Header: warm charcoal `#1c1317`
|
||||||
|
- Logo SVG fix: `.top-bar-logo path { fill: #f0e4d0 }` overrides hardcoded black
|
||||||
|
- **User noted:** full design polish deferred — current version is functional placeholder
|
||||||
|
- Retro theme not yet QA'd at all 7 breakpoints
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HARD CONSTRAINTS (NON-NEGOTIABLE)
|
||||||
|
|
||||||
|
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
|
||||||
|
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
|
||||||
|
4. All 4 themes must work after every change
|
||||||
|
5. Mobile parity maintained
|
||||||
|
6. Print/PDF tested after CSS changes
|
||||||
|
7. No frameworks, no npm — vanilla only
|
||||||
|
8. Surgical changes only — read before editing
|
||||||
|
9. Sections IV–VI are placeholders — do not activate or build out
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STAGE 6 GOALS — Choose priorities from this menu:
|
||||||
|
|
||||||
|
### Option A: Remaining Visual QA
|
||||||
|
Complete the breakpoint matrix for retro theme + remaining breakpoints:
|
||||||
|
| Width | Context |
|
||||||
|
|-------|---------|
|
||||||
|
| 900px | Small tablet |
|
||||||
|
| 600px | Phone portrait |
|
||||||
|
| 375px | Small phone |
|
||||||
|
| 780px landscape | Phone landscape |
|
||||||
|
|
||||||
|
### Option B: Feature Work
|
||||||
|
- **Sidebar keyboard shortcuts** — Ctrl+P print, Ctrl+E export, Ctrl+R reset, Escape close focus mode
|
||||||
|
- **Additional nudge logic** — new contextual nudges (e.g. "no endpoints but users set", "VoIP seats ≠ user count", high admin-to-MRR ratio)
|
||||||
|
- **Any specific feature requests from the user**
|
||||||
|
|
||||||
|
### Option C: Further Code Quality
|
||||||
|
- Spacing magic numbers → token migration (95+ instances of 14px/16px/20px)
|
||||||
|
- `--transition-fast` token adoption across components.css
|
||||||
|
- CSS selector specificity audit
|
||||||
|
- Print CSS hardening
|
||||||
|
|
||||||
|
### Option D: Retro Theme Full Design Pass
|
||||||
|
- Complete cyberpunk aesthetic overhaul with user collaboration
|
||||||
|
- Color refinement, contrast tuning, component-level styling
|
||||||
|
- Full breakpoint QA after design is finalized
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PRICING REFERENCE
|
||||||
|
|
||||||
|
```
|
||||||
|
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
|
||||||
|
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
|
||||||
|
Endpoints: $35/ea | USB +$4 | BMB +$25
|
||||||
|
Servers: $120/ea
|
||||||
|
ZT Net: $25/seat | $100/router
|
||||||
|
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
|
||||||
|
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
|
||||||
|
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
|
||||||
|
HST: 13% (Ontario)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VERIFICATION COMMAND
|
||||||
|
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
|
||||||
|
254 tests, zero dependencies. Run after any pricing/engine/render changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CONTEXT MANAGEMENT
|
||||||
|
|
||||||
|
After completing work:
|
||||||
|
- Update `docs/CHECKPOINT.md` with results
|
||||||
|
- If context is heavy, create `docs/STAGE7-SESSION-PROMPT.md` for the next chat
|
||||||
|
- Keep this document chain as the canonical handoff mechanism
|
||||||
166
docs/STAGE7-SESSION-PROMPT.md
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
# SVS MSP CALC — STAGE 7 SESSION PROMPT
|
||||||
|
# Post-Elastic — Feature Work & Polish
|
||||||
|
# Generated: 2026-03-15
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHERE WE ARE
|
||||||
|
|
||||||
|
**Beta build: COMPLETE.** Sections I–III are production-quality.
|
||||||
|
**Phases 1–9: COMPLETE.** Bug fixes, visual polish, UX hardening, docs/QA, a11y/perf, code quality, test expansion, print enhancements, visual QA.
|
||||||
|
**Phase 10 / Stage 6: COMPLETE.** Elastic responsive foundation — fluid `clamp()` tokens replace 5 fixed breakpoints with 3 structural ones. Max width scales to 2400px for large monitors.
|
||||||
|
**Tests:** 254/254 passing.
|
||||||
|
**Sections IV–VI:** Intentionally deferred as placeholders — do not activate.
|
||||||
|
|
||||||
|
### Completed Stages
|
||||||
|
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
|
||||||
|
- **Stage 2** — Beta build (Phases 1–4: bug fixes, visual polish, UX hardening, docs/QA)
|
||||||
|
- **Phase 5** — Accessibility/performance audit + Font Awesome icon fix
|
||||||
|
- **Stage 3 / Phase 6** — Code quality pass (tokens, CSS dedup, dead code)
|
||||||
|
- **Stage 4 / Phase 7** — Test expansion (88 → 254 tests)
|
||||||
|
- **Stage 4 / Phase 8** — Enhanced Print/PDF (rep name, notes, validity date, page breaks, CYA section)
|
||||||
|
- **Stage 5 / Phase 9** — Visual QA (3 breakpoints × 4 themes) + Retro theme overhaul
|
||||||
|
- **Stage 6 / Phase 10** — Elastic responsive foundation (5 → 3 breakpoints, fluid clamp() tokens, max-width up to 2400px)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## START EVERY SESSION BY READING
|
||||||
|
|
||||||
|
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
|
||||||
|
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
|
||||||
|
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
|
||||||
|
4. This file — session goals and context
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROJECT SNAPSHOT
|
||||||
|
|
||||||
|
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
|
||||||
|
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
|
||||||
|
**Used by:** SVS sales team, live on screen during prospect calls
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
```
|
||||||
|
svsmspcalc/
|
||||||
|
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
|
||||||
|
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
|
||||||
|
├── quote-engine.js # Pure quote math (197 lines)
|
||||||
|
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
|
||||||
|
├── quote-render.js # DOM rendering + nudges (729 lines)
|
||||||
|
├── quote-persistence.js # localStorage save/restore (237 lines)
|
||||||
|
├── quote-export.js # Print/PDF + JSON export (320 lines)
|
||||||
|
├── quote-import.js # JSON quote import (166 lines)
|
||||||
|
├── theme-manager.js # 4-theme switching (121 lines)
|
||||||
|
├── mobile-sync.js # Mobile panel sync (275 lines)
|
||||||
|
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
|
||||||
|
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars (fluid clamp() layout)
|
||||||
|
├── SVS-MSP-Calculator-base.css # Global chrome
|
||||||
|
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
|
||||||
|
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (67KB)
|
||||||
|
├── SVS-MSP-Calculator-responsive.css # 3 structural breakpoints (1100/600/780L)
|
||||||
|
├── SVS-MSP-Calculator-print.css # Print-specific rules
|
||||||
|
├── SVS-MSP-Calculator-light.css # Light theme overrides
|
||||||
|
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
|
||||||
|
├── SVS-MSP-Calculator-70retro.css # Retro Cyberpunk theme (paper + hot rose/teal)
|
||||||
|
├── package-prices.csv # Overrideable pricing (31 rows)
|
||||||
|
├── tests/
|
||||||
|
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
|
||||||
|
└── docs/
|
||||||
|
├── CHECKPOINT.md # Build status checkpoint
|
||||||
|
├── MASTER-SESSION-PROMPT.md # Full architecture brief
|
||||||
|
├── QUICK-REF.md # Compact reference
|
||||||
|
├── README.md
|
||||||
|
├── code-verification.md # Known-good baseline
|
||||||
|
├── phase-roadmap.md # Phase status
|
||||||
|
├── quote-rules.md # Business logic rules
|
||||||
|
├── regression-checklist.md # Test procedures
|
||||||
|
└── STAGE7-SESSION-PROMPT.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Responsive Architecture (Phase 10)
|
||||||
|
- **Fluid tokens** in `tokens.css` — `clamp()` drives all spacing/sizing continuously
|
||||||
|
- **3 structural breakpoints** in `responsive.css`:
|
||||||
|
- `≤1100px` — 2-col grid → 1-col, sidebar → mobile pill/panel
|
||||||
|
- `≤600px` — phone layout shifts (stacking, gutter collapse, touch targets)
|
||||||
|
- `≤780px landscape` — restore 2-col with sticky sidebar
|
||||||
|
- **Width scaling:** 1080p → ~1766px, 1440p → ~2355px, 4K → 2400px cap
|
||||||
|
- No more 1350px or 900px breakpoints — fluid tokens handle the range
|
||||||
|
|
||||||
|
### Retro Theme Status
|
||||||
|
- Overhauled from 70s wood-panel brown → warm paper + cyberpunk neon-warm accents
|
||||||
|
- Accent: hot rose `#e11d48` | Teal: `#0d9488` | Header: warm charcoal `#1c1317`
|
||||||
|
- **User noted:** full design polish deferred — current version is functional placeholder
|
||||||
|
- Retro theme not yet QA'd across all viewport widths
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HARD CONSTRAINTS (NON-NEGOTIABLE)
|
||||||
|
|
||||||
|
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
|
||||||
|
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
|
||||||
|
4. All 4 themes must work after every change
|
||||||
|
5. Mobile parity maintained
|
||||||
|
6. Print/PDF tested after CSS changes
|
||||||
|
7. No frameworks, no npm — vanilla only
|
||||||
|
8. Surgical changes only — read before editing
|
||||||
|
9. Sections IV–VI are placeholders — do not activate or build out
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STAGE 7 GOALS — Choose priorities from this menu:
|
||||||
|
|
||||||
|
### Option A: Feature Work
|
||||||
|
- **Sidebar keyboard shortcuts** — Ctrl+P print, Ctrl+E export, Ctrl+R reset, Escape close focus mode
|
||||||
|
- **Additional nudge logic** — new contextual nudges (e.g. "no endpoints but users set", "VoIP seats ≠ user count", high admin-to-MRR ratio)
|
||||||
|
- **Any specific feature requests from the user**
|
||||||
|
|
||||||
|
### Option B: Further Code Quality
|
||||||
|
- `--transition-fast` token adoption across components.css
|
||||||
|
- CSS selector specificity audit
|
||||||
|
- Print CSS hardening
|
||||||
|
|
||||||
|
### Option C: Retro Theme Full Design Pass
|
||||||
|
- Complete cyberpunk aesthetic overhaul with user collaboration
|
||||||
|
- Color refinement, contrast tuning, component-level styling
|
||||||
|
- Full viewport QA after design is finalized
|
||||||
|
|
||||||
|
### Option D: Visual QA Sweep
|
||||||
|
- Verify elastic responsive behavior across viewport range (resize from 375px → 2560px)
|
||||||
|
- Confirm all 4 themes render cleanly with new fluid tokens
|
||||||
|
- Test landscape orientation on tablet/phone sizes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PRICING REFERENCE
|
||||||
|
|
||||||
|
```
|
||||||
|
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
|
||||||
|
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
|
||||||
|
Endpoints: $35/ea | USB +$4 | BMB +$25
|
||||||
|
Servers: $120/ea
|
||||||
|
ZT Net: $25/seat | $100/router
|
||||||
|
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
|
||||||
|
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
|
||||||
|
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
|
||||||
|
HST: 13% (Ontario)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VERIFICATION COMMAND
|
||||||
|
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
|
||||||
|
254 tests, zero dependencies. Run after any pricing/engine/render changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CONTEXT MANAGEMENT
|
||||||
|
|
||||||
|
After completing work:
|
||||||
|
- Update `docs/CHECKPOINT.md` with results
|
||||||
|
- If context is heavy, create `docs/STAGE8-SESSION-PROMPT.md` for the next chat
|
||||||
|
- Keep this document chain as the canonical handoff mechanism
|
||||||
167
docs/STAGE9-SESSION-PROMPT.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# SVS MSP CALC — STAGE 9 SESSION PROMPT
|
||||||
|
# Next Session
|
||||||
|
# Generated: 2026-03-15
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHERE WE ARE
|
||||||
|
|
||||||
|
**Beta build: COMPLETE.** Sections I–III are production-quality.
|
||||||
|
**Phases 1–11 + Stage 8: COMPLETE.** Bug fixes, visual polish, UX hardening, docs/QA, a11y/perf, code quality I & II, test expansion, print enhancements, visual QA, elastic responsive, feature work.
|
||||||
|
**Stage 8 Feature Fixes: COMPLETE.**
|
||||||
|
- Fullscreen live quote view: Print button in sidebar header (Reset/Import/Export JSON hidden)
|
||||||
|
- Toggle switches: distinct off/on colors per theme (`--surface-switch-off`/`--surface-switch-on`)
|
||||||
|
- Pricing: migrated from CSV to JSON (`package-prices.json`)
|
||||||
|
- JSON export/import: already captures all form data including quote notes (schema v1.1)
|
||||||
|
**Tests:** 254/254 passing.
|
||||||
|
**Sections IV–VI:** Intentionally deferred as placeholders — do not activate.
|
||||||
|
|
||||||
|
### Completed Stages
|
||||||
|
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
|
||||||
|
- **Stage 2** — Beta build (Phases 1–4: bug fixes, visual polish, UX hardening, docs/QA)
|
||||||
|
- **Phase 5** — Accessibility/performance audit + Font Awesome icon fix
|
||||||
|
- **Stage 3 / Phase 6** — Code quality pass I (tokens, CSS dedup, dead code)
|
||||||
|
- **Stage 4 / Phase 7** — Test expansion (88 → 254 tests)
|
||||||
|
- **Stage 4 / Phase 8** — Enhanced Print/PDF (rep name, notes, validity date, page breaks, CYA section)
|
||||||
|
- **Stage 5 / Phase 9** — Visual QA (3 breakpoints × 4 themes) + Retro theme overhaul
|
||||||
|
- **Stage 6 / Phase 10** — Elastic responsive foundation (5 → 3 breakpoints, fluid clamp() tokens, max-width up to 2400px)
|
||||||
|
- **Stage 7 / Phase 11** — Feature work: keyboard shortcuts (Ctrl+P/E/R) + 4 new contextual nudges
|
||||||
|
- **Stage 8 / Phase 12** — Code quality pass II: transition tokens, CSS specificity audit, print CSS hardening
|
||||||
|
- **Stage 8 Feature Fixes** — Fullscreen print button fix, toggle switch 2-state themed colors, pricing CSV → JSON migration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## START EVERY SESSION BY READING
|
||||||
|
|
||||||
|
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
|
||||||
|
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
|
||||||
|
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
|
||||||
|
4. This file — session goals and context
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROJECT SNAPSHOT
|
||||||
|
|
||||||
|
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
|
||||||
|
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
|
||||||
|
**Used by:** SVS sales team, live on screen during prospect calls
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
```
|
||||||
|
svsmspcalc/
|
||||||
|
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
|
||||||
|
├── SVS-MSP-Calculator.js # Orchestration + keyboard shortcuts (375 lines)
|
||||||
|
├── quote-engine.js # Pure quote math (197 lines)
|
||||||
|
├── quote-pricing.js # Pricing defaults + JSON override (134 lines)
|
||||||
|
├── quote-render.js # DOM rendering + nudges (760 lines)
|
||||||
|
├── quote-persistence.js # localStorage save/restore (237 lines)
|
||||||
|
├── quote-export.js # Print/PDF + JSON export (320 lines)
|
||||||
|
├── quote-import.js # JSON quote import (166 lines)
|
||||||
|
├── theme-manager.js # 4-theme switching (121 lines)
|
||||||
|
├── mobile-sync.js # Mobile panel sync (275 lines)
|
||||||
|
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
|
||||||
|
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars (fluid clamp() layout)
|
||||||
|
├── SVS-MSP-Calculator-base.css # Global chrome
|
||||||
|
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
|
||||||
|
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (67KB)
|
||||||
|
├── SVS-MSP-Calculator-responsive.css # 3 structural breakpoints (1100/600/780L)
|
||||||
|
├── SVS-MSP-Calculator-print.css # Print-specific rules
|
||||||
|
├── SVS-MSP-Calculator-light.css # Light theme overrides
|
||||||
|
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
|
||||||
|
├── SVS-MSP-Calculator-70retro.css # Retro Cyberpunk theme (paper + hot rose/teal)
|
||||||
|
├── package-prices-data.js # Pricing data (edit this to change prices — loaded via <script>)
|
||||||
|
├── package-prices.json # Pricing reference (JSON format, not loaded at runtime)
|
||||||
|
├── package-prices.csv # Legacy pricing reference (not loaded at runtime)
|
||||||
|
├── tests/
|
||||||
|
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
|
||||||
|
└── docs/
|
||||||
|
├── CHECKPOINT.md # Build status checkpoint
|
||||||
|
├── MASTER-SESSION-PROMPT.md # Full architecture brief
|
||||||
|
├── QUICK-REF.md # Compact reference
|
||||||
|
├── README.md
|
||||||
|
├── code-verification.md # Known-good baseline
|
||||||
|
├── phase-roadmap.md # Phase status
|
||||||
|
├── quote-rules.md # Business logic rules
|
||||||
|
├── regression-checklist.md # Test procedures
|
||||||
|
├── STAGE8-SESSION-PROMPT.md # Previous stage
|
||||||
|
└── STAGE9-SESSION-PROMPT.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pricing Configuration
|
||||||
|
|
||||||
|
Pricing is loaded from `package-prices-data.js` via `<script>` tag (works on `file://` — no web server needed). The loader also has a `fetch()` fallback for web server environments, then falls back to built-in defaults.
|
||||||
|
|
||||||
|
**To update pricing:** Edit `package-prices-data.js` — change the `value` field for any key:
|
||||||
|
```js
|
||||||
|
window.SVS_PRICING_DATA = {
|
||||||
|
"user_packages": {
|
||||||
|
"RATE_M365": { "value": 130, "description": "..." },
|
||||||
|
...
|
||||||
|
},
|
||||||
|
...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Categories: `user_packages`, `user_addons`, `endpoints`, `endpoint_addons`, `zero_trust_network`, `voip`, `site_admin`, `contract_discounts`, `tax`, `vs_comparison`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HARD CONSTRAINTS (NON-NEGOTIABLE)
|
||||||
|
|
||||||
|
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
|
||||||
|
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
|
||||||
|
4. All 4 themes must work after every change
|
||||||
|
5. Mobile parity maintained
|
||||||
|
6. Print/PDF tested after CSS changes
|
||||||
|
7. No frameworks, no npm — vanilla only
|
||||||
|
8. Surgical changes only — read before editing
|
||||||
|
9. Sections IV–VI are placeholders — do not activate or build out
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STAGE 9 GOALS
|
||||||
|
|
||||||
|
_To be determined by the user in the next session._
|
||||||
|
|
||||||
|
Potential areas for continued work:
|
||||||
|
- **Spacing token consolidation** — 150+ magic-number spacings in components.css (deferred from Stage 8)
|
||||||
|
- **Visual QA pass** — Retro theme at all viewport widths + landscape (noted as incomplete in Stage 5)
|
||||||
|
- **Additional feature work** — user-driven
|
||||||
|
- **Test coverage** — additional edge cases or integration-level tests
|
||||||
|
- **Documentation updates** — README, MASTER-SESSION-PROMPT alignment
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PRICING REFERENCE
|
||||||
|
|
||||||
|
```
|
||||||
|
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
|
||||||
|
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
|
||||||
|
Endpoints: $35/ea | USB +$4 | BMB +$25
|
||||||
|
Servers: $120/ea
|
||||||
|
ZT Net: $25/seat | $100/router
|
||||||
|
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
|
||||||
|
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
|
||||||
|
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
|
||||||
|
HST: 13% (Ontario)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VERIFICATION COMMAND
|
||||||
|
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
|
||||||
|
254 tests, zero dependencies. Run after any pricing/engine/render changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CONTEXT MANAGEMENT
|
||||||
|
|
||||||
|
After completing work:
|
||||||
|
- Update `docs/CHECKPOINT.md` with results
|
||||||
|
- If context is heavy, create `docs/STAGE10-SESSION-PROMPT.md` for the next chat
|
||||||
|
- Keep this document chain as the canonical handoff mechanism
|
||||||
65
docs/ai-session-brief.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# AI Session Brief
|
||||||
|
|
||||||
|
## Mission
|
||||||
|
|
||||||
|
Operate like a senior engineering, UX/UI, QA, and sales-enablement team in one:
|
||||||
|
|
||||||
|
- Senior frontend engineer: minimal safe changes, clean code, strong regression awareness
|
||||||
|
- UX/UI lead: polish, readability, hierarchy, responsive quality, accessibility, and visual restraint
|
||||||
|
- Marketing manager: keep messaging clear, persuasive, professional, and sales-usable
|
||||||
|
|
||||||
|
## Post-startup reminder
|
||||||
|
|
||||||
|
After reading docs, always remind the user:
|
||||||
|
|
||||||
|
> You have an automated quote engine test suite (88 tests). Run anytime:
|
||||||
|
> `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
> I can run it for you, or modify it to add new test scenarios.
|
||||||
|
|
||||||
|
## Non-negotiables
|
||||||
|
|
||||||
|
- Inspect existing code before editing
|
||||||
|
- Prefer the smallest safe change
|
||||||
|
- Preserve current behavior unless explicitly asked to change it
|
||||||
|
- Keep the HTML shell and DOM IDs stable unless approved
|
||||||
|
- Preserve calculations, exports, persistence, theme switching, and mobile parity
|
||||||
|
- Avoid broad rewrites, framework changes, or "cleanup for its own sake"
|
||||||
|
- Minimize token waste
|
||||||
|
- Read only the files needed for the task
|
||||||
|
- Treat code quality as production-grade: clear intent, low duplication, safe edge handling, and no casual hacks
|
||||||
|
- Always check for regressions, syntax issues, broken flows, and stale code before calling work done
|
||||||
|
- Run `node svsmspcalc/tests/test-quote-engine.js` after any pricing or engine changes
|
||||||
|
|
||||||
|
## Product bar
|
||||||
|
|
||||||
|
- Sales-facing and professional
|
||||||
|
- Clear, persuasive, and polished rather than flashy
|
||||||
|
- Responsive behavior should feel elastic and harmonious, not breakpoint-fragile
|
||||||
|
- Prefer tokenized or container-based layout fixes over piling on viewport hacks
|
||||||
|
- Light mode should feel comfortable, soft khaki/brown, and high-readability
|
||||||
|
- UI changes should improve both visual quality and sales clarity
|
||||||
|
- Copy should sound confident, concise, and client-facing
|
||||||
|
|
||||||
|
## Current scope guardrails
|
||||||
|
|
||||||
|
- All six sections (I-VI) are structurally active with unified `sec-controls-row` headers
|
||||||
|
- Sections default to collapsed on load; inner collapsibles default to collapsed
|
||||||
|
- Keep docs concise; only append notes that materially speed up future resume work
|
||||||
|
|
||||||
|
## Required validation mindset
|
||||||
|
|
||||||
|
- Run automated tests after pricing/engine changes
|
||||||
|
- Run syntax checks after JS edits
|
||||||
|
- Review for stale references, dead code, and duplicated logic
|
||||||
|
- Assume inline handlers and mobile clone sync can hide dependencies; verify before deleting code
|
||||||
|
- Call out any unverified areas, especially print/PDF, JSON export, persistence, and mobile parity
|
||||||
|
|
||||||
|
## Resume order
|
||||||
|
|
||||||
|
1. Read `docs/README.md`
|
||||||
|
2. Read `docs/phase-roadmap.md`
|
||||||
|
3. Read `docs/code-verification.md`
|
||||||
|
4. Use `docs/quote-rules.md` only if business-rule detail is needed
|
||||||
|
5. Use `docs/regression-checklist.md` only when validating behavior
|
||||||
|
6. Then inspect only the code files relevant to the request
|
||||||
|
7. **Remind user about the test suite** (see Post-startup reminder above)
|
||||||
124
docs/code-verification.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# Code Verification
|
||||||
|
|
||||||
|
## Latest checkpoint
|
||||||
|
|
||||||
|
- Date: March 15, 2026
|
||||||
|
- Verification: automated quote engine tests (88/88 passing) + visual review
|
||||||
|
- Status: Phases 1–3 complete (bug fixes, visual polish, UX hardening)
|
||||||
|
|
||||||
|
## Automated test suite
|
||||||
|
|
||||||
|
**Location:** `svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
**Run:** `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
**Result:** 88 tests passing across 21 test groups
|
||||||
|
|
||||||
|
### What's covered
|
||||||
|
|
||||||
|
| Area | Tests |
|
||||||
|
|------|-------|
|
||||||
|
| M365 vs BYOL rates | User base rates, rate switching |
|
||||||
|
| Admin fee scaling | Floor ($150), minimum ($650), ZT premium ($250), 1Password markup (10%) |
|
||||||
|
| Admin waiver | Fee calculated but excluded from MRR |
|
||||||
|
| User add-ons | Extended Hours, 1Password, INKY, Zero Trust per-user |
|
||||||
|
| Endpoint add-ons | USB Blocking, Bare Metal Backup |
|
||||||
|
| Server pricing | Per-server rate, inclusion in subtotals |
|
||||||
|
| Contract discounts | m2m (0%), 12mo (3%), 24mo (5%) |
|
||||||
|
| HST | 13% applied after discount, disabled = 0, combo with discount |
|
||||||
|
| VoIP | All 3 tiers, phone + fax add-ons |
|
||||||
|
| Zero Trust networking | Seats + routers |
|
||||||
|
| Edge cases | Zero users, per-user breakdowns |
|
||||||
|
| MRR integrity | MRR = sum of components across 5 different configs |
|
||||||
|
| Realistic scenario | Full 22-user quote with all features |
|
||||||
|
|
||||||
|
### How to extend
|
||||||
|
|
||||||
|
- Add `describe()` / `it()` blocks in the test file
|
||||||
|
- Uses `eq(actual, expected, label)` for exact assertions
|
||||||
|
- Uses `near(actual, expected, tolerance, label)` for floating-point
|
||||||
|
- Ask Claude to add tests for new scenarios
|
||||||
|
|
||||||
|
## What is currently known-good
|
||||||
|
|
||||||
|
### Section header redesign (completed this session)
|
||||||
|
- All 6 sections use unified `sec-controls-row` pattern (stepper + badge + price on row 2)
|
||||||
|
- Subtitles moved to row 3, visible only when expanded
|
||||||
|
- Section headers use CSS grid: numeral + title + chevron on row 1
|
||||||
|
- Container query at ≤520px and media query at ≤600px handle small-screen stacking
|
||||||
|
- Cascade order issues resolved (container query overrides placed after base rules)
|
||||||
|
|
||||||
|
### UI/UX changes (completed this session)
|
||||||
|
- ~15-20% spacing optimization across all tokens, layout, components, and responsive CSS
|
||||||
|
- All sections default to collapsed state on load
|
||||||
|
- Inner collapsibles (What's Included, Add-Ons) default to collapsed
|
||||||
|
- "What This Fee Supports" card removed from SEC-01
|
||||||
|
- "Protection First, Recovery Optional" card removed from SEC-03
|
||||||
|
- Progress bar always visible when SEC-01 is collapsed
|
||||||
|
- Floor note restyled to match title font (DM Mono, 12px, uppercase)
|
||||||
|
- Summary badges show price only (no counts — stepper already shows quantity)
|
||||||
|
|
||||||
|
### Print/export changes
|
||||||
|
- Print now respects user's HST toggle (Phase 1 fix — no longer forces HST on)
|
||||||
|
- JSON export includes `version: '1.0'` schema field (Phase 1 addition)
|
||||||
|
- Print CSS hides `sec-controls-row` instead of old `sec-collapsed-counter`
|
||||||
|
|
||||||
|
### Dead code removed
|
||||||
|
- `sec-collapsed-counter` CSS and HTML (replaced by `sec-controls-row`)
|
||||||
|
- `floorProgress` span (right-side threshold text)
|
||||||
|
|
||||||
|
## Runtime areas that must stay intact
|
||||||
|
|
||||||
|
- Quote calculations (now verified by automated tests)
|
||||||
|
- Exports
|
||||||
|
- Local persistence
|
||||||
|
- Theme switching
|
||||||
|
- Mobile quote sync/parity
|
||||||
|
|
||||||
|
### Phase 1 bug fixes (verified)
|
||||||
|
- ADDON_INKY default: $5 → $8 in quote-pricing.js (test expectations updated)
|
||||||
|
- Onboarding fee manual override preserved across term switches (data-manual-value attribute)
|
||||||
|
- VoIP fax CSV comment: "Flat/mo" → "Per seat/mo"
|
||||||
|
- Print HST: now respects user toggle instead of forcing HST on
|
||||||
|
- JSON export: schema version '1.0' added
|
||||||
|
- ZT admin supplement: amber nudge warning when ztActive
|
||||||
|
|
||||||
|
### Phase 2 visual polish (verified)
|
||||||
|
- Hardcoded danger icon color → `var(--text-danger)`
|
||||||
|
- Hardcoded pill-savings active color → `var(--text-pill-savings-active)` (new token in all 4 themes)
|
||||||
|
- All 4 themes fully token-covered for Sections I–III
|
||||||
|
- No remaining hardcoded colors in active section CSS
|
||||||
|
|
||||||
|
### Phase 3 UX hardening (verified)
|
||||||
|
- Smooth theme-switch transition (0.25s crossfade via `.theme-transitioning`)
|
||||||
|
- Nudge crossfade on rotation/nav (180ms fade-out → swap → fade-in)
|
||||||
|
- Summary badge fade-in on collapse (0.25s animation)
|
||||||
|
- Addon toggle micro-feedback (0.2s scale pulse)
|
||||||
|
- Touch targets ≥44px on mobile (close btn, nudge nav, section toggles)
|
||||||
|
- Mobile focus trap in panel (Tab cycles within, focus returns to pill on close)
|
||||||
|
- Safe-area insets for notch phones (pill position, panel padding)
|
||||||
|
|
||||||
|
### Phase 5 accessibility & performance fixes (verified)
|
||||||
|
- `aria-expanded` on all section toggles and collapsible headers (12 elements)
|
||||||
|
- Focus trap on reset confirm modal (Tab cycles within modal)
|
||||||
|
- `aria-label` on all stepper buttons (12 elements: "Decrease/Increase users/endpoints/etc.")
|
||||||
|
- Glass theme `background-attachment: scroll` on mobile (≤1100px) to prevent iOS scroll jank
|
||||||
|
- Mobile sync guard: skips 35+ element sync when panel closed on desktop
|
||||||
|
- Mobile sync gaps fixed: `sidebarFocusClientName`, `sl-discount-detail`, `sl-value-onboarding-label` added to sync map
|
||||||
|
- Full sync forced on `openMobilePanel()` to ensure fresh data
|
||||||
|
|
||||||
|
### Font Awesome icon fix (verified)
|
||||||
|
- All 36 FA Sharp Solid icon references in components.css converted from file paths to inline `data:image/svg+xml` URIs
|
||||||
|
- Icons now render on `file://` protocol without a server
|
||||||
|
- If adding new icons: use inline data URI format, not file path references
|
||||||
|
|
||||||
|
## What still needs caution
|
||||||
|
|
||||||
|
- DOM rendering tests (sidebar values, warnings, mobile sync) are not yet automated
|
||||||
|
- Export/persistence tests are not yet automated
|
||||||
|
|
||||||
|
## Required check standard for future edits
|
||||||
|
|
||||||
|
- After pricing/engine edits: run `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
- After JS edits: run syntax checks
|
||||||
|
- After logic edits: review quote math, persistence, export, and mobile parity impact
|
||||||
|
- After sidebar changes: verify the mobile clone/sync map still covers the changed elements
|
||||||
|
- After copy or styling edits in sales-critical areas: check readability and messaging
|
||||||
50
docs/phase-roadmap.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Phase Roadmap
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
All phases complete. Project is in user-driven stage work.
|
||||||
|
|
||||||
|
| Stage | Phase | Work | Status |
|
||||||
|
|-------|-------|------|--------|
|
||||||
|
| 1 | 0 | Discovery audit, codebase mapping, doc generation | Complete |
|
||||||
|
| 2 | 1 | Bug fixes (6 issues: INKY $8, onboarding, VoIP CSV, print HST, JSON, ZT nudge) | Complete |
|
||||||
|
| 2 | 2 | Visual polish (hardcoded colors → tokens, 4 themes audited) | Complete |
|
||||||
|
| 2 | 3 | UX hardening (transitions, touch targets, focus traps, safe-area) | Complete |
|
||||||
|
| 2 | 4 | Documentation & QA (doc sync, regression walkthrough, beta DOD) | Complete |
|
||||||
|
| — | 5 | Accessibility/performance audit + Font Awesome icon fix | Complete |
|
||||||
|
| 3 | 6 | Code quality I (tokens, CSS dedup, dead code) | Complete |
|
||||||
|
| 4 | 7 | Test expansion (88 → 254 tests) | Complete |
|
||||||
|
| 4 | 8 | Enhanced Print/PDF (rep name, notes, validity, page breaks, CYA) | Complete |
|
||||||
|
| 5 | 9 | Visual QA (3 breakpoints × 4 themes) + Retro theme overhaul | Complete |
|
||||||
|
| 6 | 10 | Elastic responsive (5 → 3 breakpoints, fluid clamp() tokens) | Complete |
|
||||||
|
| 7 | 11 | Feature work: keyboard shortcuts (Ctrl+P/E/R) + 4 nudges | Complete |
|
||||||
|
| 8 | 12 | Code quality II: transition tokens, specificity, print CSS | Complete |
|
||||||
|
| 8 | — | Feature fixes: fullscreen print, toggle colors, CSV→JSON pricing | Complete |
|
||||||
|
| 9 | — | Spacing token consolidation, pricing label sync, Retro visual QA | Complete |
|
||||||
|
|
||||||
|
**Tests:** 254/254 passing. `node svsmspcalc/tests/test-quote-engine.js`
|
||||||
|
|
||||||
|
## Architecture baseline
|
||||||
|
|
||||||
|
- JS split into pricing, engine, render, persistence, export, import, theme, and mobile-sync modules
|
||||||
|
- CSS split into tokens, base, layout, components, responsive, print, and 3 theme override files
|
||||||
|
- HTML shell kept stable during the split
|
||||||
|
- Pricing loads from `package-prices-data.js` via `<script>` tag (no web server needed)
|
||||||
|
- Automated test suite at `tests/test-quote-engine.js` (254 tests, zero deps)
|
||||||
|
|
||||||
|
## Approved work
|
||||||
|
|
||||||
|
- Bug fixes
|
||||||
|
- CSS/UI polish, theme improvements
|
||||||
|
- Responsive cleanup and hardening
|
||||||
|
- Spacing, alignment, color consistency fixes
|
||||||
|
- Documentation cleanup
|
||||||
|
- Test coverage expansion
|
||||||
|
- Small dead-code cleanup (after usage verification)
|
||||||
|
|
||||||
|
## Do not do without explicit approval
|
||||||
|
|
||||||
|
- Broad rewrites or framework migration
|
||||||
|
- DOM ID or event-contract changes
|
||||||
|
- Reworking quote math or export structure without a clear bug
|
||||||
|
- Activating Sections IV–VI beyond placeholder state
|
||||||
96
docs/quote-rules.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# Quote Rules
|
||||||
|
|
||||||
|
Use this only when pricing or behavior rules are relevant. For general resume context, the four startup docs are enough.
|
||||||
|
|
||||||
|
## Pricing source of truth
|
||||||
|
|
||||||
|
- Built-in pricing defaults live in `quote-pricing.js`.
|
||||||
|
- `package-prices.json` may override them at startup.
|
||||||
|
- `readFormState()` in `quote-engine.js` maps the DOM into a normalized state object.
|
||||||
|
- `calculateQuote(state, pricing)` in `quote-engine.js` is the current pricing source of truth.
|
||||||
|
- `calcQuote()` in `SVS-MSP-Calculator.js` remains as the compatibility wrapper used by the rest of the runtime.
|
||||||
|
|
||||||
|
## Core billing rules
|
||||||
|
|
||||||
|
- User package base rate:
|
||||||
|
- `RATE_M365_M2M` ($140) when M365 Included + month-to-month
|
||||||
|
- `RATE_M365` ($130) when M365 Included + 12-month or 24-month term
|
||||||
|
- `RATE_BYOL` ($110) when BYOL is selected (all terms)
|
||||||
|
- M365 retail comparison: monthly $36, annual $29 — savings shown in UI
|
||||||
|
- User add-ons:
|
||||||
|
- Extended Hours
|
||||||
|
- 1Password
|
||||||
|
- INKY Pro
|
||||||
|
- Zero Trust User Seat
|
||||||
|
- Endpoint pricing:
|
||||||
|
- `RATE_ENDPOINT` per endpoint
|
||||||
|
- `ADDON_USB_BLOCKING` per endpoint when selected
|
||||||
|
- `ADDON_BARE_METAL_BACKUP` per endpoint when selected
|
||||||
|
- Server pricing:
|
||||||
|
- `RATE_SERVER` per server
|
||||||
|
- Zero Trust Networking:
|
||||||
|
- `ZT_SEAT_RATE` per non-user device seat
|
||||||
|
- `ZT_ROUTER_RATE` per HaaS device
|
||||||
|
- VoIP:
|
||||||
|
- tier rate per seat
|
||||||
|
- optional desk phone add-on
|
||||||
|
- optional eFax add-on priced per seat
|
||||||
|
|
||||||
|
## Site admin fee rules
|
||||||
|
|
||||||
|
- Base subtotal for threshold logic:
|
||||||
|
- user base
|
||||||
|
- endpoint base
|
||||||
|
- server base
|
||||||
|
- Site admin base:
|
||||||
|
- `max(ADMIN_FEE_FLOOR, ADMIN_FEE_MINIMUM - baseSubtotal)`
|
||||||
|
- Additional site admin charges:
|
||||||
|
- `ADMIN_FEE_ZT` when Zero Trust is active
|
||||||
|
- `ADMIN_1PWM_PCT` surcharge on 1Password MRR when 1Password is selected
|
||||||
|
- Admin fee can be manually waived
|
||||||
|
|
||||||
|
## Contract term rules
|
||||||
|
|
||||||
|
- Month-to-month: no MRR discount, full onboarding fee
|
||||||
|
- 12-month: 3% off MRR + 50% off onboarding fee
|
||||||
|
- 24-month: 5% off MRR + complimentary onboarding (fully waived)
|
||||||
|
|
||||||
|
## Onboarding fee rules
|
||||||
|
|
||||||
|
- Default onboarding fee is auto-calculated as 50% of pre-discount MRR
|
||||||
|
- 12-month term auto-applies 50% discount to onboarding (25% of MRR)
|
||||||
|
- 24-month term fully waives onboarding (complimentary)
|
||||||
|
- Manual entry overrides the auto value until reset
|
||||||
|
- Manual override value is preserved in `data-manual-value` attribute across term switches
|
||||||
|
- Waived onboarding disables the input and shows complimentary state
|
||||||
|
- Onboarding fee is not included in headline MRR
|
||||||
|
|
||||||
|
## Tax rules
|
||||||
|
|
||||||
|
- Ontario HST is optional (user toggle)
|
||||||
|
- HST is applied to discounted MRR, not base MRR
|
||||||
|
- Print/PDF export respects the user's HST toggle state (does not force HST on)
|
||||||
|
|
||||||
|
## Savings and comparison rules
|
||||||
|
|
||||||
|
- M365 bundle savings are shown when M365 Included is selected
|
||||||
|
- BYOL can trigger a missed-savings callout
|
||||||
|
- VoIP can compare against a current phone bill input
|
||||||
|
- In-house IT comparison uses internal tool-cost and salary assumptions from pricing config
|
||||||
|
|
||||||
|
## eFax pricing decision
|
||||||
|
|
||||||
|
- The approved business rule is per seat
|
||||||
|
- Current logic already calculates the eFax add-on per VoIP seat
|
||||||
|
- Any remaining `per line` sales copy should be aligned in a small approved copy pass
|
||||||
|
|
||||||
|
## JSON export rules
|
||||||
|
|
||||||
|
- Export includes `version: '1.0'` as first field in payload
|
||||||
|
- Schema defined in STAGE2-BUILD-PROMPT.md
|
||||||
|
- Intended for n8n workflow consumption
|
||||||
|
|
||||||
|
## Admin fee nudge
|
||||||
|
|
||||||
|
- When Zero Trust is active (`ztActive`), an amber nudge warns about the $250 admin fee supplement
|
||||||
|
- Nudge is informational — does not block configuration
|
||||||
104
docs/regression-checklist.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# Regression Checklist
|
||||||
|
|
||||||
|
Use this when a change could affect runtime behavior. It is not required reading for every session.
|
||||||
|
|
||||||
|
## Automated tests (run first)
|
||||||
|
|
||||||
|
```
|
||||||
|
node svsmspcalc/tests/test-quote-engine.js
|
||||||
|
```
|
||||||
|
|
||||||
|
This covers all core quote math, admin fee logic, discounts, HST, VoIP, ZT, and MRR integrity. If all 88 tests pass, the items marked **[AUTO]** below are verified.
|
||||||
|
|
||||||
|
## Core quote math
|
||||||
|
|
||||||
|
- **[AUTO]** Verify M365 Included and BYOL base rates
|
||||||
|
- **[AUTO]** Verify each user add-on changes MRR correctly
|
||||||
|
- **[AUTO]** Verify endpoint count affects endpoint pricing
|
||||||
|
- **[AUTO]** Verify server count affects server pricing
|
||||||
|
- **[AUTO]** Verify Zero Trust Networking seat and HaaS counts affect MRR correctly
|
||||||
|
- **[AUTO]** Verify VoIP tier changes pricing correctly
|
||||||
|
- **[AUTO]** Verify desk phone and eFax add-ons change pricing correctly
|
||||||
|
|
||||||
|
## Site admin fee
|
||||||
|
|
||||||
|
- **[AUTO]** Verify admin fee floor at low subtotal
|
||||||
|
- **[AUTO]** Verify admin fee reduces as base subtotal rises
|
||||||
|
- **[AUTO]** Verify admin fee stops reducing at the configured floor
|
||||||
|
- **[AUTO]** Verify Zero Trust supplement appears when Zero Trust is active
|
||||||
|
- **[AUTO]** Verify 1Password admin surcharge appears when 1Password is selected
|
||||||
|
- **[AUTO]** Verify admin fee waive excludes fee from MRR
|
||||||
|
- [MANUAL] Verify admin fee waive state updates all relevant displays
|
||||||
|
|
||||||
|
## Contract term and onboarding
|
||||||
|
|
||||||
|
- **[AUTO]** Verify month-to-month has no discount
|
||||||
|
- **[AUTO]** Verify 12-month applies 3% discount
|
||||||
|
- **[AUTO]** Verify 24-month applies 5% discount
|
||||||
|
- [MANUAL] Verify 12-month auto-waives onboarding
|
||||||
|
- [MANUAL] Verify 24-month auto-waives onboarding
|
||||||
|
- [MANUAL] Verify switching back to month-to-month restores editable onboarding behavior
|
||||||
|
- [MANUAL] Verify manual onboarding override persists until reset by waiver logic
|
||||||
|
|
||||||
|
## Tax and totals
|
||||||
|
|
||||||
|
- **[AUTO]** Verify HST toggle applies 13% to discounted MRR
|
||||||
|
- **[AUTO]** Verify HST disabled = $0
|
||||||
|
- **[AUTO]** Verify HST applied after contract discount (not before)
|
||||||
|
- **[AUTO]** Verify annual projection matches effective MRR * 12
|
||||||
|
- **[AUTO]** Verify per-user effective cost is 0 when users = 0
|
||||||
|
|
||||||
|
## Sidebar and mobile parity
|
||||||
|
|
||||||
|
- [MANUAL] Verify desktop sidebar values match configured quote
|
||||||
|
- [MANUAL] Verify mobile pill MRR matches headline MRR
|
||||||
|
- [MANUAL] Verify mobile panel content matches desktop sidebar
|
||||||
|
- [MANUAL] Verify mobile HST toggle syncs back to desktop state
|
||||||
|
- [MANUAL] Verify nudge banner content and state match on desktop and mobile
|
||||||
|
|
||||||
|
## Persistence and reset
|
||||||
|
|
||||||
|
- [MANUAL] Verify quote state restores after reload
|
||||||
|
- [MANUAL] Verify theme preference restores after reload
|
||||||
|
- [MANUAL] Verify quote reference persists within the intended window
|
||||||
|
- [MANUAL] Verify reset clears quote state
|
||||||
|
- [MANUAL] Verify reset preserves theme preference
|
||||||
|
|
||||||
|
## Export and print
|
||||||
|
|
||||||
|
- [MANUAL] Verify Print / Save PDF opens the formatted print view
|
||||||
|
- [MANUAL] Verify print respects user's HST toggle state
|
||||||
|
- [MANUAL] Verify print view uses the expected totals, discount, onboarding, and HST states
|
||||||
|
- [MANUAL] Verify JSON export downloads a file
|
||||||
|
- [MANUAL] Verify JSON export copies content to clipboard when supported
|
||||||
|
- [MANUAL] Verify JSON payload matches the visible quote configuration
|
||||||
|
- [MANUAL] Verify JSON export includes `"version": "1.0"` field
|
||||||
|
|
||||||
|
## Section header layout
|
||||||
|
|
||||||
|
- [MANUAL] Verify all 6 sections show numeral + title + chevron on row 1
|
||||||
|
- [MANUAL] Verify controls row (stepper + badge + price) on row 2
|
||||||
|
- [MANUAL] Verify subtitle appears on row 3 only when expanded
|
||||||
|
- [MANUAL] Verify small-screen stacking at ≤600px / container ≤520px
|
||||||
|
- [MANUAL] Verify all elements span full width when stacked
|
||||||
|
|
||||||
|
## UX interactions (Phase 3)
|
||||||
|
|
||||||
|
- [MANUAL] Verify theme switch transitions smoothly (no flash)
|
||||||
|
- [MANUAL] Verify nudge banner crossfades on rotation and manual nav
|
||||||
|
- [MANUAL] Verify section summary badge fades in on collapse
|
||||||
|
- [MANUAL] Verify addon row shows subtle pulse on toggle
|
||||||
|
- [MANUAL] Verify touch targets ≥44px at ≤600px (close btn, nudge nav, section toggles)
|
||||||
|
- [MANUAL] Verify mobile panel traps focus (Tab cycles within panel)
|
||||||
|
- [MANUAL] Verify focus returns to pill on panel close
|
||||||
|
- [MANUAL] Verify safe-area insets respected on notch phones (pill position, panel padding)
|
||||||
|
|
||||||
|
## Accessibility (Phase 5)
|
||||||
|
|
||||||
|
- [MANUAL] Verify `aria-expanded` toggles on section headers when opening/closing
|
||||||
|
- [MANUAL] Verify `aria-expanded` toggles on collapsible headers when opening/closing
|
||||||
|
- [MANUAL] Verify reset modal traps focus (Tab cycles within modal, cannot reach background)
|
||||||
|
- [MANUAL] Verify stepper buttons announce descriptive labels via screen reader
|
||||||
|
- [MANUAL] Verify Glass theme scrolls smoothly on mobile (no jank from fixed backgrounds)
|
||||||
|
- [MANUAL] Verify mobile panel gets full sync on open (client name, discount label visible)
|
||||||
|
- [MANUAL] Verify mobile sync skips on desktop when panel is closed (performance)
|
||||||
18
fontawesomekit/LICENSE.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
Font Awesome Pro License
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Font Awesome Pro is commercial software that requires a paid license. Full
|
||||||
|
Font Awesome Pro license: https://fontawesome.com/license.
|
||||||
|
|
||||||
|
# Commercial License
|
||||||
|
The Font Awesome Pro commercial license allows you to pay for FA Pro once, own
|
||||||
|
it, and use it just about everywhere you'd like.
|
||||||
|
|
||||||
|
# Attribution
|
||||||
|
Attribution is not required by the Font Awesome Pro commercial license.
|
||||||
|
|
||||||
|
# Brand Icons
|
||||||
|
All brand icons are trademarks of their respective owners. The use of these
|
||||||
|
trademarks does not indicate endorsement of the trademark holder by Font
|
||||||
|
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||||
|
to represent the company, product, or service to which they refer.**
|
||||||
6650
fontawesomekit/metadata/categories.yml
Normal file
239112
fontawesomekit/metadata/icon-families.json
Normal file
133643
fontawesomekit/metadata/icon-families.yml
Normal file
1
fontawesomekit/metadata/icons.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
2267
fontawesomekit/metadata/shims.json
Normal file
503
fontawesomekit/metadata/shims.yml
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
---
|
||||||
|
ils:
|
||||||
|
name: shekel-sign
|
||||||
|
rouble:
|
||||||
|
name: ruble-sign
|
||||||
|
file-zip-o:
|
||||||
|
name: file-zipper
|
||||||
|
prefix: far
|
||||||
|
bathtub:
|
||||||
|
name: bath
|
||||||
|
life-saver:
|
||||||
|
name: life-ring
|
||||||
|
hand-stop-o:
|
||||||
|
name: hand
|
||||||
|
prefix: far
|
||||||
|
minus-square-o:
|
||||||
|
name: square-minus
|
||||||
|
prefix: far
|
||||||
|
dedent:
|
||||||
|
name: outdent
|
||||||
|
bank:
|
||||||
|
name: building-columns
|
||||||
|
file-movie-o:
|
||||||
|
name: file-video
|
||||||
|
prefix: far
|
||||||
|
check-circle-o:
|
||||||
|
name: circle-check
|
||||||
|
prefix: far
|
||||||
|
rmb:
|
||||||
|
name: yen-sign
|
||||||
|
times-rectangle:
|
||||||
|
name: rectangle-xmark
|
||||||
|
television:
|
||||||
|
name: tv
|
||||||
|
hdd-o:
|
||||||
|
name: hard-drive
|
||||||
|
prefix: far
|
||||||
|
frown-o:
|
||||||
|
name: face-frown
|
||||||
|
prefix: far
|
||||||
|
mail-forward:
|
||||||
|
name: share
|
||||||
|
inr:
|
||||||
|
name: indian-rupee-sign
|
||||||
|
save:
|
||||||
|
name: floppy-disk
|
||||||
|
prefix: far
|
||||||
|
signing:
|
||||||
|
name: hands
|
||||||
|
credit-card-alt:
|
||||||
|
name: credit-card
|
||||||
|
shekel:
|
||||||
|
name: shekel-sign
|
||||||
|
life-buoy:
|
||||||
|
name: life-ring
|
||||||
|
edit:
|
||||||
|
name: pen-to-square
|
||||||
|
prefix: far
|
||||||
|
window-close-o:
|
||||||
|
name: rectangle-xmark
|
||||||
|
prefix: far
|
||||||
|
cloud-download:
|
||||||
|
name: cloud-arrow-down
|
||||||
|
file-archive-o:
|
||||||
|
name: file-zipper
|
||||||
|
prefix: far
|
||||||
|
euro:
|
||||||
|
name: euro-sign
|
||||||
|
code-fork:
|
||||||
|
name: code-branch
|
||||||
|
hourglass-3:
|
||||||
|
name: hourglass-end
|
||||||
|
external-link:
|
||||||
|
name: up-right-from-square
|
||||||
|
send-o:
|
||||||
|
name: paper-plane
|
||||||
|
prefix: far
|
||||||
|
thumb-tack:
|
||||||
|
name: thumbtack
|
||||||
|
turkish-lira:
|
||||||
|
name: turkish-lira-sign
|
||||||
|
krw:
|
||||||
|
name: won-sign
|
||||||
|
toggle-right:
|
||||||
|
name: square-caret-right
|
||||||
|
prefix: far
|
||||||
|
eyedropper:
|
||||||
|
name: eye-dropper
|
||||||
|
tachometer:
|
||||||
|
name: gauge-high
|
||||||
|
transgender-alt:
|
||||||
|
name: transgender
|
||||||
|
battery-3:
|
||||||
|
name: battery-three-quarters
|
||||||
|
close:
|
||||||
|
name: xmark
|
||||||
|
battery-0:
|
||||||
|
name: battery-empty
|
||||||
|
star-half-o:
|
||||||
|
name: star-half-stroke
|
||||||
|
prefix: far
|
||||||
|
thermometer-1:
|
||||||
|
name: temperature-quarter
|
||||||
|
cc:
|
||||||
|
name: closed-captioning
|
||||||
|
prefix: far
|
||||||
|
caret-square-o-down:
|
||||||
|
name: square-caret-down
|
||||||
|
prefix: far
|
||||||
|
star-half-empty:
|
||||||
|
name: star-half-stroke
|
||||||
|
prefix: far
|
||||||
|
compress:
|
||||||
|
name: down-left-and-up-right-to-center
|
||||||
|
yen:
|
||||||
|
name: yen-sign
|
||||||
|
arrows-v:
|
||||||
|
name: up-down
|
||||||
|
sign-out:
|
||||||
|
name: right-from-bracket
|
||||||
|
trash-o:
|
||||||
|
name: trash-can
|
||||||
|
prefix: far
|
||||||
|
feed:
|
||||||
|
name: rss
|
||||||
|
reorder:
|
||||||
|
name: bars
|
||||||
|
long-arrow-left:
|
||||||
|
name: left-long
|
||||||
|
group:
|
||||||
|
name: users
|
||||||
|
times-circle-o:
|
||||||
|
name: circle-xmark
|
||||||
|
prefix: far
|
||||||
|
diamond:
|
||||||
|
name: gem
|
||||||
|
prefix: far
|
||||||
|
mail-reply:
|
||||||
|
name: reply
|
||||||
|
mobile-phone:
|
||||||
|
name: mobile-screen-button
|
||||||
|
file-picture-o:
|
||||||
|
name: file-image
|
||||||
|
prefix: far
|
||||||
|
unsorted:
|
||||||
|
name: sort
|
||||||
|
navicon:
|
||||||
|
name: bars
|
||||||
|
file-sound-o:
|
||||||
|
name: file-audio
|
||||||
|
prefix: far
|
||||||
|
hand-o-up:
|
||||||
|
name: hand-point-up
|
||||||
|
prefix: far
|
||||||
|
globe:
|
||||||
|
name: earth-americas
|
||||||
|
try:
|
||||||
|
name: turkish-lira-sign
|
||||||
|
question-circle-o:
|
||||||
|
name: circle-question
|
||||||
|
prefix: far
|
||||||
|
cutlery:
|
||||||
|
name: utensils
|
||||||
|
file-text:
|
||||||
|
name: file-lines
|
||||||
|
arrow-circle-o-down:
|
||||||
|
name: circle-down
|
||||||
|
prefix: far
|
||||||
|
rotate-left:
|
||||||
|
name: arrow-rotate-left
|
||||||
|
cut:
|
||||||
|
name: scissors
|
||||||
|
chain:
|
||||||
|
name: link
|
||||||
|
level-down:
|
||||||
|
name: turn-down
|
||||||
|
sort-desc:
|
||||||
|
name: sort-down
|
||||||
|
toggle-left:
|
||||||
|
name: square-caret-left
|
||||||
|
prefix: far
|
||||||
|
cloud-upload:
|
||||||
|
name: cloud-arrow-up
|
||||||
|
user-circle-o:
|
||||||
|
name: circle-user
|
||||||
|
prefix: far
|
||||||
|
bar-chart-o:
|
||||||
|
name: chart-column
|
||||||
|
hourglass-1:
|
||||||
|
name: hourglass-start
|
||||||
|
soccer-ball-o:
|
||||||
|
name: futbol
|
||||||
|
prefix: far
|
||||||
|
thermometer-3:
|
||||||
|
name: temperature-three-quarters
|
||||||
|
circle-o-notch:
|
||||||
|
name: circle-notch
|
||||||
|
calendar-times-o:
|
||||||
|
name: calendar-xmark
|
||||||
|
prefix: far
|
||||||
|
rotate-right:
|
||||||
|
name: arrow-rotate-right
|
||||||
|
automobile:
|
||||||
|
name: car
|
||||||
|
eur:
|
||||||
|
name: euro-sign
|
||||||
|
ruble:
|
||||||
|
name: ruble-sign
|
||||||
|
tablet:
|
||||||
|
name: tablet-screen-button
|
||||||
|
unlink:
|
||||||
|
name: link-slash
|
||||||
|
deafness:
|
||||||
|
name: ear-deaf
|
||||||
|
vcard:
|
||||||
|
name: address-card
|
||||||
|
sort-numeric-desc:
|
||||||
|
name: arrow-down-9-1
|
||||||
|
volume-control-phone:
|
||||||
|
name: phone-volume
|
||||||
|
glass:
|
||||||
|
name: martini-glass-empty
|
||||||
|
circle-thin:
|
||||||
|
name: circle
|
||||||
|
prefix: far
|
||||||
|
institution:
|
||||||
|
name: building-columns
|
||||||
|
dot-circle-o:
|
||||||
|
name: circle-dot
|
||||||
|
prefix: far
|
||||||
|
drivers-license-o:
|
||||||
|
name: id-card
|
||||||
|
prefix: far
|
||||||
|
pie-chart:
|
||||||
|
name: chart-pie
|
||||||
|
vcard-o:
|
||||||
|
name: address-card
|
||||||
|
prefix: far
|
||||||
|
thermometer-4:
|
||||||
|
name: temperature-full
|
||||||
|
sort-amount-asc:
|
||||||
|
name: arrow-down-short-wide
|
||||||
|
photo:
|
||||||
|
name: image
|
||||||
|
prefix: far
|
||||||
|
floppy-o:
|
||||||
|
name: floppy-disk
|
||||||
|
prefix: far
|
||||||
|
hand-o-left:
|
||||||
|
name: hand-point-left
|
||||||
|
prefix: far
|
||||||
|
flash:
|
||||||
|
name: bolt
|
||||||
|
unlock-alt:
|
||||||
|
name: unlock
|
||||||
|
thumbs-o-down:
|
||||||
|
name: thumbs-down
|
||||||
|
prefix: far
|
||||||
|
arrow-circle-o-right:
|
||||||
|
name: circle-right
|
||||||
|
prefix: far
|
||||||
|
hand-grab-o:
|
||||||
|
name: hand-back-fist
|
||||||
|
prefix: far
|
||||||
|
video-camera:
|
||||||
|
name: video
|
||||||
|
calendar:
|
||||||
|
name: calendar-days
|
||||||
|
mail-reply-all:
|
||||||
|
name: reply-all
|
||||||
|
magic:
|
||||||
|
name: wand-magic-sparkles
|
||||||
|
smile-o:
|
||||||
|
name: face-smile
|
||||||
|
prefix: far
|
||||||
|
s15:
|
||||||
|
name: bath
|
||||||
|
send:
|
||||||
|
name: paper-plane
|
||||||
|
support:
|
||||||
|
name: life-ring
|
||||||
|
exchange:
|
||||||
|
name: right-left
|
||||||
|
refresh:
|
||||||
|
name: arrows-rotate
|
||||||
|
area-chart:
|
||||||
|
name: chart-area
|
||||||
|
stop-circle-o:
|
||||||
|
name: circle-stop
|
||||||
|
prefix: far
|
||||||
|
level-up:
|
||||||
|
name: turn-up
|
||||||
|
map-marker:
|
||||||
|
name: location-dot
|
||||||
|
hourglass-o:
|
||||||
|
name: hourglass
|
||||||
|
external-link-square:
|
||||||
|
name: square-up-right
|
||||||
|
gear:
|
||||||
|
name: gear
|
||||||
|
files-o:
|
||||||
|
name: copy
|
||||||
|
prefix: far
|
||||||
|
home:
|
||||||
|
name: house
|
||||||
|
share-square-o:
|
||||||
|
name: share-from-square
|
||||||
|
thermometer-2:
|
||||||
|
name: temperature-half
|
||||||
|
hand-rock-o:
|
||||||
|
name: hand-back-fist
|
||||||
|
prefix: far
|
||||||
|
legal:
|
||||||
|
name: gavel
|
||||||
|
warning:
|
||||||
|
name: triangle-exclamation
|
||||||
|
play-circle-o:
|
||||||
|
name: circle-play
|
||||||
|
prefix: far
|
||||||
|
sheqel:
|
||||||
|
name: shekel-sign
|
||||||
|
fa:
|
||||||
|
name: font-awesome
|
||||||
|
prefix: fab
|
||||||
|
image:
|
||||||
|
name: image
|
||||||
|
prefix: far
|
||||||
|
picture-o:
|
||||||
|
name: image
|
||||||
|
prefix: far
|
||||||
|
life-bouy:
|
||||||
|
name: life-ring
|
||||||
|
dashboard:
|
||||||
|
name: gauge-high
|
||||||
|
tasks:
|
||||||
|
name: bars-progress
|
||||||
|
sort-alpha-asc:
|
||||||
|
name: arrow-down-a-z
|
||||||
|
toggle-up:
|
||||||
|
name: square-caret-up
|
||||||
|
prefix: far
|
||||||
|
chain-broken:
|
||||||
|
name: link-slash
|
||||||
|
long-arrow-down:
|
||||||
|
name: down-long
|
||||||
|
rupee:
|
||||||
|
name: indian-rupee-sign
|
||||||
|
hand-paper-o:
|
||||||
|
name: hand
|
||||||
|
prefix: far
|
||||||
|
battery-2:
|
||||||
|
name: battery-half
|
||||||
|
asl-interpreting:
|
||||||
|
name: hands-asl-interpreting
|
||||||
|
battery:
|
||||||
|
name: battery-full
|
||||||
|
hand-o-right:
|
||||||
|
name: hand-point-right
|
||||||
|
prefix: far
|
||||||
|
meh-o:
|
||||||
|
name: face-meh
|
||||||
|
prefix: far
|
||||||
|
clipboard:
|
||||||
|
name: paste
|
||||||
|
header:
|
||||||
|
name: heading
|
||||||
|
cab:
|
||||||
|
name: taxi
|
||||||
|
thumbs-o-up:
|
||||||
|
name: thumbs-up
|
||||||
|
prefix: far
|
||||||
|
money:
|
||||||
|
name: money-bill-1
|
||||||
|
battery-1:
|
||||||
|
name: battery-quarter
|
||||||
|
repeat:
|
||||||
|
name: arrow-rotate-right
|
||||||
|
long-arrow-up:
|
||||||
|
name: up-long
|
||||||
|
battery-4:
|
||||||
|
name: battery-full
|
||||||
|
hotel:
|
||||||
|
name: bed
|
||||||
|
toggle-down:
|
||||||
|
name: square-caret-down
|
||||||
|
prefix: far
|
||||||
|
transgender:
|
||||||
|
name: mars-and-venus
|
||||||
|
long-arrow-right:
|
||||||
|
name: right-long
|
||||||
|
sort-alpha-desc:
|
||||||
|
name: arrow-down-z-a
|
||||||
|
gbp:
|
||||||
|
name: sterling-sign
|
||||||
|
file-photo-o:
|
||||||
|
name: file-image
|
||||||
|
prefix: far
|
||||||
|
hard-of-hearing:
|
||||||
|
name: ear-deaf
|
||||||
|
file-text-o:
|
||||||
|
name: file-lines
|
||||||
|
prefix: far
|
||||||
|
drivers-license:
|
||||||
|
name: id-card
|
||||||
|
thermometer-0:
|
||||||
|
name: temperature-empty
|
||||||
|
list-alt:
|
||||||
|
name: rectangle-list
|
||||||
|
prefix: far
|
||||||
|
caret-square-o-left:
|
||||||
|
name: square-caret-left
|
||||||
|
prefix: far
|
||||||
|
hand-o-down:
|
||||||
|
name: hand-point-down
|
||||||
|
prefix: far
|
||||||
|
caret-square-o-right:
|
||||||
|
name: square-caret-right
|
||||||
|
prefix: far
|
||||||
|
sort-amount-desc:
|
||||||
|
name: arrow-down-wide-short
|
||||||
|
expand:
|
||||||
|
name: up-right-and-down-left-from-center
|
||||||
|
arrows:
|
||||||
|
name: up-down-left-right
|
||||||
|
trash:
|
||||||
|
name: trash-can
|
||||||
|
plus-square-o:
|
||||||
|
name: square-plus
|
||||||
|
prefix: far
|
||||||
|
caret-square-o-up:
|
||||||
|
name: square-caret-up
|
||||||
|
prefix: far
|
||||||
|
thermometer:
|
||||||
|
name: temperature-full
|
||||||
|
pause-circle-o:
|
||||||
|
name: circle-pause
|
||||||
|
prefix: far
|
||||||
|
arrows-h:
|
||||||
|
name: left-right
|
||||||
|
won:
|
||||||
|
name: won-sign
|
||||||
|
sign-in:
|
||||||
|
name: right-to-bracket
|
||||||
|
cny:
|
||||||
|
name: yen-sign
|
||||||
|
arrow-circle-o-up:
|
||||||
|
name: circle-up
|
||||||
|
prefix: far
|
||||||
|
hourglass-2:
|
||||||
|
name: hourglass-half
|
||||||
|
pencil-square:
|
||||||
|
name: square-pen
|
||||||
|
usd:
|
||||||
|
name: dollar-sign
|
||||||
|
sticky-note-o:
|
||||||
|
name: note-sticky
|
||||||
|
prefix: far
|
||||||
|
commenting-o:
|
||||||
|
name: comment-dots
|
||||||
|
prefix: far
|
||||||
|
rub:
|
||||||
|
name: ruble-sign
|
||||||
|
mortar-board:
|
||||||
|
name: graduation-cap
|
||||||
|
commenting:
|
||||||
|
name: comment-dots
|
||||||
|
remove:
|
||||||
|
name: xmark
|
||||||
|
sort-numeric-asc:
|
||||||
|
name: arrow-down-1-9
|
||||||
|
jpy:
|
||||||
|
name: yen-sign
|
||||||
|
sort-asc:
|
||||||
|
name: sort-up
|
||||||
|
arrow-circle-o-left:
|
||||||
|
name: circle-left
|
||||||
|
prefix: far
|
||||||
|
mobile:
|
||||||
|
name: mobile-screen-button
|
||||||
|
pencil-square-o:
|
||||||
|
name: pen-to-square
|
||||||
|
prefix: far
|
||||||
|
star-half-full:
|
||||||
|
name: star-half-stroke
|
||||||
|
prefix: far
|
||||||
|
arrows-alt:
|
||||||
|
name: maximize
|
||||||
|
line-chart:
|
||||||
|
name: chart-line
|
||||||
|
intersex:
|
||||||
|
name: mars-and-venus
|
||||||
|
dollar:
|
||||||
|
name: dollar-sign
|
||||||
|
gears:
|
||||||
|
name: gears
|
||||||
|
check-square-o:
|
||||||
|
name: square-check
|
||||||
|
prefix: far
|
||||||
|
times-rectangle-o:
|
||||||
|
name: rectangle-xmark
|
||||||
|
prefix: far
|
||||||
|
bar-chart:
|
||||||
|
name: chart-column
|
||||||
1038
fontawesomekit/metadata/sponsors.yml
Normal file
BIN
fontawesomekit/otfs/Font Awesome 7 Sharp-Solid-900.otf
Normal file
1
fontawesomekit/svgs-full/sharp-solid/0.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M160 256C160 167.6 231.6 96 320 96C408.4 96 480 167.6 480 256L480 384C480 472.4 408.4 544 320 544C231.6 544 160 472.4 160 384L160 256zM320 160C267 160 224 203 224 256L224 384C224 437 267 480 320 480C373 480 416 437 416 384L416 256C416 203 373 160 320 160z"/></svg>
|
||||||
|
After Width: | Height: | Size: 523 B |
1
fontawesomekit/svgs-full/sharp-solid/00.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M176 96C114.1 96 64 146.1 64 208L64 432C64 493.9 114.1 544 176 544C237.9 544 288 493.9 288 432L288 208C288 146.1 237.9 96 176 96zM128 208C128 181.5 149.5 160 176 160C202.5 160 224 181.5 224 208L224 432C224 458.5 202.5 480 176 480C149.5 480 128 458.5 128 432L128 208zM464 96C402.1 96 352 146.1 352 208L352 432C352 493.9 402.1 544 464 544C525.9 544 576 493.9 576 432L576 208C576 146.1 525.9 96 464 96zM416 208C416 181.5 437.5 160 464 160C490.5 160 512 181.5 512 208L512 432C512 458.5 490.5 480 464 480C437.5 480 416 458.5 416 432L416 208z"/></svg>
|
||||||
|
After Width: | Height: | Size: 804 B |
1
fontawesomekit/svgs-full/sharp-solid/1.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M224 96L192 96L192 160L288 160L288 480L192 480L192 544L448 544L448 480L352 480L352 96L224 96z"/></svg>
|
||||||
|
After Width: | Height: | Size: 361 B |
1
fontawesomekit/svgs-full/sharp-solid/100.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M306.5 96C354.4 96 391.5 138 385.5 185.5L373.4 282.2C368.4 322 334.5 352 294.4 352C246.5 352 209.4 310 215.4 262.5L227.5 165.8C232.5 126 266.4 96 306.5 96zM291 173.7L278.9 270.4C277.7 279.7 285 288 294.4 288C302.3 288 308.9 282.1 309.9 274.3L322 177.6C323.2 168.3 315.9 160 306.5 160C298.6 160 292 165.9 291 173.7zM556.5 415.7L92.5 495.7L84.3 448.4L548.3 368.4L556.5 415.7zM524.4 503.7L284.4 543.7L276.5 496.4L516.5 456.4L524.4 503.7zM412.6 168.6C416.3 127.5 450.8 96 492.1 96C539 96 575.8 136.3 571.6 183L564.3 263.4C560.6 304.5 526.1 336 484.8 336C437.9 336 401.1 295.7 405.3 249L412.6 168.6zM492.1 160C483.9 160 477.1 166.2 476.4 174.4L469.1 254.8C468.3 264.1 475.5 272 484.8 272C493 272 499.8 265.8 500.5 257.6L507.8 177.2C508.6 167.9 501.4 160 492.1 160zM128.7 180.5L125 182.1L95.6 195.2L70.4 136.8C85.6 130.1 114.1 117.4 155.8 98.8L200.1 132L165.6 373.5L99.7 382.9L100.7 376L128.6 180.5z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
fontawesomekit/svgs-full/sharp-solid/2.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M176 96L379 96C439.2 96 488 144.8 488 205C488 248.8 461.8 288.3 421.6 305.4L282.5 364.4C247 379.4 224 414.2 224 452.7L224 480L480 480L480 544L160 544L160 452.7C160 388.5 198.4 330.5 257.5 305.4L396.6 246.4C413.2 239.3 424 223 424 205C424 180.1 403.8 160 379 160L176 160L176 96z"/></svg>
|
||||||
|
After Width: | Height: | Size: 545 B |
1
fontawesomekit/svgs-full/sharp-solid/3.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M352 352L208 352L208 288L352 288C387.3 288 416 259.3 416 224C416 188.7 387.3 160 352 160L160 160L160 96L352 96C422.7 96 480 153.3 480 224C480 262.2 463.2 296.5 436.7 320C463.3 343.5 480 377.8 480 416C480 486.7 422.7 544 352 544L160 544L160 480L352 480C387.3 480 416 451.3 416 416C416 380.7 387.3 352 352 352z"/></svg>
|
||||||
|
After Width: | Height: | Size: 576 B |
1
fontawesomekit/svgs-full/sharp-solid/360-degrees.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M640 96C640 78.3 625.7 64 608 64C590.3 64 576 78.3 576 96C576 113.7 590.3 128 608 128C625.7 128 640 113.7 640 96zM320 192L352 192L352 128L320 128C267 128 224 171 224 224L224 335.8L224 336L224 432C224 476.2 259.8 512 304 512C348.2 512 384 476.2 384 432L384 336C384 291.8 348.2 256 304 256C298.5 256 293.2 256.6 288 257.6L288 224C288 206.3 302.3 192 320 192zM288 384L288 335.9C288 327.1 295.2 320 304 320C312.8 320 320 327.2 320 336L320 432C320 440.8 312.8 448 304 448C295.2 448 288 440.8 288 432L288 384zM32 128L0 128L0 192L97.3 192L39.7 259.2L32 268.2L32 320L80 320C106.5 320 128 341.5 128 368L128 416C128 433.7 113.7 448 96 448C78.3 448 64 433.7 64 416L0 416C0 469 43 512 96 512C149 512 192 469 192 416L192 368C192 320.4 162.2 279.7 120.3 263.5L184.3 188.9L192 179.9L192 128.1L32 128.1zM480 208C480 199.2 487.2 192 496 192C504.8 192 512 199.2 512 208L512 432C512 440.8 504.8 448 496 448C487.2 448 480 440.8 480 432L480 208zM496 128C451.8 128 416 163.8 416 208L416 432C416 476.2 451.8 512 496 512C540.2 512 576 476.2 576 432L576 208C576 163.8 540.2 128 496 128z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
1
fontawesomekit/svgs-full/sharp-solid/4.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M192 128L192 96L128 96L128 416L384 416L384 544L448 544L448 416L512 416L512 352L448 352L448 96L384 96L384 352L192 352L192 128z"/></svg>
|
||||||
|
After Width: | Height: | Size: 393 B |
1
fontawesomekit/svgs-full/sharp-solid/5.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M160 96L448 96L448 160L224 160L224 272L344 272C419.1 272 480 332.9 480 408C480 483.1 419.1 544 344 544L160 544L160 480L344 480C383.8 480 416 447.8 416 408C416 368.2 383.8 336 344 336L160 336L160 96z"/></svg>
|
||||||
|
After Width: | Height: | Size: 466 B |
1
fontawesomekit/svgs-full/sharp-solid/6.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M384 96L416 96L416 160L296 160C247.4 160 208 199.4 208 248L208 280.2C230.9 264.9 258.4 256 288 256L336 256C415.5 256 480 320.5 480 400C480 479.5 415.5 544 336 544L288 544C208.5 544 144 479.5 144 400L144 248C144 164.1 212.1 96 296 96L384 96zM208 400C208 444.2 243.8 480 288 480L336 480C380.2 480 416 444.2 416 400C416 355.8 380.2 320 336 320L288 320C243.8 320 208 355.8 208 400z"/></svg>
|
||||||
|
After Width: | Height: | Size: 645 B |
1
fontawesomekit/svgs-full/sharp-solid/7.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M480 96L480 135.9L476.3 142.9L264.6 544L192.2 544L394.9 160L160 160L160 96L480 96z"/></svg>
|
||||||
|
After Width: | Height: | Size: 350 B |
1
fontawesomekit/svgs-full/sharp-solid/8.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M464 224C464 153.3 406.7 96 336 96L304 96C233.3 96 176 153.3 176 224C176 258.6 189.7 290 212 313C180.5 336.3 160 373.8 160 416C160 486.7 217.3 544 288 544L352 544C422.7 544 480 486.7 480 416C480 373.8 459.5 336.3 428 313C450.3 290 464 258.6 464 224zM336.1 352L352 352C387.3 352 416 380.7 416 416C416 451.3 387.3 480 352 480L288 480C252.7 480 224 451.3 224 416C224 380.7 252.7 352 288 352L336.1 352zM336.1 288L304 288C268.7 288 240 259.3 240 224C240 188.7 268.7 160 304 160L336 160C371.3 160 400 188.7 400 224C400 259.3 371.4 288 336 288z"/></svg>
|
||||||
|
After Width: | Height: | Size: 805 B |
1
fontawesomekit/svgs-full/sharp-solid/9.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M336 384C365.6 384 393.1 375.1 416 359.8L416 392C416 440.6 376.6 480 328 480L192 480L192 544L328 544C411.9 544 480 475.9 480 392L480 239.9C479.9 160.4 415.5 96 336 96L288 96C208.5 96 144 160.5 144 240C144 319.5 208.5 384 288 384L336 384zM416 240C416 284.2 380.2 320 336 320L288 320C243.8 320 208 284.2 208 240C208 195.8 243.8 160 288 160L336 160C380.2 160 416 195.8 416 240z"/></svg>
|
||||||
|
After Width: | Height: | Size: 642 B |
1
fontawesomekit/svgs-full/sharp-solid/a.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M298.1 96L342.3 96L350.1 116.6C432.4 333.4 486.4 475.9 512.3 544L443.8 544L407.4 448L232.9 448L196.5 544L128 544C153.9 475.9 208 333.4 290.2 116.6L298.1 96zM383.1 384L320.1 218.2L257.1 384L383 384z"/></svg>
|
||||||
|
After Width: | Height: | Size: 465 B |
1
fontawesomekit/svgs-full/sharp-solid/abacus.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M544 160L544 272L480 272L480 240L512 240L512 192L480 192L480 160L544 160zM448 192L416 192L416 240L448 240L448 272L320 272L320 240L352 240L352 192L320 192L320 160L448 160L448 192zM288 192L256 192L256 240L288 240L288 272L192 272L192 240L224 240L224 192L192 192L192 160L288 160L288 192zM160 192L128 192L128 240L160 240L160 272L96 272L96 160L160 160L160 192zM96 480L96 304L160 304L160 328L128 328L128 376L160 376L160 400L128 400L128 448L160 448L160 480L96 480zM192 448L224 448L224 400L192 400L192 376L224 376L224 328L192 328L192 304L288 304L288 328L256 328L256 376L288 376L288 400L256 400L256 448L288 448L288 480L192 480L192 448zM320 376L352 376L352 328L320 328L320 304L448 304L448 400L416 400L416 448L448 448L448 480L320 480L320 448L352 448L352 400L320 400L320 376zM480 448L512 448L512 400L480 400L480 304L544 304L544 480L480 480L480 448zM96 96L32 96L32 544L608 544L608 96L96 96z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
fontawesomekit/svgs-full/sharp-solid/accent-grave.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M368 64L400 288L336 288L256 64L368 64z"/></svg>
|
||||||
|
After Width: | Height: | Size: 306 B |
1
fontawesomekit/svgs-full/sharp-solid/acorn.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M495.6 304C491.4 384.1 454.7 499.1 320 544C185.3 499.1 148.6 384.1 144.4 304L495.5 304zM369.4 92.7L355.7 112.4C352.4 117.2 349.7 122.5 347.8 128L448 128C501 128 544 171 544 224L544 256L96 256L96 224C96 171 139 128 192 128L298.2 128C301.2 112.7 307.4 98.1 316.3 85.1L330 65.3L369.5 92.6z"/></svg>
|
||||||
|
After Width: | Height: | Size: 554 B |
1
fontawesomekit/svgs-full/sharp-solid/ad.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M576 128L64 128L64 512L576 512L576 128zM480 248L480 416L432 416L432 410.7C423.3 414.1 413.9 416 404 416C362 416 328 382 328 340C328 298 362 264 404 264C413.9 264 423.3 265.9 432 269.3L432 224L480 224L480 248zM432 340C432 324.5 419.5 312 404 312C388.5 312 376 324.5 376 340C376 355.5 388.5 368 404 368C419.5 368 432 355.5 432 340zM224 272C215.2 272 208 279.2 208 288L208 320L256 320L256 288C256 279.2 248.8 272 240 272L224 272zM256 368L208 368L208 416L160 416L160 288C160 252.7 188.7 224 224 224L240 224C275.3 224 304 252.7 304 288L304 416L256 416L256 368z"/></svg>
|
||||||
|
After Width: | Height: | Size: 823 B |
1
fontawesomekit/svgs-full/sharp-solid/add.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M352 128L352 96L288 96L288 288L96 288L96 352L288 352L288 544L352 544L352 352L544 352L544 288L352 288L352 128z"/></svg>
|
||||||
|
After Width: | Height: | Size: 377 B |
1
fontawesomekit/svgs-full/sharp-solid/address-book.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M512 64L96 64L96 576L512 576L512 64zM384 352L416 448L192 448L224 352L384 352zM248 256C248 225.1 273.1 200 304 200C334.9 200 360 225.1 360 256C360 286.9 334.9 312 304 312C273.1 312 248 286.9 248 256zM576 144L576 128L544 128L544 224L576 224L576 144zM576 256L544 256L544 352L576 352L576 256zM576 400L576 384L544 384L544 480L576 480L576 400z"/></svg>
|
||||||
|
After Width: | Height: | Size: 605 B |
1
fontawesomekit/svgs-full/sharp-solid/address-card.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M608 96L32 96L32 544L608 544L608 96zM288 352L320 448L96 448L128 352L288 352zM152 256C152 225.1 177.1 200 208 200C238.9 200 264 225.1 264 256C264 286.9 238.9 312 208 312C177.1 312 152 286.9 152 256zM392 208L528 208L528 256L368 256L368 208L392 208zM392 304L528 304L528 352L368 352L368 304L392 304z"/></svg>
|
||||||
|
After Width: | Height: | Size: 563 B |
1
fontawesomekit/svgs-full/sharp-solid/adjust.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M512 320C512 214 426 128 320 128L320 512C426 512 512 426 512 320zM64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576C178.6 576 64 461.4 64 320z"/></svg>
|
||||||
|
After Width: | Height: | Size: 447 B |
1
fontawesomekit/svgs-full/sharp-solid/aeropress.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M448 464L512 464L512 512L448 512L448 576L192 576L192 512L128 512L128 464L192 464L192 192L448 192L448 464zM376 416C362.7 416 352 426.7 352 440C352 453.3 362.7 464 376 464C389.3 464 400 453.3 400 440C400 426.7 389.3 416 376 416zM376 336C362.7 336 352 346.7 352 360C352 373.3 362.7 384 376 384C389.3 384 400 373.3 400 360C400 346.7 389.3 336 376 336zM376 256C362.7 256 352 266.7 352 280C352 293.3 362.7 304 376 304C389.3 304 400 293.3 400 280C400 266.7 389.3 256 376 256zM464 96L416 96L416 144L224 144L224 96L176 96L176 48L464 48L464 96z"/></svg>
|
||||||
|
After Width: | Height: | Size: 802 B |
1
fontawesomekit/svgs-full/sharp-solid/air-conditioner.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M608 128L32 128L32 384L608 384L608 128zM120 272L544 272L544 320L96 320L96 272L120 272zM240 536C240 549.3 229.3 560 216 560C202.7 560 192 549.3 192 536L192 512L144 512L144 536C144 575.8 176.2 608 216 608C255.8 608 288 575.8 288 536L288 432L240 432L240 536zM400 432L352 432L352 536C352 575.8 384.2 608 424 608C463.8 608 496 575.8 496 536L496 512L448 512L448 536C448 549.3 437.3 560 424 560C410.7 560 400 549.3 400 536L400 432z"/></svg>
|
||||||
|
After Width: | Height: | Size: 692 B |
1
fontawesomekit/svgs-full/sharp-solid/air-freshener.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M192 64L192 176L320 176L320 64L192 64zM192 224C139 224 96 267 96 320L96 576L416 576L416 320C416 267 373 224 320 224L192 224zM256 320C300.2 320 336 355.8 336 400C336 444.2 300.2 480 256 480C211.8 480 176 444.2 176 400C176 355.8 211.8 320 256 320zM416 96L384 112L416 128L432 160L448 128L480 112L448 96L432 64L416 96zM448 208L480 224L496 256L512 224L544 208L512 192L496 160L480 192L448 208zM560 64L544 96L512 112L544 128L560 160L576 128L608 112L576 96L560 64zM544 288L512 304L544 320L560 352L576 320L608 304L576 288L560 256L544 288z"/></svg>
|
||||||
|
After Width: | Height: | Size: 797 B |
1
fontawesomekit/svgs-full/sharp-solid/airplay-audio.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M112 320C112 373.1 131.9 421.5 164.6 458.3L135.4 487.5L130.6 492.3C89.2 446.8 64 386.3 64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 386.3 550.8 446.8 509.4 492.2L504.6 487.4L475.4 458.2C508.1 421.5 528 373.1 528 320C528 205.1 434.9 112 320 112C205.1 112 112 205.1 112 320zM480 320C480 359.8 465.5 396.2 441.4 424.2L407.3 390.1C422.7 370.9 432 346.5 432 319.9C432 258 381.9 207.9 320 207.9C258.1 207.9 208 258.1 208 320C208 346.6 217.2 371 232.7 390.2L198.6 424.3C174.5 396.2 160 359.8 160 320C160 231.6 231.6 160 320 160C408.4 160 480 231.6 480 320zM320 302.9L267 355.9C260.1 345.7 256 333.3 256 320C256 284.7 284.7 256 320 256C355.3 256 384 284.7 384 320C384 333.3 379.9 345.6 373 355.9L320 302.9zM114.7 576C138.9 551.8 199.8 490.9 297.3 393.4L320 370.7L525.3 576L114.7 576z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
1
fontawesomekit/svgs-full/sharp-solid/airplay.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M128 192L512 192L512 416L449.1 416C477.9 444.8 499.2 466.1 513.1 480L576 480L576 128L64 128L64 480L126.9 480C140.8 466.1 162.1 444.8 190.9 416L128 416L128 192zM130.7 544L509.3 544L320 354.7C319.8 354.9 274.9 399.8 185.4 489.3L130.7 544z"/></svg>
|
||||||
|
After Width: | Height: | Size: 504 B |
1
fontawesomekit/svgs-full/sharp-solid/alarm-clock.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M556.3 217.2C524.1 160.8 472.3 117.1 410.2 95.3C427.6 76.1 452.8 64 480.8 64C533.4 64 576 106.6 576 159.2C576 181 568.7 201.1 556.3 217.2zM83.7 217.2C71.3 201.2 64 181.1 64 159.2C64 106.6 106.6 64 159.2 64C187.2 64 212.4 76.1 229.8 95.3C167.7 117.1 115.9 160.9 83.7 217.2zM454.2 531.4C416.8 559.4 370.3 576 320 576C269.7 576 223.2 559.4 185.9 531.4C160.3 557 141 576.3 128 589.3L82.7 544C95.7 531 115 511.7 140.6 486.2C112.6 448.8 96 402.3 96 352C96 228.3 196.3 128 320 128C443.7 128 544 228.3 544 352C544 402.3 527.4 448.8 499.4 486.2L557.2 544L511.9 589.3L454.1 531.5zM296 224L296 361.9L303 368.9L375 440.9L408.9 407L343.9 342L343.9 223.9L295.9 223.9z"/></svg>
|
||||||
|
After Width: | Height: | Size: 921 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M320 128C443.7 128 544 228.3 544 352C544 402.3 527.4 448.8 499.4 486.2L557.2 544L511.9 589.3L454.1 531.5C416.8 559.4 370.3 576 320 576C269.7 576 223.2 559.4 185.8 531.4L128 589.3L82.8 544L140.6 486.2C112.6 448.8 96 402.3 96 352C96 228.3 196.3 128 320 128zM292 420L292 476L348 476L348 420L292 420zM288 224L300.8 384L339.2 384L352 224L288 224zM159.2 64C187.2 64 212.4 76.1 229.8 95.3C167.7 117.1 115.9 160.9 83.7 217.2C71.3 201.2 64 181.1 64 159.2C64 106.6 106.6 64 159.2 64zM480.7 64C533.3 64 576 106.6 576 159.2C576 181 568.7 201.1 556.3 217.2C524.1 160.8 472.3 117.1 410.1 95.3C427.5 76.1 452.7 64 480.7 64z"/></svg>
|
||||||
|
After Width: | Height: | Size: 876 B |
1
fontawesomekit/svgs-full/sharp-solid/alarm-minus.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M320 128C443.7 128 544 228.3 544 352C544 402.3 527.4 448.8 499.4 486.2L557.2 544L511.9 589.3L454.1 531.5C416.8 559.4 370.3 576 320 576C269.7 576 223.2 559.4 185.8 531.4L128 589.3L82.8 544L140.6 486.2C112.6 448.8 96 402.3 96 352C96 228.3 196.3 128 320 128zM224 328L224 376L416 376L416 328L224 328zM159.2 64C187.2 64 212.4 76.1 229.8 95.3C167.7 117.1 115.9 160.9 83.7 217.2C71.3 201.2 64 181.1 64 159.2C64 106.6 106.6 64 159.2 64zM480.7 64C533.3 64 576 106.6 576 159.2C576 181 568.7 201.1 556.3 217.2C524.1 160.8 472.3 117.1 410.1 95.3C427.5 76.1 452.7 64 480.7 64z"/></svg>
|
||||||
|
After Width: | Height: | Size: 831 B |
1
fontawesomekit/svgs-full/sharp-solid/alarm-plus.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M556.3 217.2C524.1 160.8 472.3 117.1 410.2 95.3C427.6 76.1 452.8 64 480.8 64C533.4 64 576 106.6 576 159.2C576 181 568.7 201.1 556.3 217.2zM83.7 217.2C71.3 201.2 64 181.1 64 159.2C64 106.6 106.6 64 159.2 64C187.2 64 212.4 76.1 229.8 95.3C167.7 117.1 115.9 160.9 83.7 217.2zM454.2 531.4C416.8 559.4 370.3 576 320 576C269.7 576 223.2 559.4 185.9 531.4C160.3 557 141 576.3 128 589.3L82.7 544C95.7 531 115 511.7 140.6 486.2C112.6 448.8 96 402.3 96 352C96 228.3 196.3 128 320 128C443.7 128 544 228.3 544 352C544 402.3 527.4 448.8 499.4 486.2L557.2 544L511.9 589.3L454.1 531.5zM296 448L344 448L344 376L416 376L416 328L344 328L344 256L296 256L296 328L224 328L224 376L296 376L296 448z"/></svg>
|
||||||
|
After Width: | Height: | Size: 943 B |
1
fontawesomekit/svgs-full/sharp-solid/alarm-snooze.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M556.3 217.2C524.1 160.8 472.3 117.1 410.2 95.3C427.6 76.1 452.8 64 480.8 64C533.4 64 576 106.6 576 159.2C576 181 568.7 201.1 556.3 217.2zM83.7 217.2C71.3 201.2 64 181.1 64 159.2C64 106.6 106.6 64 159.2 64C187.2 64 212.4 76.1 229.8 95.3C167.7 117.1 115.9 160.9 83.7 217.2zM454.2 531.4C416.8 559.4 370.3 576 320 576C269.7 576 223.2 559.4 185.9 531.4C160.3 557 141 576.3 128 589.3L82.7 544C95.7 531 115 511.7 140.6 486.2C112.6 448.8 96 402.3 96 352C96 228.3 196.3 128 320 128C443.7 128 544 228.3 544 352C544 402.3 527.4 448.8 499.4 486.2L557.2 544L511.9 589.3L454.1 531.5zM264 256L240 256L240 304L319.2 304C272.3 388.4 245.7 436.3 239.2 448L400 448L400 400L320.8 400C367.7 315.6 394.3 267.7 400.8 256L264 256z"/></svg>
|
||||||
|
After Width: | Height: | Size: 975 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M64 96L512 96L512 278C496.7 274.1 480.6 272 464 272C456.2 272 448.6 272.5 441.1 273.4C421.1 207.8 360.1 160 288 160C199.6 160 128 231.6 128 320C128 403.2 191.5 471.5 272.6 479.3C274.4 502.3 280.2 524.1 289.4 544L64 544L64 96zM288 288C305.7 288 320 302.3 320 320C320 337.7 305.7 352 288 352C270.3 352 256 337.7 256 320C256 302.3 270.3 288 288 288zM464 320C543.5 320 608 384.5 608 464C608 543.5 543.5 608 464 608C384.5 608 320 543.5 320 464C320 384.5 384.5 320 464 320zM480 400L480 384L448 384L448 448L384 448L384 480L448 480L448 544L480 544L480 480L544 480L544 448L480 448L480 400z"/></svg>
|
||||||
|
After Width: | Height: | Size: 848 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M64 96L512 96L512 278C496.7 274.1 480.6 272 464 272C456.2 272 448.6 272.5 441.1 273.4C421.1 207.8 360.1 160 288 160C199.6 160 128 231.6 128 320C128 403.2 191.5 471.5 272.6 479.3C274.4 502.3 280.2 524.1 289.4 544L64 544L64 96zM288 288C305.7 288 320 302.3 320 320C320 337.7 305.7 352 288 352C270.3 352 256 337.7 256 320C256 302.3 270.3 288 288 288zM320 464C320 384.5 384.5 320 464 320C543.5 320 608 384.5 608 464C608 543.5 543.5 608 464 608C384.5 608 320 543.5 320 464zM543.5 542.9L528 512L400 512L384.5 542.9C404.8 563.4 432.9 576 464 576C495.1 576 523.2 563.4 543.5 542.9zM512 432C512 405.5 490.5 384 464 384C437.5 384 416 405.5 416 432C416 458.5 437.5 480 464 480C490.5 480 512 458.5 512 432z"/></svg>
|
||||||
|
After Width: | Height: | Size: 961 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M160 64L160 112L416 112L416 64L160 64zM96 256L40.7 256C41.9 264.3 55.2 357.3 80.5 534.8L86.4 576L308 576C297.5 561.4 289 545.2 282.9 527.9C196.9 526 128 476.6 128 416C128 354.1 199.6 304 288 304C308.8 304 328.7 306.8 346.9 311.8C379.3 286.8 419.9 272 464 272C487.7 272 510.4 276.3 531.3 284.1L535.3 256L96 256zM273.2 442C275.4 423 280.3 404.9 287.6 388C265.7 388.1 248 400.6 248 416C248 427.8 258.5 437.9 273.2 442zM136 160L112 160L112 208L464 208L464 160L136 160zM464 608C543.5 608 608 543.5 608 464C608 384.5 543.5 320 464 320C384.5 320 320 384.5 320 464C320 543.5 384.5 608 464 608zM480 400L480 448L544 448L544 480L480 480L480 544L448 544L448 480L384 480L384 448L448 448L448 384L480 384L480 400z"/></svg>
|
||||||
|
After Width: | Height: | Size: 966 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M160 64L160 112L416 112L416 64L160 64zM96 256L40.7 256C41.9 264.3 55.2 357.3 80.5 534.8L86.4 576L308 576C297.5 561.4 289 545.2 282.9 527.9C196.9 526 128 476.6 128 416C128 354.1 199.6 304 288 304C308.8 304 328.7 306.8 346.9 311.8C379.3 286.8 419.9 272 464 272C487.7 272 510.4 276.3 531.3 284.1L535.3 256L96 256zM273.2 442C275.4 423 280.3 404.9 287.6 388C265.7 388.1 248 400.6 248 416C248 427.8 258.5 437.9 273.2 442zM136 160L112 160L112 208L464 208L464 160L136 160zM608 464C608 384.5 543.5 320 464 320C384.5 320 320 384.5 320 464C320 543.5 384.5 608 464 608C543.5 608 608 543.5 608 464zM543.5 542.9C523.2 563.4 495.1 576 464 576C432.9 576 404.8 563.4 384.5 542.9L400 512L528 512L543.5 542.9zM416 432C416 405.5 437.5 384 464 384C490.5 384 512 405.5 512 432C512 458.5 490.5 480 464 480C437.5 480 416 458.5 416 432z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M192.3 64L192.3 112L448.3 112L448.3 64L192.3 64zM128.3 256L73 256C74.2 264.3 87.5 357.3 112.8 534.8L118.7 576L522 576L527.9 534.8C553.3 357.2 566.5 264.3 567.7 256L128.3 256zM480.3 416C480.3 477.9 408.7 528 320.3 528C231.9 528 160.3 477.9 160.3 416C160.3 354.1 231.9 304 320.3 304C408.7 304 480.3 354.1 480.3 416zM320.3 444C342.4 444 360.3 431.5 360.3 416C360.3 400.5 342.4 388 320.3 388C298.2 388 280.3 400.5 280.3 416C280.3 431.5 298.2 444 320.3 444zM168.3 160L144.3 160L144.3 208L496.3 208L496.3 160L168.3 160z"/></svg>
|
||||||
|
After Width: | Height: | Size: 781 B |
1
fontawesomekit/svgs-full/sharp-solid/album.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M96 96L544 96L544 544L96 544L96 96zM480 320C480 231.6 408.4 160 320 160C231.6 160 160 231.6 160 320C160 408.4 231.6 480 320 480C408.4 480 480 408.4 480 320zM320 288C337.7 288 352 302.3 352 320C352 337.7 337.7 352 320 352C302.3 352 288 337.7 288 320C288 302.3 302.3 288 320 288z"/></svg>
|
||||||
|
After Width: | Height: | Size: 545 B |
1
fontawesomekit/svgs-full/sharp-solid/alicorn.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M448 224L448 304L448 304C447.5 334 435.2 360.4 416 379.7L416 576L320 576L320 410.7C315.9 410 311.8 409.1 307.7 407.9L224 384L224 419.9C216.8 427.1 203.1 440.8 182.9 461L221.2 576L120 576C90.2 486.7 74.6 439.7 73 435C86.1 421.9 101.9 406.1 120.6 387.4L78.2 323.9C70.9 312.9 66.3 300.5 64.6 287.5C54.5 294.8 47.9 306.6 47.9 320L47.9 400L-.1 400L-.1 320C-.1 275.4 33.1 238.5 76.2 232.8C89.2 211.4 111.4 196.3 137.3 192.8C141.1 208.1 146.5 222.6 153.8 235.8C165.1 256.2 181.2 273.5 203.7 285.6C211.2 289.6 219.2 293 227.9 295.7C139.6 237.7 159.8 96 159.8 96L319.8 192C320 121.3 377.3 64 448 64L560 64L560 96L528 96L544 112L640 112L640 128L560 154.7L560 256L480 288L448 224zM512 144C512 135.2 504.8 128 496 128C487.2 128 480 135.2 480 144C480 152.8 487.2 160 496 160C504.8 160 512 152.8 512 144z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
1
fontawesomekit/svgs-full/sharp-solid/alien-8bit.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M128 96L192 96L192 128L256 128L256 192L384 192L384 128L448 128L448 96L512 96L512 160L448 160L448 224L512 224L512 288L544 288L544 192L608 192L608 352L544 352L544 448L480 448L480 544L352 544L352 480L416 480L416 448L224 448L224 480L288 480L288 544L160 544L160 448L96 448L96 352L32 352L32 192L96 192L96 288L128 288L128 224L192 224L192 160L128 160L128 96zM192 384L256 384L256 288L192 288L192 384zM384 384L448 384L448 288L384 288L384 384z"/></svg>
|
||||||
|
After Width: | Height: | Size: 700 B |
1
fontawesomekit/svgs-full/sharp-solid/alien-monster.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M128 96L192 96L192 128L256 128L256 192L384 192L384 128L448 128L448 96L512 96L512 160L448 160L448 224L512 224L512 288L544 288L544 192L608 192L608 352L544 352L544 448L480 448L480 544L352 544L352 480L416 480L416 448L224 448L224 480L288 480L288 544L160 544L160 448L96 448L96 352L32 352L32 192L96 192L96 288L128 288L128 224L192 224L192 160L128 160L128 96zM192 384L256 384L256 288L192 288L192 384zM384 384L448 384L448 288L384 288L384 384z"/></svg>
|
||||||
|
After Width: | Height: | Size: 700 B |
1
fontawesomekit/svgs-full/sharp-solid/alien.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M320 64C196.3 64 96 164.3 96 288C96 464 320 576 320 576C320 576 544 464 544 288C544 164.3 443.7 64 320 64zM352 368C352 323.8 387.8 288 432 288L480 288L480 304C480 348.2 444.2 384 400 384L352 384L352 368zM208 288C252.2 288 288 323.8 288 368L288 384L240 384C195.8 384 160 348.2 160 304L160 288L208 288z"/></svg>
|
||||||
|
After Width: | Height: | Size: 568 B |
1
fontawesomekit/svgs-full/sharp-solid/align-center.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M448 96L192 96L192 160L448 160L448 96zM544 224L96 224L96 288L544 288L544 224zM96 480L96 544L544 544L544 480L96 480zM448 352L192 352L192 416L448 416L448 352z"/></svg>
|
||||||
|
After Width: | Height: | Size: 424 B |
1
fontawesomekit/svgs-full/sharp-solid/align-justify.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M544 96L96 96L96 160L544 160L544 96zM544 352L96 352L96 416L544 416L544 352zM96 224L96 288L544 288L544 224L96 224zM544 480L96 480L96 544L544 544L544 480z"/></svg>
|
||||||
|
After Width: | Height: | Size: 420 B |
1
fontawesomekit/svgs-full/sharp-solid/align-left.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M384 96L384 160L96 160L96 96L384 96zM384 352L384 416L96 416L96 352L384 352zM96 224L544 224L544 288L96 288L96 224zM544 480L544 544L96 544L96 480L544 480z"/></svg>
|
||||||
|
After Width: | Height: | Size: 420 B |
1
fontawesomekit/svgs-full/sharp-solid/align-right.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M544 96L544 160L256 160L256 96L544 96zM544 352L544 416L256 416L256 352L544 352zM96 224L544 224L544 288L96 288L96 224zM544 480L544 544L96 544L96 480L544 480z"/></svg>
|
||||||
|
After Width: | Height: | Size: 424 B |
1
fontawesomekit/svgs-full/sharp-solid/align-slash.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M81.3 47.1L64.3 30.1L30.4 64L47.4 81L559.4 593L576.4 610L610.3 576.1L593.3 559.1L450.2 416L544.2 416L544.2 352L386.2 352L322.2 288L544.2 288L544.2 224L258.2 224L194.2 160L544.2 160L544.2 96L130.2 96L81.3 47.1zM442.3 544L378.3 480L96.1 480L96.1 544L442.3 544zM250.3 352L96.1 352L96.1 416L314.3 416L250.3 352zM122.3 224L96.1 224L96.1 288L186.3 288L122.3 224z"/></svg>
|
||||||
|
After Width: | Height: | Size: 624 B |
1
fontawesomekit/svgs-full/sharp-solid/allergies.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M352.2 96L352.2 64L288.2 64L288.2 320L256.2 320L256.2 96L192.2 96L192.2 400C192.2 401.5 192.2 403.1 192.3 404.6C160.8 374.6 136.6 351.6 119.7 335.5L64.5 393.4C72.7 401.2 114.2 440.8 189 512C232.1 553.1 289.4 576 349 576L368.2 576C465.4 576 544.2 497.2 544.2 400L544.2 160L480.2 160L480.2 320L448.2 320L448.2 96L384.2 96L384.2 320L352.2 320L352.2 96zM264.2 416C264.2 402.7 274.9 392 288.2 392C301.5 392 312.2 402.7 312.2 416C312.2 429.3 301.5 440 288.2 440C274.9 440 264.2 429.3 264.2 416zM384.2 360C397.5 360 408.2 370.7 408.2 384C408.2 397.3 397.5 408 384.2 408C370.9 408 360.2 397.3 360.2 384C360.2 370.7 370.9 360 384.2 360zM424.2 448C424.2 434.7 434.9 424 448.2 424C461.5 424 472.2 434.7 472.2 448C472.2 461.3 461.5 472 448.2 472C434.9 472 424.2 461.3 424.2 448zM336.2 472C349.5 472 360.2 482.7 360.2 496C360.2 509.3 349.5 520 336.2 520C322.9 520 312.2 509.3 312.2 496C312.2 482.7 322.9 472 336.2 472z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
fontawesomekit/svgs-full/sharp-solid/almost-equal-to.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M542.2 397.2C530 445.9 486.3 480 436.1 480C425.2 480 414.4 478.4 403.9 475.2L218.2 418C213.9 416.7 209.4 416 204.9 416C184.1 416 166 430.2 160.9 450.3L153.5 480L87.6 480L98.9 434.8C111.1 386.1 154.8 352 205 352C215.9 352 226.7 353.6 237.2 356.8L422.8 414C427.1 415.3 431.6 416 436.1 416C456.9 416 475 401.9 480.1 381.7L487.5 352L553.4 352L542.1 397.2zM542.2 205.2C530 253.9 486.3 288 436.1 288C425.2 288 414.4 286.4 403.9 283.2L218.2 226C213.9 224.7 209.4 224 204.9 224C184.1 224 166 238.2 160.9 258.3L153.5 288L87.6 288L98.9 242.8C111.1 194.1 154.8 160 205 160C215.9 160 226.7 161.6 237.2 164.8L422.8 222C427.1 223.3 431.6 224 436.1 224C456.9 224 475 209.9 480.1 189.7L487.5 160L553.4 160L542.1 205.2z"/></svg>
|
||||||
|
After Width: | Height: | Size: 970 B |
1
fontawesomekit/svgs-full/sharp-solid/alt.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M448 128L432.6 128L423 140L176.6 448L0 448L0 512L207.4 512L217 500L463.4 192L640 192L640 128L448 128zM448 448L416 448L416 512L640 512L640 448L448 448z"/></svg>
|
||||||
|
After Width: | Height: | Size: 418 B |
1
fontawesomekit/svgs-full/sharp-solid/ambulance.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M448 96L32 96L32 512L99.3 512C109.7 548.9 143.7 576 184 576C224.3 576 258.2 548.9 268.7 512L371.3 512C381.7 548.9 415.7 576 456 576C496.3 576 530.2 548.9 540.7 512L608 512L608 274.7L598.6 265.3L534.6 201.3L525.2 191.9L447.9 191.9L447.9 95.9zM544 301.3L544 352L448 352L448 256L498.7 256L544 301.3zM184 448C206.1 448 224 465.9 224 488C224 510.1 206.1 528 184 528C161.9 528 144 510.1 144 488C144 465.9 161.9 448 184 448zM416 488C416 465.9 433.9 448 456 448C478.1 448 496 465.9 496 488C496 510.1 478.1 528 456 528C433.9 528 416 510.1 416 488zM208 184L272 184L272 240L328 240L328 304L272 304L272 360L208 360L208 304L152 304L152 240L208 240L208 184z"/></svg>
|
||||||
|
After Width: | Height: | Size: 911 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M260.5 133.9L285.1 113.4L244.1 64.2C237.9 69.3 201.1 100 133.6 156.3L162.9 97.7L105.7 69.1L91.4 97.7L13.5 253.4C4.6 271.2 0 290.8 0 310.7L0 320C0 390.7 57.3 448 128 448L208 448L208 447.7C254.2 444.2 291.8 410.6 301.6 366.4L239.1 352.6C235.1 370.5 219.1 383.9 200 383.9C177.9 383.9 160 366 160 343.9C160 321.8 177.9 303.9 200 303.9C219.1 303.9 235.1 317.3 239.1 335.2L301.6 321.4C293.6 285.1 266.7 255.9 231.7 244.8C280.5 231.5 312.4 222.8 327.3 218.7L310.5 157.1C298.9 160.3 260.4 170.8 194.9 188.6L260.5 133.9zM378.8 506.3L354.2 526.8L395.2 576C401.4 570.9 438.2 540.2 505.7 483.9L476.4 542.5L533.6 571.1L547.9 542.5L625.8 386.8C634.7 369 639.3 349.4 639.3 329.6L639.3 320.3C639.3 249.6 582 192.3 511.3 192.3L431.3 192.3L431.3 192.6C385.1 196.1 347.5 229.7 337.7 273.8L400.2 287.6C404.2 269.7 420.2 256.3 439.3 256.3C461.4 256.3 479.3 274.2 479.3 296.3C479.3 318.4 461.4 336.3 439.3 336.3C420.2 336.3 404.2 322.9 400.2 305L337.7 318.8C345.7 355.1 372.6 384.3 407.6 395.4C358.8 408.7 326.9 417.4 312 421.5L328.8 483.2C340.4 480 378.9 469.5 444.4 451.7L378.8 506.4z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
1
fontawesomekit/svgs-full/sharp-solid/amp-guitar.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M184 64L160 64L160 160L64 160L64 544L576 544L576 160L480 160L480 64L184 64zM432 160L208 160L208 112L432 112L432 160zM136 256C136 242.7 146.7 232 160 232C173.3 232 184 242.7 184 256C184 269.3 173.3 280 160 280C146.7 280 136 269.3 136 256zM256 232C269.3 232 280 242.7 280 256C280 269.3 269.3 280 256 280C242.7 280 232 269.3 232 256C232 242.7 242.7 232 256 232zM360 256C360 242.7 370.7 232 384 232C397.3 232 408 242.7 408 256C408 269.3 397.3 280 384 280C370.7 280 360 269.3 360 256zM480 232C493.3 232 504 242.7 504 256C504 269.3 493.3 280 480 280C466.7 280 456 269.3 456 256C456 242.7 466.7 232 480 232zM464 368L176 368L176 432L464 432L464 368zM176 320L512 320L512 480L128 480L128 320L176 320z"/></svg>
|
||||||
|
After Width: | Height: | Size: 958 B |
1
fontawesomekit/svgs-full/sharp-solid/ampersand.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M240.2 176.7C240.2 188.9 244.7 200.8 252.7 209.7L280.3 240.6L316.7 214C329 205 336.2 190.8 336.2 175.6C336.2 149.4 314.8 128 288.4 128C262.2 128 240.2 150 240.2 176.7zM354.5 265.6L323.2 288.4L411.3 386.9C446.9 354.8 470.7 333.4 482.6 322.7L525.4 370.3C513.5 381 489.7 402.4 454 434.6C492.2 477.3 516 503.9 525.4 514.4L477.7 557.1C468.2 546.5 444.5 520 406.4 477.4L341.6 535.7L332.5 543.9L256.2 543.9C186 543.9 128.2 487 128.2 416.4C128.2 375.7 147.6 337.5 180.5 313.5L228.4 278.5L205 252.4C186.4 231.6 176.2 204.4 176.2 176.7C176.2 115.1 226.3 64 288.4 64C349.9 64 400.2 113.9 400.2 175.6C400.2 211.2 383.2 244.7 354.5 265.7zM256.2 480L307.9 480L363.7 429.8L271.3 326.5L218.3 365.2C201.9 377.2 192.3 396.2 192.3 416.5C192.3 451.4 221 480 256.3 480z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1016 B |
1
fontawesomekit/svgs-full/sharp-solid/analytics.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M564.5 153L589.5 133L549.5 83L524.5 103L383.9 215.5L275.7 134.4L255.9 119.5L236.5 135L76.5 263L51.5 283L91.5 333L116.5 313L257.1 200.5L365.3 281.6L385.1 296.5L404.5 281L564.5 153zM224.5 288L224.5 544L288.5 544L288.5 288L224.5 288zM96.5 384L96.5 544L160.5 544L160.5 384L96.5 384zM416.5 352L352.5 352L352.5 544L416.5 544L416.5 352zM480.5 288L480.5 544L544.5 544L544.5 288L480.5 288z"/></svg>
|
||||||
|
After Width: | Height: | Size: 648 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--! Font Awesome Pro 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc. --><path fill="currentColor" d="M320 160C320 177.7 305.7 192 288 192C270.3 192 256 177.7 256 160C256 142.3 270.3 128 288 128C305.7 128 320 142.3 320 160zM288 64C235 64 192 107 192 160C192 201.8 218.7 237.4 256 250.5L256 512L144 512L144 371.9C154.5 381.1 165.9 391.1 178.3 401.9L209.9 365.8C205.5 362 178.2 338 127.8 294L112 280.1C109.5 282.3 82.9 305.6 32.2 349.9L14.1 365.7L45.7 401.8C58.1 391 69.5 381 80 371.8L80 575.9L340 575.9C317.3 544.4 304 505.7 304 463.9C304 436.6 309.7 410.6 320 387L320 250.4C357.3 237.2 384 201.7 384 159.9C384 106.9 341 63.9 288 63.9zM640 464C640 384.5 575.5 320 496 320C416.5 320 352 384.5 352 464C352 543.5 416.5 608 496 608C575.5 608 640 543.5 640 464zM566.3 412.5L556.9 425.4L492.9 513.4L481.9 528.5C477.5 524.1 459.8 506.4 428.7 475.3L417.4 464L440 441.4C445 446.4 457.7 459.1 478.1 479.5L531.1 406.6L540.5 393.7L566.4 412.5z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |