From e6c0baef3b10ce37685fbff372bf757e07075184 Mon Sep 17 00:00:00 2001 From: John OReilly Date: Mon, 16 Mar 2026 01:42:17 -0400 Subject: [PATCH] Pre-Alpha to Alpha Ready --- SVS-MSP-Calculator-components.css | 297 ++- SVS-MSP-Calculator-glass.css | 56 +- SVS-MSP-Calculator-layout.css | 6 +- SVS-MSP-Calculator-light.css | 32 +- SVS-MSP-Calculator-print.css | 3 + SVS-MSP-Calculator-responsive.css | 16 +- SVS-MSP-Calculator-tokens.css | 60 +- SVS-MSP-Calculator.html | 8 +- SVS-MSP-Calculator.js | 14 + docs/QUICK-REF.md | 154 +- docs/SESSION-HANDOFF.md | 56 + docs/STAGE11-SESSION-PROMPT.md | 100 + pre-alpha/M365icons/azure-svgrepo-com.svg | 2 + pre-alpha/M365icons/excel.svg | 2 + pre-alpha/M365icons/excel2-svgrepo-com.svg | 2 + pre-alpha/M365icons/outlook-svgrepo-com.svg | 2 + pre-alpha/M365icons/powerpoint.svg | 2 + pre-alpha/M365icons/teams.svg | 21 + pre-alpha/M365icons/word.svg | 2 + .../SVS-MSP-Calculator-70retro.css | 84 +- pre-alpha/SVS-MSP-Calculator-base.css | 73 + pre-alpha/SVS-MSP-Calculator-components.css | 2123 +++++++++++++++++ pre-alpha/SVS-MSP-Calculator-glass.css | 623 +++++ pre-alpha/SVS-MSP-Calculator-layout.css | 183 ++ pre-alpha/SVS-MSP-Calculator-light.css | 129 + pre-alpha/SVS-MSP-Calculator-print.css | 158 ++ pre-alpha/SVS-MSP-Calculator-responsive.css | 436 ++++ pre-alpha/SVS-MSP-Calculator-tokens.css | 264 ++ pre-alpha/SVS-MSP-Calculator.css | 10 + pre-alpha/SVS-MSP-Calculator.html | 1012 ++++++++ pre-alpha/SVS-MSP-Calculator.js | 393 +++ pre-alpha/docs/CHECKPOINT.md | 400 ++++ pre-alpha/docs/MASTER-SESSION-PROMPT.md | 416 ++++ pre-alpha/docs/QUICK-REF.md | 108 + pre-alpha/docs/README.md | 62 + pre-alpha/docs/STAGE10-UI-SESSION-PROMPT.md | 89 + pre-alpha/docs/STAGE11-SESSION-PROMPT.md | 100 + pre-alpha/docs/STAGE3-SESSION-PROMPT.md | 176 ++ pre-alpha/docs/STAGE4-SESSION-PROMPT.md | 186 ++ pre-alpha/docs/STAGE5-SESSION-PROMPT.md | 193 ++ pre-alpha/docs/STAGE6-SESSION-PROMPT.md | 162 ++ pre-alpha/docs/STAGE7-SESSION-PROMPT.md | 166 ++ pre-alpha/docs/STAGE9-SESSION-PROMPT.md | 167 ++ pre-alpha/docs/ai-session-brief.md | 65 + pre-alpha/docs/code-verification.md | 124 + pre-alpha/docs/phase-roadmap.md | 50 + pre-alpha/docs/quote-rules.md | 96 + pre-alpha/docs/regression-checklist.md | 104 + pre-alpha/mobile-sync.js | 283 +++ pre-alpha/package-prices-data.js | 66 + pre-alpha/quote-engine.js | 197 ++ pre-alpha/quote-export.js | 342 +++ pre-alpha/quote-import.js | 168 ++ pre-alpha/quote-persistence.js | 240 ++ pre-alpha/quote-pricing.js | 125 + pre-alpha/quote-render.js | 759 ++++++ pre-alpha/tests/test-quote-engine.js | 1148 +++++++++ pre-alpha/theme-manager.js | 121 + quote-render.js | 73 +- theme-manager.js | 8 +- 60 files changed, 12287 insertions(+), 230 deletions(-) create mode 100644 docs/SESSION-HANDOFF.md create mode 100644 docs/STAGE11-SESSION-PROMPT.md create mode 100644 pre-alpha/M365icons/azure-svgrepo-com.svg create mode 100644 pre-alpha/M365icons/excel.svg create mode 100644 pre-alpha/M365icons/excel2-svgrepo-com.svg create mode 100644 pre-alpha/M365icons/outlook-svgrepo-com.svg create mode 100644 pre-alpha/M365icons/powerpoint.svg create mode 100644 pre-alpha/M365icons/teams.svg create mode 100644 pre-alpha/M365icons/word.svg rename SVS-MSP-Calculator-70retro.css => pre-alpha/SVS-MSP-Calculator-70retro.css (81%) create mode 100644 pre-alpha/SVS-MSP-Calculator-base.css create mode 100644 pre-alpha/SVS-MSP-Calculator-components.css create mode 100644 pre-alpha/SVS-MSP-Calculator-glass.css create mode 100644 pre-alpha/SVS-MSP-Calculator-layout.css create mode 100644 pre-alpha/SVS-MSP-Calculator-light.css create mode 100644 pre-alpha/SVS-MSP-Calculator-print.css create mode 100644 pre-alpha/SVS-MSP-Calculator-responsive.css create mode 100644 pre-alpha/SVS-MSP-Calculator-tokens.css create mode 100644 pre-alpha/SVS-MSP-Calculator.css create mode 100644 pre-alpha/SVS-MSP-Calculator.html create mode 100644 pre-alpha/SVS-MSP-Calculator.js create mode 100644 pre-alpha/docs/CHECKPOINT.md create mode 100644 pre-alpha/docs/MASTER-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/QUICK-REF.md create mode 100644 pre-alpha/docs/README.md create mode 100644 pre-alpha/docs/STAGE10-UI-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/STAGE11-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/STAGE3-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/STAGE4-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/STAGE5-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/STAGE6-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/STAGE7-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/STAGE9-SESSION-PROMPT.md create mode 100644 pre-alpha/docs/ai-session-brief.md create mode 100644 pre-alpha/docs/code-verification.md create mode 100644 pre-alpha/docs/phase-roadmap.md create mode 100644 pre-alpha/docs/quote-rules.md create mode 100644 pre-alpha/docs/regression-checklist.md create mode 100644 pre-alpha/mobile-sync.js create mode 100644 pre-alpha/package-prices-data.js create mode 100644 pre-alpha/quote-engine.js create mode 100644 pre-alpha/quote-export.js create mode 100644 pre-alpha/quote-import.js create mode 100644 pre-alpha/quote-persistence.js create mode 100644 pre-alpha/quote-pricing.js create mode 100644 pre-alpha/quote-render.js create mode 100644 pre-alpha/tests/test-quote-engine.js create mode 100644 pre-alpha/theme-manager.js diff --git a/SVS-MSP-Calculator-components.css b/SVS-MSP-Calculator-components.css index 1d80d5d..b05dfe0 100644 --- a/SVS-MSP-Calculator-components.css +++ b/SVS-MSP-Calculator-components.css @@ -15,6 +15,7 @@ margin-left: var(--section-offset); border-radius: var(--radius-card); border: 1px solid var(--border); + border-left: 3px solid transparent; background: var(--surface-section); padding: var(--section-padding-top) var(--section-padding-x) var(--section-padding-bottom); } @@ -39,7 +40,7 @@ .fa-icon--green { color: var(--green); } .fa-icon--amber { color: var(--amber); } .fa-icon--danger { color: var(--text-danger); } - .fa-icon--white { color: #fff; } + .fa-icon--white { color: var(--text-on-accent); } .fa-icon--theme { color: currentColor; } /* ── FA Sharp Solid icons — inline data URIs for file:// compatibility ── */ .fa-icon-file-invoice { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath fill='currentColor' d='M240 0L0 0 0 512 384 512 384 144 240 0zm85.5 176L208 176 208 58.5 325.5 176zM64 416l0-128 256 0 0 128-256 0zM88 64l72 0 0 48-96 0 0-48 24 0zm0 96l72 0 0 48-96 0 0-48 24 0z'/%3E%3C/svg%3E"); } @@ -85,6 +86,58 @@ #sec-04 { order: 4; } #sec-05 { order: 5; } #sec-06 { order: 6; } + + /* ── GROUP LABEL — "Managed IT Services" eyebrow above section I ── */ + .group-label { + order: 0; + margin-left: var(--section-offset); + margin-bottom: calc(var(--space-xs) * -1); + font-family: 'DM Mono', monospace; + font-size: 0.6875rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--accent); + opacity: 0.7; + } + .group-label-sections { + opacity: 0.6; + letter-spacing: 0.06em; + } + + /* ── GROUP DIVIDER — separates Server Managed from Site Management ── */ + .group-divider { + order: 3; + border: none; + border-top: 1px solid var(--border); + margin: var(--space-xs) var(--section-offset) var(--space-xs) var(--section-offset); + opacity: 0.5; + } + + /* ── GROUP STRIP — bracket alongside sections I–III ── */ + #sec-02::after, #sec-03::after, #sec-01::after { + content: ''; + position: absolute; + left: calc(var(--section-offset) * -1 + 2px); + top: -12px; + bottom: -12px; + width: 6px; + border-left: 3px solid var(--group-strip); + pointer-events: none; + z-index: 0; + } + /* Top cap — wraps above first section */ + #sec-02::after { + top: -12px; + border-top: 3px solid var(--group-strip); + border-top-left-radius: 4px; + } + /* Bottom cap — wraps below last section */ + #sec-01::after { + bottom: -12px; + border-bottom: 3px solid var(--group-strip); + border-bottom-left-radius: 4px; + } + .section-header { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; @@ -111,6 +164,11 @@ left: calc(var(--section-offset) * -1); top: calc(var(--section-padding-top) - 2px); text-align: right; + transition: color var(--transition-medium, 0.2s) ease, opacity var(--transition-medium, 0.2s) ease; + } + .sec-active .section-num { + color: var(--accent); + opacity: 0.9; } .section-title-block { min-width: 0; @@ -141,7 +199,7 @@ .section-title { font-family: 'Poppins', sans-serif; font-size: clamp(1.375rem, 1.5vw, 1.75rem); - font-weight: 600; + font-weight: 700; color: var(--ink); line-height: var(--text-title-line); word-break: break-word; @@ -152,7 +210,8 @@ .section-subtitle { grid-column: 1 / -1; grid-row: 3; - font-size: var(--text-copy-size); + font-size: 0.9em; + font-weight: 400; color: var(--muted); margin-top: var(--space-xs); line-height: var(--text-copy-line); @@ -198,7 +257,7 @@ will-change: height, opacity; } .section-content { min-width: 0; } - .section-content > * + * { margin-top: var(--space-stack); } + .section-content > * + * { margin-top: var(--space-stack-roomy); } .section-content > .collapsible-header + .collapsible-body, .section-content > .collapsible-body + .collapsible-header { margin-top: 0; @@ -278,20 +337,22 @@ display: none; align-items: center; font-family: 'DM Mono', monospace; - font-size: 13px; - font-weight: 500; + font-size: 0.9375rem; + font-weight: 700; + font-variant-numeric: tabular-nums; letter-spacing: 0.08em; color: var(--accent); background: var(--surface-summary-badge); border: 2px solid var(--border-summary-badge); - border-radius: var(--radius-control); - padding: var(--control-pad-y-tight) var(--space-md); + border-radius: 8px; + padding: 8px 16px; white-space: normal; line-height: var(--text-compact-line); max-width: min(100%, 26ch); margin-top: var(--space-md); text-align: left; align-self: flex-start; + box-shadow: 0 1px 4px rgba(45, 122, 168, 0.2); animation: badgeFadeIn 0.25s ease both; } .sec-open .sec-summary-badge { @@ -507,24 +568,36 @@ Separate from section-level collapse. JS toggleCollapsible(id) toggles .open on .collapsible-body and swaps +/- on toggle icon. .addon-preview-pill pills shown when collapsed (JS toggleCollapsible). + Arrow on RIGHT side to differentiate from section chevron. ─────────────────────────────────────────────────────────────── */ .collapsible-header { display: flex; align-items: center; gap: var(--space-sm); cursor: pointer; - padding: var(--space-md) 0; - border-top: 1px solid var(--border); + padding: var(--space-stack) var(--space-md); + border: 1px solid var(--border); + border-radius: 8px; + margin-top: var(--space-sm); + background: var(--surface-feature); user-select: none; + transition: background var(--transition-fast), border-color var(--transition-fast); + } + .collapsible-header:hover { + background: color-mix(in srgb, var(--accent) 6%, var(--surface-feature)); + border-color: color-mix(in srgb, var(--accent) 15%, var(--border)); } .collapsible-header--mt16 { margin-top: var(--space-stack-roomy); } - .collapsible-header--addon { flex-wrap: wrap; gap: var(--space-xs); margin-top: 0; } + .collapsible-header--addon { flex-wrap: wrap; gap: var(--space-xs); } .collapsible-toggle { color: var(--accent); width: 22px; flex-shrink: 0; display: flex; align-items: center; + justify-content: center; + order: 1; + margin-left: auto; transition: transform var(--transition-medium) ease; } .collapsible-toggle.open { transform: rotate(180deg); } @@ -532,17 +605,20 @@ .collapsible-label { font-family: 'DM Mono', monospace; font-size: 13px; + font-weight: 500; text-transform: uppercase; - letter-spacing: 0.07em; - color: var(--muted); + letter-spacing: 0.08em; + color: var(--ink); + order: 0; } .addon-preview-wrap { display: flex; flex-wrap: wrap; gap: 5px; - width: 100%; padding-left: var(--space-3xl); margin-top: 6px; + width: 100%; padding-left: 0; margin-top: 6px; + order: 2; } .addon-preview-pill { font-family: 'DM Mono', monospace; - font-size: 13px; + font-size: 12px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted); @@ -619,21 +695,22 @@ background: var(--surface-step); border: 1px solid var(--surface-step-border); color: var(--text-step); - font-size: 20px; + font-size: 1.125rem; font-weight: 400; - width: 36px; + min-width: 44px; + min-height: 44px; cursor: pointer; display: flex; align-items: center; justify-content: center; - transition: background 0.12s, color 0.12s; + transition: background 0.12s, color 0.12s, border-color var(--transition-fast, 150ms) ease; flex-shrink: 0; user-select: none; line-height: 1; } .step-btn:first-child { border-radius: var(--radius-control) 0 0 var(--radius-control); border-right: none; } .step-btn:last-child { border-radius: 0 var(--radius-control) var(--radius-control) 0; border-left: none; } - .step-btn:hover { background: var(--surface-step-hover); color: var(--ink); } + .step-btn:hover { background: var(--surface-step-hover); color: var(--ink); border-color: var(--accent); } .step-btn:active { background: var(--surface-step-active); color: var(--btn-primary-fg); border-color: var(--accent); } .num-input { background: var(--surface-input); @@ -641,8 +718,10 @@ border-radius: 0; color: var(--ink); font-family: 'DM Mono', monospace; - font-size: 22px; - width: 72px; + font-size: 1.125rem; + font-weight: 700; + width: 56px; + height: 44px; text-align: center; padding: var(--space-sm); outline: none; @@ -699,7 +778,7 @@ top: 1px; width: 5px; height: 10px; - border: solid #fff; + border: solid var(--text-on-accent); border-width: 0 2px 2px 0; transform: rotate(45deg); } @@ -861,43 +940,50 @@ .m365-app-strip { border: 1px solid var(--border); - border-radius: 10px; - padding: var(--space-stack-roomy) var(--space-lg) var(--space-stack); + border-radius: var(--radius-card); + padding: var(--space-lg) var(--space-lg) var(--space-stack); background: var(--surface-feature); } .m365-app-list { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); - gap: var(--space-md); + gap: var(--space-stack); } .m365-app-item { display: flex; flex-direction: column; align-items: center; justify-content: center; - gap: var(--space-sm); + gap: 8px; text-align: center; - padding: var(--space-stack-tight) var(--space-sm); - border-radius: var(--radius-control); - background: color-mix(in srgb, var(--surface-accent-soft) 44%, transparent); + padding: var(--space-stack) var(--space-sm); + border-radius: 10px; + transition: background var(--transition-fast), transform var(--transition-fast); + } + .m365-app-item:hover { + background: color-mix(in srgb, var(--accent) 8%, transparent); + transform: translateY(-1px); } .m365-app-icon { - width: 17px; - height: 17px; + width: 28px; + height: 28px; object-fit: contain; display: block; } .m365-app-name { font-family: 'DM Mono', monospace; - font-size: 0.75rem; - letter-spacing: 0.05em; + font-size: 0.6875rem; + font-weight: 500; + letter-spacing: 0.06em; color: var(--ink); + opacity: 0.85; } .m365-app-strip-note { margin-top: var(--space-md); + padding-top: var(--space-sm); font-family: 'DM Mono', monospace; - font-size: 0.75rem; - letter-spacing: 0.05em; + font-size: 0.6875rem; + letter-spacing: 0.06em; color: var(--muted); } .m365-app-note-byol { display: none; } @@ -929,7 +1015,7 @@ with _m IDs and synced by update() via syncEl/syncClass. ─────────────────────────────────────────────────────────────── */ .sidebar { - --sidebar-rule-color: color-mix(in srgb, var(--border) 88%, transparent); + --sidebar-rule-color: var(--sidebar-line-rule); --sidebar-copy-size: 0.84375rem; --sidebar-copy-line: 1.5; --sidebar-note-size: 0.78125rem; @@ -939,12 +1025,12 @@ border: 1px solid var(--border-sidebar); border-radius: var(--radius-card); overflow: hidden; - box-shadow: 0 18px 42px rgba(0,0,0,0.12); + box-shadow: var(--shadow-sidebar); } .sidebar-focus-backdrop { position: fixed; inset: 0; - background: rgba(5, 11, 19, 0.58); + background: var(--surface-sidebar-focus-backdrop); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); opacity: 0; @@ -977,9 +1063,9 @@ justify-content: center; height: 34px; padding: 0 var(--space-stack); - border: 1px solid rgba(255,255,255,0.18); + border: 1px solid var(--border-overlay-btn); border-radius: 10px; - background: rgba(255,255,255,0.06); + background: var(--surface-overlay-btn); color: var(--text-sidebar-kicker); font-family: 'DM Mono', monospace; font-size: 0.6875rem; @@ -991,16 +1077,16 @@ margin-left: auto; } .sidebar-focus-print-btn:hover { - background: rgba(255,255,255,0.12); - border-color: rgba(255,255,255,0.28); + background: var(--surface-overlay-btn-hover); + border-color: var(--border-overlay-btn-hover); } .sidebar-focus-print-btn:active { transform: translateY(1px); } .sidebar-focus-toggle { width: 34px; height: 34px; - border: 1px solid rgba(255,255,255,0.18); + border: 1px solid var(--border-overlay-btn); border-radius: 10px; - background: rgba(255,255,255,0.06); + background: var(--surface-overlay-btn); color: var(--text-sidebar-kicker); display: inline-flex; align-items: center; @@ -1010,12 +1096,12 @@ flex: 0 0 auto; } .sidebar-focus-toggle:hover { - background: rgba(255,255,255,0.12); - border-color: rgba(255,255,255,0.28); + background: var(--surface-overlay-btn-hover); + border-color: var(--border-overlay-btn-hover); } .sidebar-focus-toggle:active { transform: translateY(1px); } .sidebar-focus-toggle:focus-visible { - outline: 2px solid rgba(255,255,255,0.5); + outline: 2px solid var(--focus-ring-overlay); outline-offset: 2px; } .sidebar-focus-icon-close { display: none; } @@ -1057,29 +1143,41 @@ border-radius: var(--radius-control); } .sidebar-group--monthly { - background: var(--sidebar-zone-services, rgba(255, 255, 255, 0.03)); + background: var(--sidebar-zone-services); + border-radius: var(--radius-card); + padding: var(--space-stack-roomy); } .sidebar-group--tax { - background: transparent; + background: var(--sidebar-zone-tax); } .sidebar-group--invoice { - background: var(--sidebar-zone-invoice, rgba(255, 255, 255, 0.05)); + background: var(--sidebar-zone-invoice); } .sidebar-group--value { - background: var(--sidebar-zone-value, rgba(255, 255, 255, 0.02)); + background: var(--sidebar-zone-value); + } + .sidebar-group--summary { + background: var(--sidebar-zone-summary); } .sidebar-group + .sidebar-group { margin-top: var(--space-stack); } + /* Subtle hover tint for line scanning readability */ + .sidebar-line:hover { + background: var(--surface-accent-soft, rgba(45, 122, 168, 0.07)); + border-left-color: var(--accent); + padding-left: calc(var(--space-xs) + 4px); + } .sidebar-group-title { display: block; flex: 0 0 auto; min-height: 12px; font-family: 'DM Mono', monospace; font-size: 0.625rem; - letter-spacing: 0.16em; + font-weight: 500; + letter-spacing: 0.18em; text-transform: uppercase; - color: var(--muted); + color: var(--sidebar-group-title-color); margin: 0 0 var(--space-stack-tight); } .sidebar-line { @@ -1090,12 +1188,23 @@ font-size: var(--sidebar-copy-size); color: var(--muted); line-height: var(--sidebar-copy-line); - padding: var(--space-stack-tight) 0; - border-bottom: 1px dashed var(--sidebar-rule-color); + padding: var(--space-stack-tight) var(--space-xs); + border-bottom: 1px var(--sidebar-line-rule-style) var(--sidebar-rule-color); + border-left: 2px solid transparent; + border-radius: 2px; + transition: background var(--transition-fast, 150ms) ease, + border-color var(--transition-fast, 150ms) ease, + padding-left var(--transition-fast, 150ms) ease; + } + /* Remove border on last visible line before a group boundary */ + .sidebar-group > .sidebar-line:last-child, + #sidebarLines > .sidebar-line:last-of-type { + border-bottom: none; } .sidebar-line > span:first-child { flex: 1 1 auto; min-width: 0; } .sidebar-line .val { font-family: 'DM Mono', monospace; + font-weight: 600; color: var(--text-money); font-size: var(--sidebar-copy-size); line-height: 1.2; @@ -1108,18 +1217,19 @@ .sidebar-mrr-label { font-family: 'DM Mono', monospace; font-size: 0.625rem; - letter-spacing: 0.16em; + letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); margin-bottom: var(--space-sm); } .sidebar-mrr { font-family: 'Poppins', sans-serif; - font-weight: 700; - font-size: var(--sidebar-mrr-size); + font-weight: 800; + font-size: 3rem; color: var(--text-money-hero); line-height: 0.94; - letter-spacing: -0.03em; + letter-spacing: -0.02em; + text-shadow: 0 2px 12px rgba(245, 240, 232, 0.08); margin-bottom: var(--space-stack-roomy); } .sidebar-line-value .val, @@ -1168,9 +1278,11 @@ color: var(--ink); letter-spacing: 0.02em; } - .vs-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; line-height: 1.56; } - .vs-table td { padding: 9px var(--space-xs); vertical-align: middle; } + .vs-table { width: 100%; border-collapse: collapse; font-size: 0.775rem; line-height: 1.56; } + .vs-table td { padding: 7px var(--space-xs); vertical-align: middle; } .vs-table td:last-child { text-align: right; font-family: 'DM Mono', monospace; white-space: nowrap; } + .vs-table tr:not(.vs-save-row) { opacity: 0.85; } + .vs-table tr.vs-save-row { opacity: 1; } .vs-table tr:first-child td { padding-top: 2px; padding-bottom: var(--space-stack-roomy); border-bottom: 1px solid var(--border); } .vs-table tr:nth-child(2) td, .vs-table tr:nth-child(4) td { padding-top: var(--space-stack-roomy); } @@ -1186,7 +1298,7 @@ .vs-label { font-family: 'DM Mono', monospace; font-size: 0.6875rem; - letter-spacing: 0.16em; + letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); margin-bottom: 0; @@ -1245,7 +1357,7 @@ } .nudge-nav-btn { background: var(--surface-ghost); - border: 1px solid rgba(255,255,255,0.06); + border: 1px solid var(--border-nudge-nav); cursor: pointer; padding: 0; width: 34px; @@ -1346,7 +1458,7 @@ .qs-term-wrap .tier-seg { padding: clamp(14px, 2.5cqi, 16px) clamp(12px, 2.3cqi, 14px) clamp(14px, 2.4cqi, 15px); background: var(--surface-term-tile); - transition: background 0.18s, border-color 0.18s, box-shadow 0.18s, transform 0.18s; + transition: background 200ms ease, color 200ms ease, border-color 200ms ease, box-shadow 200ms ease, transform 200ms ease; } .qs-term-wrap .tier-seg:hover { background: var(--surface-term-tile-hover); @@ -1516,7 +1628,10 @@ transition: left 0.2s, background 0.2s; box-shadow: var(--shadow-switch-knob); } - .qs-toggle-row input:checked ~ .qs-switch { background: var(--surface-switch-on); } + .qs-toggle-row input:checked ~ .qs-switch { + background: var(--surface-switch-on); + box-shadow: 0 0 8px rgba(58, 184, 112, 0.3); + } .qs-toggle-row input:checked ~ .qs-switch::after { left: 17px; } .qs-fee-waive:has(input:disabled) { opacity: 0.5; cursor: default; } .qs-fee-input:disabled { opacity: 0.4; cursor: not-allowed; } @@ -1701,16 +1816,31 @@ .sl-discount-val { color: var(--green) !important; } .sl-discount-detail { font-size: 0.7rem; opacity: 0.7; } .sl-hst-val { color: var(--text-money) !important; font-size: var(--sidebar-copy-size); } - .sidebar-line-discount { border-bottom-style: dashed; opacity: 0.8; } + .sidebar-line-discount { border-bottom-style: dashed; border-bottom-color: var(--sidebar-rule-color); opacity: 0.8; } .sidebar-line.sidebar-line-hst { margin-top: 0; padding-top: var(--space-stack-tight); padding-bottom: var(--space-stack-tight); - border-top: 1px dashed var(--sidebar-rule-color); + border-top: 1px var(--sidebar-line-rule-style) var(--sidebar-rule-color); + border-bottom: none; } .sidebar-line.sidebar-line-total { font-weight: 600; - margin-top: 2px; + margin-top: var(--space-sm); + border-top: 2px solid color-mix(in srgb, var(--accent) 30%, transparent); + border-bottom: none; + padding-top: var(--space-stack-roomy); + } + .sidebar-line.sidebar-line-total .val { + font-size: 1.1em; + font-weight: 700; + color: var(--text-money-hero); + } + .suffix-mo { + font-size: 0.7em; + font-weight: 500; + color: var(--muted); + letter-spacing: 0.02em; } .sl-hst-toggle { justify-content: flex-start; @@ -1740,7 +1870,7 @@ display: flex; flex-direction: column; z-index: 540; - box-shadow: 0 28px 64px rgba(0,0,0,0.28); + box-shadow: var(--shadow-sidebar-focus); } body.sidebar-focus-open .side-col .sidebar-focus-client { display: block; @@ -1807,21 +1937,34 @@ Subtle left accent glow on hover; stronger treatment when open. ─────────────────────────────────────────────────────────────── */ .section { - transition: border-color 0.2s, box-shadow 0.2s; + box-shadow: var(--shadow-card); + transition: border-color var(--transition-medium, 0.2s) ease, box-shadow var(--transition-medium, 0.2s) ease; + } + .section.sec-active { + border-left: 3px solid color-mix(in srgb, var(--accent) 50%, transparent); } .section:hover { border-color: var(--section-hover-border); - box-shadow: var(--section-hover-shadow); + box-shadow: var(--shadow-card-hover), var(--section-hover-shadow); } .section.sec-open { border-color: var(--section-open-border); - box-shadow: var(--section-open-shadow); + box-shadow: var(--shadow-card-open), var(--section-open-shadow); } /* ── ADDON ROW SELECTED — stronger check indicator ────────────── .selected gets a more prominent border + check indicator via the checkbox's native accent-color. No pseudo-element needed. ─────────────────────────────────────────────────────────────── */ + @keyframes stepper-pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.05); } + 100% { transform: scale(1); } + } + .num-input.pulse { + animation: stepper-pulse 150ms ease-out; + } + @keyframes addonPulse { 0% { transform: scale(1); } 50% { transform: scale(1.015); } @@ -1882,8 +2025,8 @@ ─────────────────────────────────────────────────────────────── */ .export-wrap { padding: var(--space-stack-roomy) var(--space-xl) var(--space-lg); - background: var(--surface-export); - border-top: 1px solid var(--border-export-top); + background: transparent; + border-top: none; display: flex; flex-direction: column; gap: var(--space-stack-tight); @@ -1909,7 +2052,7 @@ .btn-export:hover { background: var(--btn-primary-hover); filter: brightness(1.15); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25); + box-shadow: var(--shadow-export-hover); } .btn-export:active { transform: scale(0.97); filter: brightness(0.95); } .btn-export-secondary { @@ -2025,9 +2168,9 @@ .pitch-footer { background: var(--surface-success); border-top: 1px solid var(--surface-success-border); - padding: var(--space-md) var(--space-3xl); + padding: var(--space-sm) var(--space-3xl); font-family: 'DM Mono', monospace; - font-size: 13px; + font-size: 11px; color: var(--green); letter-spacing: 0.05em; text-align: center; diff --git a/SVS-MSP-Calculator-glass.css b/SVS-MSP-Calculator-glass.css index b5a2004..d6445c4 100644 --- a/SVS-MSP-Calculator-glass.css +++ b/SVS-MSP-Calculator-glass.css @@ -32,6 +32,10 @@ html { --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); + --group-strip: color-mix(in srgb, var(--accent) 20%, var(--paper)); + --shadow-card: 0 2px 12px rgba(0,0,0,0.2), 0 1px 4px rgba(0,0,0,0.12); + --shadow-card-hover: 0 4px 20px rgba(0,0,0,0.25), 0 2px 8px rgba(0,0,0,0.15); + --shadow-card-open: 0 8px 28px rgba(0,0,0,0.3), 0 2px 10px rgba(0,0,0,0.18); --section-hover-border: rgba(105, 200, 255, 0.34); --section-hover-shadow: -4px 0 0 0 rgba(105, 200, 255, 0.36), @@ -79,15 +83,15 @@ html { --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-heading-soft: var(--glass-heading); --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-border: var(--glass-panel-border); --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-ghost-hover-text: var(--glass-heading); --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)); @@ -96,7 +100,7 @@ html { --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-wrap: var(--glass-panel-border); --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: @@ -106,7 +110,7 @@ html { --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: var(--glass-heading); --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); @@ -115,7 +119,7 @@ html { --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-border: var(--glass-panel-border); --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); @@ -137,9 +141,9 @@ html { --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; + --glass-selected-bg: var(--glass-ghost-hover-bg); + --glass-selected-border: var(--glass-ghost-hover-border); + --glass-selected-text: var(--glass-heading); --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)); @@ -151,7 +155,7 @@ html { --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-border: var(--glass-ghost-hover-border); --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); @@ -162,8 +166,8 @@ html { --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; + --glass-reset-hover-border: var(--glass-ghost-hover-border); + --glass-reset-hover-text: var(--glass-heading); --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)); @@ -182,9 +186,13 @@ html { --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); + --sidebar-zone-services: rgba(105, 200, 255, 0.04); + --sidebar-zone-invoice: rgba(105, 200, 255, 0.07); + --sidebar-zone-value: rgba(99, 216, 162, 0.04); + --sidebar-zone-summary: rgba(105, 200, 255, 0.03); + --sidebar-row-stripe: rgba(105, 200, 255, 0.03); + --sidebar-line-rule: rgba(143, 183, 221, 0.12); + --sidebar-total-rule: rgba(143, 183, 221, 0.22); } body { @@ -226,10 +234,6 @@ body::before { .outer { padding-top: var(--sidebar-top-gap) !important; } - - .side-col { - top: var(--sidebar-sticky-top) !important; - } } .section, @@ -242,7 +246,6 @@ body::before { .mobile-panel-actions, .confirm-modal-card, .vs-comparison-wrap, -.export-wrap, .pitch-inner { background: var(--glass-panel-bg) !important; border-color: var(--glass-panel-border) !important; @@ -458,16 +461,20 @@ body::before { } .collapsible-header, -.sidebar-line, .pitch-item, .vs-label::after, -.sidebar-line-total, .pitch-footer, -.export-wrap, .mobile-panel-close-row, .mobile-panel-actions { border-color: var(--glass-divider) !important; } +.sidebar-line { + border-bottom-color: var(--glass-divider) !important; +} +.sidebar-line.sidebar-line-total { + border-top-color: var(--sidebar-total-rule) !important; + border-bottom: none !important; +} .sidebar-title, .sidebar-client.placeholder { @@ -505,7 +512,8 @@ body::before { } .export-wrap { - border-top: 1px solid var(--glass-divider) !important; + background: transparent !important; + border-top: none !important; } body.sidebar-focus-open .side-col .export-wrap { background: transparent !important; diff --git a/SVS-MSP-Calculator-layout.css b/SVS-MSP-Calculator-layout.css index 461a01b..12eadb1 100644 --- a/SVS-MSP-Calculator-layout.css +++ b/SVS-MSP-Calculator-layout.css @@ -19,8 +19,8 @@ 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; } + .main-col { display: flex; flex-direction: column; gap: clamp(12px, 1.2vw, 20px); container-type: inline-size; } + .side-col { position: static; 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 { @@ -130,7 +130,7 @@ } .confirm-btn-danger { background: var(--amber); - color: #fff; + color: var(--btn-primary-fg); border: 1px solid transparent; } .confirm-btn-danger:hover { filter: brightness(1.05); } diff --git a/SVS-MSP-Calculator-light.css b/SVS-MSP-Calculator-light.css index 0e95da0..8857761 100644 --- a/SVS-MSP-Calculator-light.css +++ b/SVS-MSP-Calculator-light.css @@ -28,7 +28,7 @@ --surface-settings: #e3d7c4; --surface-settings-divider: #c8bcab; --surface-input: #f1eadf; - --surface-term-wrap: #ece3d6; + --surface-term-wrap: var(--card); --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%); @@ -39,7 +39,7 @@ --text-term-name: #64594e; --text-term-name-active: #f8f5ef; --text-term-sub: #4d433a; - --text-term-sub-active: #f8f5ef; + --text-term-sub-active: var(--text-term-name-active); --text-term-discount: #2f2a25; --text-term-discount-active: #ffffff; --surface-best-value: rgba(86, 146, 105, 0.12); @@ -53,11 +53,11 @@ --surface-sidebar-body: #ebe5dd; --surface-sidebar-utility: #d8d1c7; --surface-export: #ddd6cd; - --surface-compare: #ddd7ce; - --surface-modal: #f0e8dc; + --surface-compare: var(--surface-export); + --surface-modal: var(--surface-input); --surface-mobile-sheet: #e5dfd6; --surface-mobile-close-row: #dbd4cb; - --surface-mobile-actions: #dbd4cb; + --surface-mobile-actions: var(--surface-mobile-close-row); --surface-mobile-sidebar: transparent; --surface-accent-soft: rgba(99, 127, 136, 0.09); --surface-summary-badge: rgba(99, 127, 136, 0.09); @@ -66,7 +66,7 @@ --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: var(--surface-input); --surface-step-hover: #e5dbcc; --surface-step-active: var(--accent); --surface-step-border: #a99e8f; @@ -77,7 +77,7 @@ --surface-danger-border: #d5a1ab; --text-danger: #7a1520; --surface-warning: #f7f0dd; - --surface-warning-panel: #f3ebda; + --surface-warning-panel: var(--surface-warning); --surface-warning-border: #ddc39b; --surface-compare-success: rgba(86, 146, 105, 0.12); --surface-compare-warning: rgba(179, 133, 72, 0.11); @@ -95,6 +95,10 @@ --text-vs-muted: var(--muted); --text-incentive: #35554a; --text-pill-savings-active: #d4f5e0; + --group-strip: color-mix(in srgb, var(--accent) 18%, var(--paper)); + --shadow-card: 0 2px 8px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04); + --shadow-card-hover: 0 4px 16px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.05); + --shadow-card-open: 0 6px 20px rgba(0,0,0,0.1), 0 2px 8px rgba(0,0,0,0.06); --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); @@ -106,13 +110,17 @@ --surface-pill-icon: rgba(255, 255, 255, 0.18); --border-sidebar: #c6beb3; --surface-sidebar-utility-border: #bfb7ad; - --border-compare: #c8c0b5; + --border-compare: var(--border-sidebar); --border-export-top: #ccc4ba; - --border-mobile-sheet: #c6beb3; - --border-mobile-row: #ccc4ba; - --sidebar-zone-services: rgba(0, 0, 0, 0.025); + --border-mobile-sheet: var(--border-sidebar); + --border-mobile-row: var(--border-export-top); + --sidebar-zone-services: rgba(0, 0, 0, 0.03); --sidebar-zone-invoice: rgba(0, 0, 0, 0.04); - --sidebar-zone-value: rgba(0, 0, 0, 0.015); + --sidebar-zone-value: rgba(33, 112, 69, 0.04); + --sidebar-zone-summary: rgba(0, 0, 0, 0.02); + --sidebar-row-stripe: rgba(0, 0, 0, 0.02); + --sidebar-line-rule: color-mix(in srgb, var(--border) 70%, transparent); + --sidebar-total-rule: color-mix(in srgb, var(--border) 90%, transparent); --surface-switch-off: #b5ad9f; --surface-switch-on: var(--green); } diff --git a/SVS-MSP-Calculator-print.css b/SVS-MSP-Calculator-print.css index fecb5de..e51ebe5 100644 --- a/SVS-MSP-Calculator-print.css +++ b/SVS-MSP-Calculator-print.css @@ -41,6 +41,9 @@ .collapsible-header { display: none !important; } .sec-chevron { display: none !important; } .sec-summary-badge { display: none !important; } + .group-label { display: none !important; } + #sec-02::after, #sec-03::after, #sec-01::after { display: none !important; } + .group-divider { display: none !important; } .sec-controls-row { display: none !important; } .quote-settings-bar { display: none !important; } .section-badge { display: none !important; } diff --git a/SVS-MSP-Calculator-responsive.css b/SVS-MSP-Calculator-responsive.css index 01fb6a3..eee0e76 100644 --- a/SVS-MSP-Calculator-responsive.css +++ b/SVS-MSP-Calculator-responsive.css @@ -44,6 +44,14 @@ .qs-fee-row { padding: 6px 0 0; } .main-col > .section:first-of-type { margin-top: var(--space-sm); } + #sec-02::after, #sec-03::after, #sec-01::after { display: none; } + .group-label { margin-left: 0; } + /* Mobile grouping — accent left border on Managed IT sections */ + #sec-02, #sec-03, #sec-01 { + border-left: 3px solid var(--group-strip); + } + .group-divider { margin-left: 0; margin-right: 0; } + .mobile-quote-pill { top: 12vh; } /* Pill toggle — stack vertically on tiny screens */ .pill-toggle { @@ -127,10 +135,10 @@ grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-stack-tight); } - .m365-app-item { padding: var(--space-stack-tight) 6px; } + .m365-app-item { padding: var(--space-sm) 6px; } .m365-app-icon { - width: 15px; - height: 15px; + width: 22px; + height: 22px; } /* Savings row — stack */ @@ -239,7 +247,7 @@ align-items: center; gap: var(--space-stack-tight); position: fixed; - top: 82px; + top: calc(var(--top-bar-sticky-offset) + var(--space-lg)); right: max(14px, env(safe-area-inset-right, 0px)); z-index: 200; background: var(--accent); diff --git a/SVS-MSP-Calculator-tokens.css b/SVS-MSP-Calculator-tokens.css index d9dddfe..0492566 100644 --- a/SVS-MSP-Calculator-tokens.css +++ b/SVS-MSP-Calculator-tokens.css @@ -165,19 +165,23 @@ --surface-danger-border: #5e2830; --text-danger: #e87882; --surface-warning: #2a1e06; - --surface-warning-panel: #2e1f08; + --surface-warning-panel: var(--surface-warning); --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-positive-soft: rgba(33,112,69,0.08); + --surface-positive-strong: rgba(33,112,69,0.13); + --border-positive-soft: rgba(33,112,69,0.22); + --border-positive-strong: rgba(33,112,69,0.3); + --surface-positive-pill: var(--surface-positive-soft); + --surface-positive-badge: var(--surface-positive-strong); + --border-positive-badge: var(--border-positive-strong); + --surface-positive-badge-strong: var(--surface-positive-strong); + --border-positive-badge-strong: var(--border-positive-strong); + --surface-positive-panel: var(--surface-positive-soft); + --border-positive-panel: var(--border-positive-soft); --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); @@ -190,10 +194,9 @@ --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); + --text-on-accent-strong: var(--text-on-accent-soft); --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; @@ -201,6 +204,10 @@ --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); + --group-strip: color-mix(in srgb, var(--accent) 18%, var(--paper)); + --shadow-card: 0 2px 8px rgba(0,0,0,0.15), 0 1px 3px rgba(0,0,0,0.1); + --shadow-card-hover: 0 4px 16px rgba(0,0,0,0.2), 0 2px 6px rgba(0,0,0,0.12); + --shadow-card-open: 0 6px 20px rgba(0,0,0,0.22), 0 2px 8px rgba(0,0,0,0.14); --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); @@ -213,6 +220,16 @@ --btn-primary-fg: #fff; --btn-primary-hover: #3a8fc4; --surface-pill-icon: rgba(255,255,255,0.2); + --surface-overlay-btn: rgba(255,255,255,0.06); + --surface-overlay-btn-hover: rgba(255,255,255,0.12); + --border-overlay-btn: rgba(255,255,255,0.18); + --border-overlay-btn-hover: rgba(255,255,255,0.28); + --focus-ring-overlay: rgba(255,255,255,0.5); + --surface-sidebar-focus-backdrop: rgba(5, 11, 19, 0.58); + --shadow-sidebar: 0 18px 42px rgba(0,0,0,0.12); + --shadow-sidebar-focus: 0 28px 64px rgba(0,0,0,0.28); + --shadow-export-hover: 0 2px 8px rgba(0,0,0,0.25); + --border-nudge-nav: rgba(255,255,255,0.06); --print-paper: #fff; --print-ink: #1a1a1a; --print-accent: #2d7aa8; @@ -231,9 +248,32 @@ --print-callout-green-border: #3ab870; --print-callout-red-border: #5e2830; --print-footer-note: #888; + --sidebar-zone-services: rgba(255, 255, 255, 0.06); + --sidebar-zone-invoice: rgba(255, 255, 255, 0.08); + --sidebar-zone-value: rgba(58, 184, 112, 0.05); + --sidebar-zone-summary: rgba(255, 255, 255, 0.03); + --sidebar-zone-tax: transparent; + --sidebar-line-rule: color-mix(in srgb, var(--border) 88%, transparent); + --sidebar-line-rule-style: dashed; + --sidebar-total-rule: var(--border); + --sidebar-total-rule-style: solid; + --sidebar-row-stripe: rgba(255, 255, 255, 0.018); + --sidebar-group-title-color: var(--muted); --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)); } +/* ── TABULAR NUMBERS ───────────────────────────────────────── + All monetary values use tabular (fixed-width) figures so + columns of numbers align perfectly. +──────────────────────────────────────────────────────────── */ +.val, +.sidebar-line .val, +.price-badge, +.sidebar-hero, +[data-money] { + font-variant-numeric: tabular-nums; +} + diff --git a/SVS-MSP-Calculator.html b/SVS-MSP-Calculator.html index 226d370..1b0962c 100644 --- a/SVS-MSP-Calculator.html +++ b/SVS-MSP-Calculator.html @@ -169,6 +169,8 @@ +
Managed IT Services (Sections I, II, III)
+ +file_type_azure \ No newline at end of file diff --git a/pre-alpha/M365icons/excel.svg b/pre-alpha/M365icons/excel.svg new file mode 100644 index 0000000..d952563 --- /dev/null +++ b/pre-alpha/M365icons/excel.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pre-alpha/M365icons/excel2-svgrepo-com.svg b/pre-alpha/M365icons/excel2-svgrepo-com.svg new file mode 100644 index 0000000..5d973e9 --- /dev/null +++ b/pre-alpha/M365icons/excel2-svgrepo-com.svg @@ -0,0 +1,2 @@ + +file_type_excel2 \ No newline at end of file diff --git a/pre-alpha/M365icons/outlook-svgrepo-com.svg b/pre-alpha/M365icons/outlook-svgrepo-com.svg new file mode 100644 index 0000000..209390d --- /dev/null +++ b/pre-alpha/M365icons/outlook-svgrepo-com.svg @@ -0,0 +1,2 @@ + +file_type_outlook \ No newline at end of file diff --git a/pre-alpha/M365icons/powerpoint.svg b/pre-alpha/M365icons/powerpoint.svg new file mode 100644 index 0000000..766e909 --- /dev/null +++ b/pre-alpha/M365icons/powerpoint.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pre-alpha/M365icons/teams.svg b/pre-alpha/M365icons/teams.svg new file mode 100644 index 0000000..d3cf515 --- /dev/null +++ b/pre-alpha/M365icons/teams.svg @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/pre-alpha/M365icons/word.svg b/pre-alpha/M365icons/word.svg new file mode 100644 index 0000000..0876ec8 --- /dev/null +++ b/pre-alpha/M365icons/word.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/SVS-MSP-Calculator-70retro.css b/pre-alpha/SVS-MSP-Calculator-70retro.css similarity index 81% rename from SVS-MSP-Calculator-70retro.css rename to pre-alpha/SVS-MSP-Calculator-70retro.css index 34b7361..c2c4662 100644 --- a/SVS-MSP-Calculator-70retro.css +++ b/pre-alpha/SVS-MSP-Calculator-70retro.css @@ -7,6 +7,13 @@ :root { /* ── Core palette ────────────────────────────────────────── */ + --retro-cream: #f0e4d0; + --retro-muted-warm: #c0aa98; + --retro-dark: #1c1317; + --retro-dark-mid: #2a1e22; + --retro-dark-deep: #140e11; + --retro-cta-gradient: linear-gradient(180deg, #e11d48 0%, #be123c 100%); + --retro-dark-gradient: linear-gradient(180deg, var(--retro-dark-mid) 0%, var(--retro-dark) 100%); --ink: #1c1317; --paper: #f0e4c8; --accent: #e11d48; @@ -16,20 +23,20 @@ --card: #e8dcc0; --green: #0d9488; --amber: #d97706; - --sky: #0d9488; + --sky: #6366f1; --focus-ring-soft: rgba(225, 29, 72, 0.2); /* ── Top bar ─────────────────────────────────────────────── */ - --top-bar-bg: #1c1317; + --top-bar-bg: var(--retro-dark); --top-bar-border: rgba(225, 29, 72, 0.35); - --top-bar-meta: #c0aa98; + --top-bar-meta: var(--retro-muted-warm); --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; + --theme-chip-fg: var(--retro-cream); /* ── Surfaces ────────────────────────────────────────────── */ --surface-section: #e4d5b5; @@ -64,7 +71,7 @@ /* ── Sidebar ─────────────────────────────────────────────── */ --surface-sidebar: #e0d4b6; - --surface-sidebar-header: #1c1317; + --surface-sidebar-header: var(--retro-dark); --surface-sidebar-body: #e6d8b8; --surface-sidebar-utility: #d0be98; --surface-export: #ccba94; @@ -75,14 +82,14 @@ /* ── Compare / Modal ─────────────────────────────────────── */ --surface-compare: #d6c8a6; --border-compare: #c4ae8a; - --surface-modal: #efe2c4; + --surface-modal: var(--surface-input); --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-sheet: var(--surface-section); + --surface-mobile-close-row: var(--surface-settings); + --surface-mobile-actions: var(--surface-settings); --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); @@ -97,8 +104,8 @@ --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: var(--surface-input); + --surface-step-hover: var(--surface-settings); --surface-step-active: var(--accent); --surface-step-border: #b5a07a; --text-step: var(--accent); @@ -132,6 +139,7 @@ --text-pill-savings-active: #ccfbf1; /* ── Section interaction ─────────────────────────────────── */ + --group-strip: rgba(225, 29, 72, 0.12); --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); @@ -141,9 +149,13 @@ --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); + --sidebar-zone-services: rgba(0, 0, 0, 0.04); + --sidebar-zone-invoice: rgba(0, 0, 0, 0.06); + --sidebar-zone-value: rgba(13, 148, 136, 0.04); + --sidebar-zone-summary: rgba(0, 0, 0, 0.025); + --sidebar-row-stripe: rgba(0, 0, 0, 0.025); + --sidebar-line-rule: color-mix(in srgb, var(--border) 60%, transparent); + --sidebar-total-rule: color-mix(in srgb, var(--border) 85%, transparent); --surface-switch-off: #c0b4a0; --surface-switch-on: var(--green); } @@ -160,23 +172,23 @@ /* ── Top bar — warm dark with hot rose neon edge ─────────── */ .top-bar { - background: linear-gradient(180deg, #2a1e22 0%, #1c1317 60%, #140e11 100%) !important; + background: linear-gradient(180deg, var(--retro-dark-mid) 0%, var(--retro-dark) 60%, var(--retro-dark-deep) 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; + color: var(--retro-cream) !important; } -.top-bar .top-bar-meta { color: #c0aa98 !important; } +.top-bar .top-bar-meta { color: var(--retro-muted-warm) !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; + fill: var(--retro-cream) !important; } /* ── Sidebar header — matches top bar ────────────────────── */ .sidebar-header { - background: linear-gradient(180deg, #2a1e22 0%, #1c1317 100%) !important; - color: #fff !important; + background: var(--retro-dark-gradient) !important; + color: var(--text-on-accent) !important; } /* ── Section number — hot rose (faded on paper) ──────────── */ @@ -186,18 +198,18 @@ /* ── Pill toggle checked — hot rose gradient ─────────────── */ .pill-toggle input:checked + label { - background: linear-gradient(180deg, #e11d48 0%, #be123c 100%) !important; - color: #fff !important; + background: var(--retro-cta-gradient) !important; + color: var(--text-on-accent) !important; } .pill-toggle input:checked + label .pill-price, .pill-toggle input:checked + label .pill-desc { - color: #fff !important; + color: var(--text-on-accent) !important; } /* ── Export buttons — hot rose CTA ───────────────────────── */ .btn-export { - background: linear-gradient(180deg, #e11d48 0%, #be123c 100%) !important; - color: #fff !important; + background: var(--retro-cta-gradient) !important; + color: var(--text-on-accent) !important; } .btn-export:hover { filter: brightness(1.1) !important; @@ -206,8 +218,8 @@ /* ── Tier segment active — hot rose gradient ─────────────── */ .tier-seg.active { - background: linear-gradient(180deg, #e11d48 0%, #be123c 100%) !important; - color: #fff !important; + background: var(--retro-cta-gradient) !important; + color: var(--text-on-accent) !important; } /* ── Import button hover — teal accent ───────────────────── */ @@ -226,7 +238,7 @@ /* ── Paper texture with warm scanlines ───────────────────── */ body { - background-color: #f0e4c8; + background-color: var(--paper); background-image: repeating-linear-gradient( 0deg, @@ -254,25 +266,25 @@ body { /* ── Callout boxes ───────────────────────────────────────── */ .callout-green { - background: #dceee6 !important; - border-color: #6db89a !important; - color: #0d9488 !important; + background: var(--surface-success) !important; + border-color: var(--surface-success-border) !important; + color: var(--green) !important; } .callout-red { - background: #f5dcd6 !important; - border-color: #d4827a !important; + background: var(--surface-danger) !important; + border-color: var(--surface-danger-border) !important; } /* ── Mobile quote pill — hot rose with glow ──────────────── */ .mobile-quote-pill { - background: linear-gradient(180deg, #e11d48 0%, #be123c 100%) !important; - color: #fff !important; + background: var(--retro-cta-gradient) !important; + color: var(--text-on-accent) !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; + background: linear-gradient(90deg, var(--accent) 0%, var(--green) 100%) !important; } /* ── Full-screen sidebar export-wrap ─────────────────────── */ diff --git a/pre-alpha/SVS-MSP-Calculator-base.css b/pre-alpha/SVS-MSP-Calculator-base.css new file mode 100644 index 0000000..4e620cb --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator-base.css @@ -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; } + + diff --git a/pre-alpha/SVS-MSP-Calculator-components.css b/pre-alpha/SVS-MSP-Calculator-components.css new file mode 100644 index 0000000..f09fdb9 --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator-components.css @@ -0,0 +1,2123 @@ +/* SVS MSP Calculator - Components */ +/* Extracted during Phase 5 to keep the HTML shell stable while splitting the monolithic stylesheet. */ + /* ── SECTION CARDS (I–VI) ─────────────────────────────────────── + Each section = position:relative card with: + .section-num — absolute, floats left outside card (Cinzel) + .section-header — flex row: title-block | summary badge | chevron + .section-body — collapsible content (overflow:hidden, JS toggle) + JS toggleSection(id) adds/removes .sec-open class on .section. + .sec-open .sec-chevron rotates 180deg (down→up arrow). + .sec-summary-badge is shown/hidden by setSummary() in update(). + ─────────────────────────────────────────────────────────────── */ + .section { + position: relative; + container-type: inline-size; + margin-left: var(--section-offset); + border-radius: var(--radius-card); + border: 1px solid var(--border); + background: var(--surface-section); + padding: var(--section-padding-top) var(--section-padding-x) var(--section-padding-bottom); + } + .fa-icon { + display: inline-block; + width: var(--icon-size, 1em); + height: var(--icon-size, 1em); + flex: 0 0 auto; + color: inherit; + background-color: currentColor; + vertical-align: middle; + mask-image: var(--fa-icon-url); + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + -webkit-mask-image: var(--fa-icon-url); + -webkit-mask-repeat: no-repeat; + -webkit-mask-position: center; + -webkit-mask-size: contain; + } + .fa-icon--accent { color: var(--accent); } + .fa-icon--green { color: var(--green); } + .fa-icon--amber { color: var(--amber); } + .fa-icon--danger { color: var(--text-danger); } + .fa-icon--white { color: var(--text-on-accent); } + .fa-icon--theme { color: currentColor; } + /* ── FA Sharp Solid icons — inline data URIs for file:// compatibility ── */ + .fa-icon-file-invoice { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath fill='currentColor' d='M240 0L0 0 0 512 384 512 384 144 240 0zm85.5 176L208 176 208 58.5 325.5 176zM64 416l0-128 256 0 0 128-256 0zM88 64l72 0 0 48-96 0 0-48 24 0zm0 96l72 0 0 48-96 0 0-48 24 0z'/%3E%3C/svg%3E"); } + .fa-icon-sun-bright { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath fill='currentColor' d='M312 544l-48 0 0-112 48 0 0 112zM180.5 397.4l-79.2 79.2-33.9-33.9 79.2-79.2 33.9 33.9zm328.1 45.3l-33.9 33.9-79.2-79.2 33.9-33.9 79.2 79.2zM288 384a128 128 0 1 1 0-256 128 128 0 1 1 0 256zM112 280l-112 0 0-48 112 0 0 48zm464 0l-112 0 0-48 112 0 0 48zM180.5 114.6l-33.9 33.9-79.2-79.2 33.9-33.9 79.2 79.2zM508.6 69.3l-79.2 79.2-33.9-33.9 79.2-79.2 33.9 33.9zM312 80l-48 0 0-112 48 0 0 112z'/%3E%3C/svg%3E"); } + .fa-icon-moon-stars { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M448-32l16 48 48 16-48 16-16 48-16-48-48-16 48-16 16-48zM314.7 83.1c-76.2 18.9-132.7 87.8-132.7 169.9 0 96.6 78.3 175 175 175 16.2 0 31.9-2.2 46.8-6.3-40.8 54.8-106.1 90.3-179.8 90.3-123.7 0-224-100.3-224-224S100.3 64 224 64c32.3 0 63 6.8 90.7 19.1zM384 352L355.2 268.8 272 240 355.2 211.2 384 128 412.8 211.2 496 240 412.8 268.8 384 352z'/%3E%3C/svg%3E"); } + .fa-icon-sparkles { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 512'%3E%3Cpath fill='currentColor' d='M480 32l32-72 32 72 72 32-72 32-32 72-32-72-72-32 72-32zM160 192l64-144 64 144 144 64-144 64-64 144-64-144-144-64 144-64zM480 344l32 72 72 32-72 32-32 72-32-72-72-32 72-32 32-72z'/%3E%3C/svg%3E"); } + .fa-icon-angles-up { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M246.9 41.4l-22.6-22.6-22.6 22.6-160 160-22.6 22.6 45.3 45.3 22.6-22.6 137.4-137.4 137.4 137.4 22.6 22.6 45.3-45.3-22.6-22.6-160-160zm0 192l-22.6-22.6-22.6 22.6-160 160-22.6 22.6 45.3 45.3 22.6-22.6 137.4-137.4 137.4 137.4 22.6 22.6 45.3-45.3-182.6-182.6z'/%3E%3C/svg%3E"); } + .fa-icon-angles-down { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M246.9 470.6l-22.6 22.6-22.6-22.6-160-160-22.6-22.6 45.3-45.3 22.6 22.6 137.4 137.4 137.4-137.4 22.6-22.6 45.3 45.3-182.6 182.6zm0-192l-22.6 22.6-22.6-22.6-160-160-22.6-22.6 45.3-45.3 22.6 22.6 137.4 137.4 160-160 45.3 45.3-22.6 22.6-160 160z'/%3E%3C/svg%3E"); } + .fa-icon-chevron-down { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M256.3 429.3l214.6-214.6 22.6-22.6-45.3-45.3-22.6 22.6-169.4 169.4-169.4-169.4-22.6-22.6-45.3 45.3 22.6 22.6 192 192 22.6 22.6z'/%3E%3C/svg%3E"); } + .fa-icon-chevron-left { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='currentColor' d='M3 256l22.6 22.6 192 192 22.6 22.6 45.3-45.3-22.6-22.6-169.4-169.4 169.4-169.4 22.6-22.6-45.3-45.3-22.6 22.6-192 192-22.6 22.6z'/%3E%3C/svg%3E"); } + .fa-icon-chevron-right { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='currentColor' d='M317.5 256L294.9 278.6 102.9 470.6 80.3 493.3 35 448 57.6 425.4 227 256 57.6 86.6 35 64 80.3 18.7 102.9 41.4 294.9 233.4 317.5 256z'/%3E%3C/svg%3E"); } + .fa-icon-square-check { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M448 480l-448 0 0-448 448 0 0 448zM308.5 151l-119.4 164.2-69.1-69.1-33.9 33.9 88.9 88.9 19.8 19.9 16.5-22.7 135.9-186.9 14.1-19.4-38.8-28.3-14.1 19.4z'/%3E%3C/svg%3E"); } + .fa-icon-circle-check { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M256 512a256 256 0 1 1 0-512 256 256 0 1 1 0 512zm84.5-361l-119.4 164.2-69.1-69.1-33.9 33.9 88.9 88.9 19.8 19.9 16.5-22.7 135.9-186.9 14.1-19.4-38.8-28.3-14.1 19.4z'/%3E%3C/svg%3E"); } + .fa-icon-circle-info { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M256 512a256 256 0 1 0 0-512 256 256 0 1 0 0 512zM216 336l24 0 0-64-48 0 0-48 96 0 0 112 32 0 0 48-128 0 0-48 24 0zm72-144l-64 0 0-64 64 0 0 64z'/%3E%3C/svg%3E"); } + .fa-icon-square-info { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M448 32l-448 0 0 448 448 0 0-448zM184 336l24 0 0-64-48 0 0-48 96 0 0 112 32 0 0 48-128 0 0-48 24 0zm72-208l0 64-64 0 0-64 64 0z'/%3E%3C/svg%3E"); } + .fa-icon-lightbulb-on { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 512'%3E%3Cpath fill='currentColor' d='M512 192c0 71.1-38.6 133.1-96 166.3l0 25.7-192 0 0-25.7C166.6 325.1 128 263.1 128 192 128 86 214 0 320 0S512 86 512 192zM224 528l0-96 192 0 0 96-192 0zm16-344c0-39.8 32.2-72 72-72l8 0 0-48-8 0c-66.3 0-120 53.7-120 120l0 8 48 0 0-8zM58.7 26.5l53.5 26.7-21.5 42.9C60.3 81 19.5 60.6 15.8 58.7L37.3 15.8 58.7 26.5zm544 42.9C579.6 81 561.8 89.9 549.3 96.2L527.8 53.3C558.2 38 599 17.7 602.7 15.8l21.5 42.9-21.5 10.7zM24 168l56 0 0 48-80 0 0-48 24 0zm560 0l56 0 0 48-80 0 0-48 24 0zM90.7 341.5C67.6 353 49.8 361.9 37.3 368.2L15.8 325.3C46.2 310 87 289.7 90.7 287.8l21.5 42.9-21.5 10.7zm458.5-53.7l74.9 37.5-21.5 42.9-74.9-37.5 21.5-42.9z'/%3E%3C/svg%3E"); } + .fa-icon-users { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 512'%3E%3Cpath fill='currentColor' d='M320 16a104 104 0 1 1 0 208 104 104 0 1 1 0-208zM96 88a72 72 0 1 1 0 144 72 72 0 1 1 0-144zm14.7 392l-110.7 0 48-192 107 0-41.8 181.2-2.5 10.8zm418.5 0l-2.5-10.8-41.8-181.2 107 0 48 192-110.7 0zM472 160a72 72 0 1 1 144 0 72 72 0 1 1 -144 0zM208 272l224 0 48 208-320 0 48-208z'/%3E%3C/svg%3E"); } + .fa-icon-user { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M224 248a120 120 0 1 0 0-240 120 120 0 1 0 0 240zM432 512l-64-208-288 0-64 208 416 0z'/%3E%3C/svg%3E"); } + .fa-icon-desktop { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M512 32l-512 0 0 384 208 0-16 48-96 0 0 48 320 0 0-48-96 0-16-48 208 0 0-384zM448 96l0 224-384 0 0-224 384 0z'/%3E%3C/svg%3E"); } + .fa-icon-server { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M448 32l-448 0 0 192 448 0 0-192zM280 104a24 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 0zM448 288l-448 0 0 192 448 0 0-192zM280 360a24 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 0-64z'/%3E%3C/svg%3E"); } + .fa-icon-shield-check { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M267.6 4.5l207.5 80.5 19.2 7.4 1.2 20.5c2.9 50-4.9 126.3-37.3 200.9-32.7 75.2-91.1 150-189.4 192.6l-12.7 5.5-12.7-5.5C144.9 463.9 86.6 389.2 53.9 313.9 21.5 239.3 13.7 162.9 16.6 113L17.8 92.5 37 85 244.5 4.5 256 0 267.6 4.5zm45.8 165.3L227.8 287.6c-19.3-20-33.7-34.9-43.2-44.7l-34.5 33.3c6.2 6.4 27.2 28.1 63.1 65.2l19.8 20.6 16.8-23.1 102.4-140.8 14.1-19.4-38.8-28.2-14.1 19.4z'/%3E%3C/svg%3E"); } + .fa-icon-phone { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M0 64L128 0 224.8 128 144 208c33 70.4 89.6 127 160 160l80-80.8 128 96.8-64 128-16 0C193.4 512 0 318.6 0 80L0 64z'/%3E%3C/svg%3E"); } + .fa-icon-building { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath fill='currentColor' d='M384 0L0 0 0 512 384 512 384 0zM240 352l0 112-96 0 0-112 96 0zM96 96l64 0 0 64-64 0 0-64zm192 0l0 64-64 0 0-64 64 0zM96 224l64 0 0 64-64 0 0-64zm192 0l0 64-64 0 0-64 64 0z'/%3E%3C/svg%3E"); } + .fa-icon-print { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M64 0l304 0 80 80 0 64-384 0 0-144zM0 192l512 0 0 192-64 0 0 128-384 0 0-128-64 0 0-192zM128 416l0 32 256 0 0-96-256 0 0 64zM456 272a24 24 0 1 0 -48 0 24 24 0 1 0 48 0z'/%3E%3C/svg%3E"); } + .fa-icon-file-code { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath fill='currentColor' d='M0 0L240 0 384 144 384 512 0 512 0 0zM208 58.5L208 176 325.5 176 208 58.5zM154.2 295.6l15.6-18.2-36.4-31.2c-5 5.9-26.2 30.6-63.6 74.2L56.4 336c3.2 3.7 23.6 27.6 61.4 71.6l15.6 18.2 36.4-31.2c-7.2-8.5-24-28-50.2-58.6l34.6-40.4zm112-31.2l-15.6-18.2-36.4 31.2c7.2 8.5 24 28 50.2 58.6-26.2 30.6-43 50.1-50.2 58.6l36.4 31.2c5-5.8 26.2-30.6 63.6-74.2L327.6 336c-3.2-3.7-23.6-27.6-61.4-71.6z'/%3E%3C/svg%3E"); } + .fa-icon-location-dot { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath fill='currentColor' d='M0 188.6C0 84.4 86 0 192 0S384 84.4 384 188.6C384 339.4 192 528 192 528S0 339.4 0 188.6zM192 256a64 64 0 1 0 0-128 64 64 0 1 0 0 128z'/%3E%3C/svg%3E"); } + .fa-icon-wrench-simple { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath fill='currentColor' d='M128 160l64 64 64-64 0-160c74.6 26.4 128 92.4 128 176 0 71.1-38.6 133.1-96 166.3l0 169.7-192 0 0-169.7C38.6 309.1 0 247.1 0 176 0 92.4 53.4 26.4 128 0l0 160z'/%3E%3C/svg%3E"); } + .fa-icon-chart-line-up { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M64 32l0 384 448 0 0 64-512 0 0-448 64 0zM304 349.3c-6.6-6.6-38.6-38.6-96-96-30.7 30.7-52 52-64 64L98.7 272c8.2-8.2 37.1-37.1 86.6-86.6L208 162.7c6.6 6.6 38.6 38.6 96 96l65.4-65.4-65.4-65.4 176 0 0 176-65.4-65.4C345.1 308.2 308.2 345.1 304 349.3z'/%3E%3C/svg%3E"); } + .fa-icon-users-gear { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 512'%3E%3Cpath fill='currentColor' d='M320 224a104 104 0 1 0 0-208 104 104 0 1 0 0 208zM96 232a72 72 0 1 0 0-144 72 72 0 1 0 0 144zm14.7 248l2.5-10.8 41.8-181.2-107 0-48 192 110.7 0zm49.3 0l184.6 0-2.1-3.7-24-41.6 32.4-18.7-32.4-18.7c14.7-25.4 33.3-57.7 56-97l16.3-28.3-182.8 0-48 208zM616 160a72 72 0 1 0 -144 0 72 72 0 1 0 144 0zM544 288l-64 0 0 33.2c-12.7 4.3-24.2 11-34 19.7l-28.8-16.6-32 55.4 28.8 16.6c-1.3 6.4-1.9 12.9-1.9 19.7s.7 13.3 1.9 19.7l-28.8 16.6 32 55.4 28.8-16.6c9.8 8.7 21.4 15.4 34.1 19.7l0 33.2 64 0 0-33.2c12.7-4.3 24.2-11 34.1-19.7l28.8 16.6 32-55.4-28.8-16.6c1.3-6.4 1.9-12.9 1.9-19.7s-.7-13.3-1.9-19.7l28.8-16.6-32-55.4-28.8 16.6c-9.8-8.7-21.4-15.4-34-19.7l0-33.2zM472 416a40 40 0 1 1 80 0 40 40 0 1 1 -80 0z'/%3E%3C/svg%3E"); } + .fa-icon-network-wired { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath fill='currentColor' d='M328 88l0 48-80 0 0-48 80 0zM248 32l-56 0 0 160 64 0 0 32-256 0 0 64 128 0 0 32-64 0 0 160 192 0 0-160-64 0 0-32 192 0 0 32-64 0 0 160 192 0 0-160-64 0 0-32 128 0 0-64-256 0 0-32 64 0 0-160-136 0zM120 376l80 0 0 48-80 0 0-48zm336 0l0 48-80 0 0-48 80 0z'/%3E%3C/svg%3E"); } + .fa-icon-book-open-cover { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath fill='currentColor' d='M288 140l0 248.1 27.8-9.9c48.4-17.3 99.3-26.1 150.7-26.1l45.5 0 0-256-45.5 0c-36.7 0-73.1 6.3-107.6 18.6-37.5 13.4-61.2 21.8-70.9 25.3zM512 32l64 0 0 384-109.5 0c-44 0-87.7 7.6-129.2 22.4L288 456 238.6 438.4C197.2 423.6 153.5 416 109.5 416L0 416 0 32 109.5 32c44 0 87.7 7.6 129.2 22.4L288 72 337.4 54.4C378.8 39.6 422.5 32 466.5 32L512 32zM0 464l109.5 0c46.8 0 93.2 8 137.2 23.8l41.3 14.7 41.3-14.7c44-15.7 90.5-23.8 137.2-23.8l109.5 0 0 48-109.5 0c-41.3 0-82.2 7.1-121.1 21l-49.4 17.6-8.1 2.9-8.1-2.9-49.4-17.6c-38.9-13.9-79.8-21-121.1-21L0 512 0 464z'/%3E%3C/svg%3E"); } + .fa-icon-handshake { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath fill='currentColor' d='M288 64l0 0 128 0 48 32 112-64 0 288-70.4 56C411.3 282.5 346.8 218.5 312 184l0 0-47.4 47.4c-21.5 21.5-52.1 28.2-79.3 20.1-.4-.1-.8-.2-1.1-.3-7-2.2-13.8-5.4-20.1-9.5-.4-.3-.9-.6-1.3-.9-3.9-2.7-7.7-5.8-11.2-9.4L144 224 288 64zM160 64l63.4 0c-90.8 100.9-139.4 154.8-145.6 161.7 25.6 25.6 38.8 38.8 39.6 39.6 50 50 131 50 181 0l13.6-13.6 157.9 156.6-41.3 34.4c-26.9-26.9-44.5-44.5-52.7-52.7L342.1 424c8.7 8.7 25.2 25.2 49.6 49.6l-7.7 6.4-62.1 0c-31.3-31.3-50.6-50.6-57.9-57.9L230.1 456c13 13 21 21 24 24L192 480 0 320 0 32 112 96 160 64z'/%3E%3C/svg%3E"); } + .fa-icon-cloud { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath fill='currentColor' d='M448 480L0 480 0 336c0-62.7 40.1-116 96-135.8L96 176c0-79.5 64.5-144 144-144 55.4 0 103.5 31.3 127.6 77.1 14.2-8.3 30.8-13.1 48.4-13.1 53 0 96 43 96 96l0 49.1c38.3 22.1 64 63.5 64 110.9l0 128-128 0z'/%3E%3C/svg%3E"); } + .fa-icon-tv-retro { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M160-13.3c6.6 6.6 38.6 38.6 96 96 57.4-57.4 89.4-89.4 96-96L397.3 32c-12 12-33.3 33.3-64 64l178.7 0 0 384-512 0 0-384 178.7 0c-30.7-30.7-52-52-64-64L160-13.3zM384 160l-320 0 0 256 320 0 0-256zm88 72a24 24 0 1 0 -48 0 24 24 0 1 0 48 0zM448 336a24 24 0 1 0 0-48 24 24 0 1 0 0 48z'/%3E%3C/svg%3E"); } + .fa-icon-triangle-exclamation { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath fill='currentColor' d='M560 480L16 480 288-16 560 480zM260 356l0 56 56 0 0-56-56 0zm-4-196l12.8 160 38.4 0 12.8-160-64 0z'/%3E%3C/svg%3E"); } + .fa-icon-hard-drive { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M448 32l-448 0 0 208 448 0 0-208zm0 256l-448 0 0 192 448 0 0-192zM224 384a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm128-32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z'/%3E%3C/svg%3E"); } + .fa-icon-up-right-and-down-left-from-center { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M512 0l0 208-71-71-96 96-17 17c-8.8-8.8-25.1-25.1-49-49l-17-17 17-17 96-96-71-71 208 0zM0 512l0-208 71 71 96-96 17-17c8.8 8.8 25.1 25.1 49 49l17 17-17 17-96 96 71 71-208 0z'/%3E%3C/svg%3E"); } + .fa-icon-down-left-and-up-right-to-center { --fa-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath fill='currentColor' d='M488.4-9.9c8.8 8.8 25.1 25.1 49 49l17 17-17 17-96 96 71 71-208 0 0-208 71 71 96-96 17-17zM272.4 272l0 208-71-71-96 96-17 17c-8.8-8.8-25.1-25.1-49-49l-17-17 17-17 96-96-71-71 208 0z'/%3E%3C/svg%3E"); } + .main-col > .section:first-of-type { margin-top: var(--space-lg); } + #sec-02 { order: 1; } + #sec-03 { order: 2; } + #sec-01 { order: 3; } + #sec-04 { order: 4; } + #sec-05 { order: 5; } + #sec-06 { order: 6; } + + /* ── GROUP LABEL — "Managed IT Services" eyebrow above section I ── */ + .group-label { + order: 0; + margin-left: var(--section-offset); + margin-bottom: calc(var(--space-xs) * -1); + font-family: 'DM Mono', monospace; + font-size: 0.6875rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--accent); + opacity: 0.7; + } + .group-label-sections { + opacity: 0.6; + letter-spacing: 0.06em; + } + + /* ── GROUP DIVIDER — separates Server Managed from Site Management ── */ + .group-divider { + order: 3; + border: none; + border-top: 1px solid var(--border); + margin: var(--space-xs) var(--section-offset) var(--space-xs) var(--section-offset); + opacity: 0.5; + } + + /* ── GROUP STRIP — bracket alongside sections I–III ── */ + #sec-02::after, #sec-03::after, #sec-01::after { + content: ''; + position: absolute; + left: calc(var(--section-offset) * -1 + 2px); + top: -8px; + bottom: -8px; + width: 6px; + border-left: 3px solid var(--group-strip); + pointer-events: none; + z-index: 0; + } + /* Top cap — wraps around top of first section */ + #sec-02::after { + top: 50%; + border-top: 3px solid var(--group-strip); + border-top-left-radius: 4px; + } + /* Bottom cap — wraps around bottom of last section */ + #sec-01::after { + bottom: 50%; + border-bottom: 3px solid var(--group-strip); + border-bottom-left-radius: 4px; + } + + .section-header { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto; + align-items: start; + gap: var(--space-stack) var(--space-lg); + margin-bottom: var(--space-2xl); + } + .section:not(.sec-open) .section-header { + margin-bottom: 0; + grid-template-columns: minmax(0, 1fr) auto; + row-gap: var(--space-md); + column-gap: var(--space-stack); + } + .section-num { + font-family: 'Cinzel', serif; + font-weight: 700; + font-size: var(--section-num-size); + line-height: 1; + color: var(--border); + flex-shrink: 0; + width: var(--section-num-width); + user-select: none; + position: absolute; + left: calc(var(--section-offset) * -1); + top: calc(var(--section-padding-top) - 2px); + text-align: right; + } + .section-title-block { + min-width: 0; + max-width: none; + width: 100%; + grid-column: 1; + grid-row: 1; + } + .section:not(.sec-open) .section-title-block { + grid-column: 1; + grid-row: 1; + max-width: none; + width: 100%; + } + .section:not(.sec-open) .section-num { + position: absolute; + left: calc(var(--section-offset) * -1); + top: calc(var(--section-padding-top) - 2px); + width: var(--section-num-width); + font-size: clamp(22px, 2.2vw, 40px); + color: var(--accent); + text-align: right; + opacity: 0.82; + } + .section:not(.sec-open) .section-title { + max-width: none; + } + .section-title { + font-family: 'Poppins', sans-serif; + font-size: clamp(1.375rem, 1.5vw, 1.75rem); + font-weight: 600; + color: var(--ink); + line-height: var(--text-title-line); + word-break: break-word; + max-width: none; + text-wrap: pretty; + } + .section-title-tag { font-size: 0.9375rem; font-weight: 400; opacity: 0.6; } + .section-subtitle { + grid-column: 1 / -1; + grid-row: 3; + font-size: var(--text-copy-size); + color: var(--muted); + margin-top: var(--space-xs); + line-height: var(--text-copy-line); + max-width: none; + } + .section-badge { + font-family: 'DM Mono', monospace; + font-size: var(--text-label-size); + text-transform: uppercase; + letter-spacing: 0.1em; + padding: var(--control-pad-y-tight) var(--control-pad-x-tight); + border: 1px solid var(--border); + border-radius: var(--radius-control); + color: var(--muted); + display: inline-block; + margin-top: var(--space-stack-tight); + } + .section-toggle { cursor: pointer; user-select: none; } + .sec-chevron { + display: flex; + align-items: center; + justify-content: center; + align-self: start; + grid-column: 3; + grid-row: 1; + color: var(--muted); + transition: transform var(--transition-medium) ease, color var(--transition-fast); + flex-shrink: 0; + transform: rotate(0deg); + width: 34px; + height: 34px; + background: var(--surface-chevron); + border-radius: var(--radius-control); + margin-top: 2px; + } + .sec-open .sec-chevron { transform: rotate(180deg); color: var(--ink); background: var(--surface-chevron-active); } + .section-toggle:hover .sec-chevron { color: var(--ink); background: var(--surface-chevron-active); } + .sec-chevron svg { display: block; } + .section-body { + overflow: hidden; + opacity: 1; + transition: height 0.34s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.22s ease; + will-change: height, opacity; + } + .section-content { min-width: 0; } + .section-content > * + * { margin-top: var(--space-stack); } + .section-content > .collapsible-header + .collapsible-body, + .section-content > .collapsible-body + .collapsible-header { + margin-top: 0; + } + .section-content > .feature-card-grid + .collapsible-header, + .section-content > .callout-green + .feature-card-grid, + .section-content > .callout-red + .feature-card-grid { + margin-top: var(--space-stack-tight); + } + + @container (max-width: 520px) { + .section { + margin-left: 0; + padding: clamp(18px, 5cqi, 24px) clamp(18px, 5cqi, 22px) clamp(22px, 6cqi, 26px); + } + + .section-header, + .section:not(.sec-open) .section-header { + grid-template-columns: auto minmax(0, 1fr) auto; + column-gap: var(--space-stack-tight); + row-gap: var(--space-sm); + align-items: center; + } + + .section-num, + .section:not(.sec-open) .section-num { + position: static; + grid-column: 1; + grid-row: 1; + width: auto; + font-size: 1rem; + color: var(--accent); + text-align: left; + opacity: 0.85; + align-self: center; + margin-top: 0; + } + + .section-title-block, + .section:not(.sec-open) .section-title-block { + grid-column: 2; + grid-row: 1; + max-width: none; + width: 100%; + } + + .section-title { + font-size: 1.1rem; + max-width: none; + } + + .section-subtitle { + font-size: 0.8125rem; + max-width: none; + } + + .sec-chevron { + grid-column: 3; + grid-row: 1; + align-self: center; + margin-top: 0; + } + + } + + /* ── SECTION SUMMARY BADGE ────────────────────────────────────── + Shown only when section is COLLAPSED (display:none by default). + JS: setSummary(id, text) sets textContent + display:inline-block + when collapsed(secId) && !!text. Hidden when section is open. + On mobile (≤600px) placed in grid col 2 row 1 (top-right of header). + ─────────────────────────────────────────────────────────────── */ + @keyframes badgeFadeIn { + from { opacity: 0; transform: translateY(-4px); } + to { opacity: 1; transform: translateY(0); } + } + .sec-summary-badge { + display: none; + align-items: center; + font-family: 'DM Mono', monospace; + font-size: 13px; + font-weight: 500; + letter-spacing: 0.08em; + color: var(--accent); + background: var(--surface-summary-badge); + border: 2px solid var(--border-summary-badge); + border-radius: var(--radius-control); + padding: var(--control-pad-y-tight) var(--space-md); + white-space: normal; + line-height: var(--text-compact-line); + max-width: min(100%, 26ch); + margin-top: var(--space-md); + text-align: left; + align-self: flex-start; + animation: badgeFadeIn 0.25s ease both; + } + .sec-open .sec-summary-badge { + display: none !important; + } + .section:not(.sec-open) .sec-summary-badge { + width: fit-content; + max-width: 100%; + margin-top: var(--space-md); + } + .section:not(.sec-open) .sec-chevron { + position: static; + grid-column: 2; + grid-row: 1; + justify-self: end; + margin-top: 2px; + } + /* Override collapsed chevron + summary badge position at small container */ + @container (max-width: 520px) { + .section:not(.sec-open) .sec-chevron { + grid-column: 3; + align-self: center; + margin-top: 0; + } + .section:not(.sec-open) .sec-summary-badge { + width: 100%; + max-width: none; + margin-top: 0; + } + } + + /* Hide subtitle when collapsed — title + controls are enough */ + .section:not(.sec-open) .section-subtitle { display: none; } + + /* ── CONTROLS ROW (stepper + badge + summary — full-width row 2) ── */ + .sec-controls-row { + grid-column: 1 / -1; + grid-row: 2; + display: flex; + align-items: stretch; + gap: var(--space-stack-tight); + margin-top: var(--space-xs); + } + .sec-controls-row .num-stepper { + flex: 0 0 auto; + } + .sec-controls-row .num-input { + width: 56px; + } + .section .sec-controls-row > .section-badge, + .section .sec-controls-row > .sec-summary-badge { + display: flex; + align-items: center; + justify-content: center; + align-self: stretch; + min-height: 40px; + margin: 0; + padding: 0 var(--control-pad-x-tight); + white-space: nowrap; + line-height: 1; + border-width: 1px; + box-sizing: border-box; + max-width: none; + } + .section .sec-controls-row > .section-badge { + flex: 1 1 0%; + } + .section .sec-controls-row > .sec-summary-badge { + flex: 0 0 auto; + } + .sec-open .sec-controls-row > .sec-summary-badge { display: none !important; } + + /* ── Controls row: small container — stack full-width ── */ + @container (max-width: 520px) { + .sec-controls-row { + grid-row: 2; + 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; + } + } + + /* ── PILL TOGGLE (Section II — M365 vs BYOL) ─────────────────── + CSS-only toggle using hidden radio inputs + adjacent label styling. + input:checked + label gets accent background. + JS reads: document.getElementById("rateBYOL").checked + On mobile (≤600px) stacks vertically (grid-template-columns:1fr). + ─────────────────────────────────────────────────────────────── */ + .pill-toggle { + display: grid; + grid-template-columns: 1fr 1fr; + border: 1px solid var(--border); + border-radius: var(--radius-control); + overflow: hidden; + margin-bottom: 0; + } + .pill-toggle input[type=radio] { display: none; } + .pill-toggle label { + padding: var(--space-lg) var(--space-xl); + cursor: pointer; + border-right: 1px solid var(--border); + transition: background var(--transition-fast); + display: flex; + flex-direction: column; + gap: 6px; + } + .pill-toggle label:last-child { border-right: none; } + .pill-toggle input:focus-visible + label { + outline: 2px solid var(--accent); + outline-offset: -2px; + } + .pill-toggle input:checked + label { + background: var(--accent); + color: var(--text-on-accent); + } + .pill-toggle input:checked + label .pill-price { color: var(--text-on-accent); } + .pill-toggle input:checked + label .pill-desc { color: var(--text-on-accent-strong); } + .pill-toggle label .pill-price { + font-family: 'DM Mono', monospace; + font-size: 1.25rem; + font-weight: 500; + color: var(--ink); + } + .pill-toggle label .pill-price small { font-size: 0.875rem; opacity: 0.6; } + .pill-toggle label .pill-desc { font-size: 0.875rem; opacity: 0.7; } + .pill-toggle label .pill-savings { + display: block; + font-size: 0.75rem; + font-family: 'DM Mono', monospace; + color: var(--green); + margin-top: var(--space-xs); + letter-spacing: 0.02em; + } + .pill-toggle input:checked + label .pill-savings { color: var(--text-pill-savings-active); } + .pill-price .m365-price-grey { + color: var(--muted); + text-decoration: line-through; + opacity: 0.5; + margin-right: 6px; + font-size: 1rem; + } + .pill-toggle input:checked + label .m365-price-grey { + color: var(--text-on-accent); + opacity: 0.45; + } + + /* ── TIER SEGMENT (Section VI — VoIP Basic/Standard/Premium) ─── + 3-column radio toggle. JS activateTier(tier) adds .active class. + .active overrides text colours to white on accent background. + Rates: basic $28 | standard $35 | premium $45 /seat/mo. + ─────────────────────────────────────────────────────────────── */ + .tier-seg-wrap { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + border: 1px solid var(--border); + border-radius: var(--radius-control); + overflow: hidden; + margin-bottom: 0; + } + .tier-seg-wrap input[type=radio] { display: none; } + .tier-seg-wrap input[type=radio]:focus-visible + .tier-seg { + outline: 2px solid var(--accent); + outline-offset: -2px; + } + .tier-seg { + padding: var(--space-stack-roomy) var(--space-stack-tight); + cursor: pointer; + border-right: 1px solid var(--border); + text-align: center; + transition: background var(--transition-fast); + } + .tier-seg:last-of-type { border-right: none; } + .tier-seg.active { background: var(--accent); } + .tier-seg .tier-name { + font-family: 'DM Mono', monospace; + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.07em; + color: var(--muted); + } + .tier-seg.active .tier-name { color: var(--text-on-accent); } + .tier-seg .tier-price { + font-family: 'DM Mono', monospace; + font-size: 1.375rem; + color: var(--ink); + margin-top: 3px; + } + .tier-seg.active .tier-price { color: var(--text-on-accent); } + .tier-seg .tier-sub { font-size: 0.75rem; color: var(--muted); margin-top: 2px; } + .tier-seg.active .tier-sub { color: var(--text-on-accent-strong); } + + /* ── INNER COLLAPSIBLES (What's Included / Add-Ons) ──────────── + Separate from section-level collapse. JS toggleCollapsible(id) + toggles .open on .collapsible-body and swaps +/- on toggle icon. + .addon-preview-pill pills shown when collapsed (JS toggleCollapsible). + ─────────────────────────────────────────────────────────────── */ + .collapsible-header { + display: flex; + align-items: center; + gap: var(--space-sm); + cursor: pointer; + padding: var(--space-md) 0; + border-top: 1px solid var(--border); + user-select: none; + } + .collapsible-header--mt16 { margin-top: var(--space-stack-roomy); } + .collapsible-header--addon { flex-wrap: wrap; gap: var(--space-xs); margin-top: 0; } + .collapsible-toggle { + color: var(--accent); + width: 22px; + flex-shrink: 0; + display: flex; + align-items: center; + transition: transform var(--transition-medium) ease; + } + .collapsible-toggle.open { transform: rotate(180deg); } + .collapsible-toggle .fa-icon { display: block; } + .collapsible-label { + font-family: 'DM Mono', monospace; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.07em; + color: var(--muted); + } + .addon-preview-wrap { + display: flex; flex-wrap: wrap; gap: 5px; + width: 100%; padding-left: var(--space-3xl); margin-top: 6px; + } + .addon-preview-pill { + font-family: 'DM Mono', monospace; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.07em; + color: var(--muted); + border: 1px solid var(--border); + border-radius: var(--radius-control); + padding: var(--control-pad-y-tight) var(--control-pad-x-tight); + white-space: nowrap; + transition: color var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast); + } + .addon-preview-pill.active { + color: var(--green); + border-color: var(--green); + background: var(--surface-positive-pill); + } + .collapsible-body { + padding: var(--space-stack-tight) 0 var(--space-stack-tight) var(--space-3xl); + overflow: hidden; + max-height: 0; + opacity: 0; + transition: max-height 0.3s ease, opacity 0.2s ease; + } + .collapsible-body.open { + max-height: 2000px; + opacity: 1; + } + + /* FEATURE LIST */ + .feature-list { + list-style: none; + display: flex; + flex-direction: column; + gap: var(--space-md); + margin: var(--space-xs) 0 0; + padding: 0; + } + .feature-list li { + font-size: var(--text-copy-size); + color: var(--muted); + line-height: var(--text-copy-line); + padding-left: var(--space-lg); + position: relative; + } + .feature-list li::before { + content: '✓'; + position: absolute; + left: 0; + color: var(--green); + font-size: 11px; + } + .byol-mode .m365-feature { text-decoration: line-through; opacity: 0.55; } + .byol-mode .m365-feature::before { color: var(--amber); } + + /* ── NUMBER INPUTS ────────────────────────────────────────────── + .input-row — flex row: label left, .num-input right + .num-input — DM Mono, text-align:center, oninput→update() + On mobile (≤600px) input-row stacks (flex-direction:column) + and num-input goes full width. + ─────────────────────────────────────────────────────────────── */ + .input-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-stack-roomy) 0; + border-top: 1px solid var(--border); + } + .input-label { font-family: 'Lato', sans-serif; font-weight: 700; font-size: 16px; } + .input-sublabel { font-size: 13px; color: var(--muted); margin-top: 6px; line-height: var(--text-compact-line); } + .num-stepper { + display: flex; + align-items: stretch; + flex-shrink: 0; + } + .step-btn { + background: var(--surface-step); + border: 1px solid var(--surface-step-border); + color: var(--text-step); + font-size: 20px; + font-weight: 400; + width: 36px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background 0.12s, color 0.12s; + flex-shrink: 0; + user-select: none; + line-height: 1; + } + .step-btn:first-child { border-radius: var(--radius-control) 0 0 var(--radius-control); border-right: none; } + .step-btn:last-child { border-radius: 0 var(--radius-control) var(--radius-control) 0; border-left: none; } + .step-btn:hover { background: var(--surface-step-hover); color: var(--ink); } + .step-btn:active { background: var(--surface-step-active); color: var(--btn-primary-fg); border-color: var(--accent); } + .num-input { + background: var(--surface-input); + border: 1px solid var(--surface-step-border); + border-radius: 0; + color: var(--ink); + font-family: 'DM Mono', monospace; + font-size: 22px; + width: 72px; + text-align: center; + padding: var(--space-sm); + outline: none; + } + .num-input:focus { border-color: var(--accent); } + .num-input::-webkit-inner-spin-button, + .num-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } + .num-input[type=number] { -moz-appearance: textfield; } + + /* ── ADDON ROWS ───────────────────────────────────────────────── + .addon-row — clickable label wrapping a hidden checkbox. + JS toggleAddon(cbId, rowId) toggles .selected class on row. + .selected gets accent-tinted background + border. + Price is right-aligned via margin-left:auto on .addon-price. + ─────────────────────────────────────────────────────────────── */ + .addon-grid { display: flex; flex-direction: column; gap: var(--space-sm); } + .addon-row { + display: flex; + align-items: flex-start; + gap: var(--space-md); + padding: var(--space-md) var(--space-stack); + border-radius: 8px; + cursor: pointer; + border: 1px solid transparent; + transition: background 0.12s, border-color 0.12s; + } + .addon-row:hover { + background: var(--surface-addon-hover); + border-color: var(--border-addon-hover); + } + .addon-row input[type=checkbox] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 18px; + height: 18px; + border: 2px solid var(--border); + border-radius: 4px; + background: transparent; + flex-shrink: 0; + margin-top: 3px; + cursor: pointer; + position: relative; + transition: background var(--transition-fast), border-color var(--transition-fast); + } + .addon-row input[type=checkbox]:checked { + background: var(--accent); + border-color: var(--accent); + } + .addon-row input[type=checkbox]:checked::after { + content: ''; + position: absolute; + left: 5px; + top: 1px; + width: 5px; + height: 10px; + border: solid var(--text-on-accent); + border-width: 0 2px 2px 0; + transform: rotate(45deg); + } + .addon-row:hover input[type=checkbox] { + border-color: var(--accent); + } + .addon-name { font-family: 'Lato', sans-serif; font-weight: 700; font-size: 15px; line-height: 1.35; } + .addon-price { + font-family: 'DM Mono', monospace; + font-size: 14px; + color: var(--accent); + white-space: nowrap; + margin-left: auto; + flex-shrink: 0; + } + .addon-desc { font-size: 13px; color: var(--muted); margin-top: 6px; line-height: var(--text-compact-line); } + + @container (max-width: 760px) { + .addon-row { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + grid-template-rows: auto auto; + column-gap: var(--space-stack-tight); + row-gap: var(--space-sm); + padding: var(--space-stack) var(--space-md); + align-items: start; + } + + .addon-row input[type=checkbox] { + grid-column: 1; + grid-row: 2; + margin-top: 2px; + } + + .addon-row > div { + grid-column: 2; + grid-row: 2; + min-width: 0; + } + + .addon-price { + grid-column: 1 / -1; + grid-row: 1; + margin-left: 0; + font-size: 0.8125rem; + white-space: normal; + } + + .addon-name { + font-size: 0.875rem; + } + + .addon-desc { + font-size: 0.75rem; + } + } + + /* ── BYOL CALLOUTS (Section II) ──────────────────────────────── + Shown/hidden by JS based on BYOL toggle state. + #byolCalloutGreen — M365 savings message (shown when M365 selected) + #byolCalloutRed — missed savings warning (shown when BYOL selected) + display:flex + align-items:flex-start keeps icon pinned to + first line; text wrapped in so it never flows under icon. + ─────────────────────────────────────────────────────────────── */ + .callout-green { + background: var(--surface-success); + border: 1px solid var(--surface-success-border); + border-radius: var(--radius-control); + padding: var(--space-stack-roomy) var(--space-xl); + font-family: 'DM Mono', monospace; + font-size: 14px; + color: var(--green); + margin-bottom: 0; + line-height: 1.65; + display: flex; + align-items: flex-start; + gap: 9px; + } + .callout-red { + background: var(--surface-danger); + border: 1px solid var(--surface-danger-border); + border-radius: var(--radius-control); + padding: var(--space-stack-roomy) var(--space-xl); + font-family: 'DM Mono', monospace; + font-size: 14px; + color: var(--text-danger); + margin-bottom: 0; + line-height: 1.65; + display: flex; + align-items: flex-start; + gap: 9px; + } + .hidden { display: none !important; } + + /* ── ADMIN FEE PROGRESS BAR (Section I) ──────────────────────── + Shows progress toward $650 engagement threshold. + JS: progressEl width% = (baseSubtotal / ADMIN_FEE_MINIMUM) * 100 + baseSubtotal = users + endpoints + servers (NOT VoIP or ZT). + Turns green at 100% (threshold met), stays blue below. + ─────────────────────────────────────────────────────────────── */ + .progress-wrap { margin: var(--space-stack) 0 var(--space-stack-tight); } + /* Threshold bar promoted outside section-body for always-visible display */ + .sec-01-threshold.progress-wrap { margin: var(--space-stack-roomy) 0 0; } + .sec-01-threshold.floor-note { margin-top: var(--space-stack-tight); } + .section:not(.sec-open) .sec-01-threshold.progress-wrap { margin-top: var(--space-stack); margin-bottom: 0; } + .sec-open .sec-01-threshold + .floor-note + .section-body { margin-top: 0; } + .progress-label { + font-family: 'DM Mono', monospace; + font-size: 12px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--muted); + display: flex; + justify-content: space-between; + margin-bottom: 6px; + } + .progress-track { + height: 7px; + background: var(--border); + border-radius: 4px; + overflow: hidden; + } + .progress-fill { + height: 100%; + border-radius: 3px; + background: var(--accent); + transition: width 0.3s ease, background 0.3s; + } + + /* ── ADMIN FEE BREAKDOWN TABLE (Section I) ───────────────────── + Shows: Base Site Admin | ZT Supplement (if active) | Total. + .fee-total row has top border separator and bold text. + All values rendered by update() via getEl('sl-admin-val') etc. + ─────────────────────────────────────────────────────────────── */ + .fee-table { width: 100%; border-collapse: collapse; margin-top: var(--space-stack-roomy); font-size: 14px; } + .fee-table td { padding: var(--space-sm) 0; color: var(--muted); } + .fee-table td:last-child { text-align: right; font-family: 'DM Mono', monospace; color: var(--ink); } + .fee-table tr.fee-total td { border-top: 1px solid var(--border); padding-top: var(--space-stack); color: var(--ink); font-weight: 600; } + + /* ── FEATURE CARDS (Section I — What's Covered) ──────────────── + Static content — 8 cards in single-column grid. + Each card has icon (inline SVG) + title (Poppins) + desc (Lato). + Not dynamically rendered; content is hard-coded in HTML. + ─────────────────────────────────────────────────────────────── */ + .feature-card-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: var(--space-stack-roomy); + margin-top: 0; + } + .feature-card { + background: var(--surface-feature); + border: 1px solid var(--border); + border-radius: 10px; + padding: var(--space-stack) var(--space-stack-roomy); + } + .feature-card-title { font-family: 'Poppins', sans-serif; font-weight: 600; font-size: 0.9375rem; margin-bottom: var(--space-sm); display:flex; align-items:center; line-height: 1.35; } + .feature-card-desc { font-size: 0.8125rem; color: var(--muted); line-height: var(--text-copy-line); } + + .m365-app-strip { + border: 1px solid var(--border); + border-radius: 10px; + padding: var(--space-stack-roomy) var(--space-lg) var(--space-stack); + background: var(--surface-feature); + } + .m365-app-list { + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: var(--space-md); + } + .m365-app-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--space-sm); + text-align: center; + padding: var(--space-stack-tight) var(--space-sm); + border-radius: var(--radius-control); + background: color-mix(in srgb, var(--surface-accent-soft) 44%, transparent); + } + .m365-app-icon { + width: 17px; + height: 17px; + object-fit: contain; + display: block; + } + .m365-app-name { + font-family: 'DM Mono', monospace; + font-size: 0.75rem; + letter-spacing: 0.05em; + color: var(--ink); + } + .m365-app-strip-note { + margin-top: var(--space-md); + font-family: 'DM Mono', monospace; + font-size: 0.75rem; + letter-spacing: 0.05em; + color: var(--muted); + } + .m365-app-note-byol { display: none; } + .m365-app-strip.byol-disabled .m365-app-item { + background: transparent; + border: 1px dashed var(--border); + } + .m365-app-strip.byol-disabled .m365-app-icon { + filter: grayscale(1) saturate(0.2); + opacity: 0.4; + } + .m365-app-strip.byol-disabled .m365-app-name { + color: var(--muted); + } + .m365-app-strip.byol-disabled .m365-app-note-default { display: none; } + .m365-app-strip.byol-disabled .m365-app-note-byol { + display: inline; + color: var(--amber); + } + + /* ── SIDEBAR (Desktop only, ≤1100px hidden) ───────────────────── + .sidebar-header — accent blue, shows "SVS MSP — LIVE QUOTE" + client name + .sidebar-body — all pricing lines, MRR total, notes, VS comparison, + nudge banner, export button + .sidebar-line — each service line (icon + label + value) + .sidebar-mrr — large Poppins 48px MRR total + IMPORTANT: nudgeBanner sits between .sidebar-header and .sidebar-body. + On mobile: the desktop sidebar is cloned into .mobile-panel-sheet + with _m IDs and synced by update() via syncEl/syncClass. + ─────────────────────────────────────────────────────────────── */ + .sidebar { + --sidebar-rule-color: var(--sidebar-line-rule); + --sidebar-copy-size: 0.84375rem; + --sidebar-copy-line: 1.5; + --sidebar-note-size: 0.78125rem; + --sidebar-mono-size: 0.75rem; + --sidebar-mrr-size: 2.8125rem; + background: var(--surface-sidebar); + border: 1px solid var(--border-sidebar); + border-radius: var(--radius-card); + overflow: hidden; + box-shadow: var(--shadow-sidebar); + } + .sidebar-focus-backdrop { + position: fixed; + inset: 0; + background: var(--surface-sidebar-focus-backdrop); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + opacity: 0; + pointer-events: none; + transition: opacity 0.2s ease; + z-index: 520; + } + .sidebar-header { + padding: var(--space-stack) 22px var(--space-md); + background: var(--surface-sidebar-header); + } + .sidebar-header-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-md); + } + .sidebar-title { + font-family: 'DM Mono', monospace; + font-size: 0.6875rem; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--text-sidebar-kicker); + margin-bottom: 0; + } + /* Print button inside sidebar header — only visible in focus mode */ + .sidebar-focus-print-btn { + display: none; + align-items: center; + justify-content: center; + height: 34px; + padding: 0 var(--space-stack); + border: 1px solid var(--border-overlay-btn); + border-radius: 10px; + background: var(--surface-overlay-btn); + color: var(--text-sidebar-kicker); + font-family: 'DM Mono', monospace; + font-size: 0.6875rem; + letter-spacing: 0.08em; + text-transform: uppercase; + cursor: pointer; + transition: background var(--transition-fast) ease, border-color var(--transition-fast) ease; + flex: 0 0 auto; + margin-left: auto; + } + .sidebar-focus-print-btn:hover { + background: var(--surface-overlay-btn-hover); + border-color: var(--border-overlay-btn-hover); + } + .sidebar-focus-print-btn:active { transform: translateY(1px); } + .sidebar-focus-toggle { + width: 34px; + height: 34px; + border: 1px solid var(--border-overlay-btn); + border-radius: 10px; + background: var(--surface-overlay-btn); + color: var(--text-sidebar-kicker); + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background var(--transition-fast) ease, border-color var(--transition-fast) ease, transform var(--transition-fast) ease; + flex: 0 0 auto; + } + .sidebar-focus-toggle:hover { + background: var(--surface-overlay-btn-hover); + border-color: var(--border-overlay-btn-hover); + } + .sidebar-focus-toggle:active { transform: translateY(1px); } + .sidebar-focus-toggle:focus-visible { + outline: 2px solid var(--focus-ring-overlay); + outline-offset: 2px; + } + .sidebar-focus-icon-close { display: none; } + .sidebar-focus-client { + display: none; + margin: var(--space-xs) 0 var(--space-lg); + padding-top: 0; + border-top: none; + } + .sidebar-focus-client-label { + display: block; + font-family: 'DM Mono', monospace; + font-size: 0.625rem; + letter-spacing: 0.14em; + text-transform: uppercase; + color: color-mix(in srgb, var(--ink) 62%, var(--muted)); + margin-bottom: var(--space-sm); + } + .sidebar-focus-client-name { + display: block; + font-family: 'Poppins', sans-serif; + font-size: 1rem; + font-weight: 600; + line-height: 1.25; + color: var(--ink); + margin-bottom: var(--space-stack); + } + .sidebar-body { padding: var(--space-stack) 22px var(--space-xl); background: var(--surface-sidebar-body); } + .sidebar-focus-columns { display: block; } + .sidebar-focus-col + .sidebar-focus-col { margin-top: var(--space-stack); } + .sidebar-focus-col > #vsComparison { margin-top: var(--space-lg); } + .sidebar-hero { + margin-bottom: var(--space-stack); + } + .sidebar-group { + display: flex; + flex-direction: column; + padding: var(--space-stack) var(--space-stack-roomy); + border-radius: var(--radius-control); + } + .sidebar-group--monthly { + background: var(--sidebar-zone-services); + } + .sidebar-group--tax { + background: var(--sidebar-zone-tax); + } + .sidebar-group--invoice { + background: var(--sidebar-zone-invoice); + } + .sidebar-group--value { + background: var(--sidebar-zone-value); + } + .sidebar-group--summary { + background: var(--sidebar-zone-summary); + } + .sidebar-group + .sidebar-group { + margin-top: var(--space-stack); + } + /* Subtle hover tint for line scanning readability */ + .sidebar-line:hover { + background: var(--sidebar-row-stripe); + } + .sidebar-group-title { + display: block; + flex: 0 0 auto; + min-height: 12px; + font-family: 'DM Mono', monospace; + font-size: 0.625rem; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--sidebar-group-title-color); + margin: 0 0 var(--space-stack-tight); + } + .sidebar-line { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: var(--space-stack); + font-size: var(--sidebar-copy-size); + color: var(--muted); + line-height: var(--sidebar-copy-line); + padding: var(--space-stack-tight) var(--space-xs); + border-bottom: 1px var(--sidebar-line-rule-style) var(--sidebar-rule-color); + border-radius: 2px; + } + /* Remove border on last visible line before a group boundary */ + .sidebar-group > .sidebar-line:last-child, + #sidebarLines > .sidebar-line:last-of-type { + border-bottom: none; + } + .sidebar-line > span:first-child { flex: 1 1 auto; min-width: 0; } + .sidebar-line .val { + font-family: 'DM Mono', monospace; + color: var(--text-money); + font-size: var(--sidebar-copy-size); + line-height: 1.2; + letter-spacing: 0.02em; + flex: 0 0 auto; + padding-left: var(--space-sm); + text-align: right; + } + .sidebar-line .lbl-icon { margin-right: var(--space-sm); opacity: 0.82; } + .sidebar-mrr-label { + font-family: 'DM Mono', monospace; + font-size: 0.625rem; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--muted); + margin-bottom: var(--space-sm); + } + .sidebar-mrr { + font-family: 'Poppins', sans-serif; + font-weight: 700; + font-size: var(--sidebar-mrr-size); + color: var(--text-money-hero); + line-height: 0.94; + letter-spacing: -0.03em; + margin-bottom: var(--space-stack-roomy); + } + .sidebar-line-value .val, + .sidebar-line-opportunity .val { + font-weight: 600; + } + .sidebar-line-value .lbl-icon .fa-icon, + .sidebar-line-opportunity .lbl-icon .fa-icon { + width: 15px; + height: 15px; + } + .sidebar-line-opportunity { + border-bottom-style: dotted; + } + .sidebar-line-opportunity .val { + color: var(--amber); + } + + /* ── VS IN-HOUSE COMPARISON ───────────────────────────────────── + Shown only when users > 0 OR endpoints > 0 (JS toggles .hidden). + Compares SVS MRR (annualised) vs 1-person Ottawa IT hire ($85K+tools) + and 5-person team ($420K+tools). Savings rows turn amber if SVS + costs MORE than the comparison (rare at low seat counts). + updateVsComparison(q) renders this section in update(). + ─────────────────────────────────────────────────────────────── */ + .vs-header { + display: flex; + flex-direction: column; + gap: var(--space-stack-tight); + margin-bottom: var(--space-stack-roomy); + } + .vs-brand-row { + display: flex; + align-items: center; + gap: var(--space-stack-tight); + } + .vs-brand-logo { + flex: 0 0 auto; + display: block; + } + .vs-brand-name { + font-family: 'Poppins', sans-serif; + font-size: 1rem; + font-weight: 600; + line-height: 1.2; + color: var(--ink); + letter-spacing: 0.02em; + } + .vs-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; line-height: 1.56; } + .vs-table td { padding: 9px var(--space-xs); vertical-align: middle; } + .vs-table td:last-child { text-align: right; font-family: 'DM Mono', monospace; white-space: nowrap; } + .vs-table tr:first-child td { padding-top: 2px; padding-bottom: var(--space-stack-roomy); border-bottom: 1px solid var(--border); } + .vs-table tr:nth-child(2) td, + .vs-table tr:nth-child(4) td { padding-top: var(--space-stack-roomy); } + .vs-save-row td { + padding: 11px var(--space-stack); + font-size: 0.71875rem; + font-family: 'DM Mono', monospace; + letter-spacing: 0.07em; + line-height: 1.35; + } + .vs-save-row td:first-child { border-radius: 8px 0 0 8px; } + .vs-save-row td:last-child { border-radius: 0 8px 8px 0; } + .vs-label { + font-family: 'DM Mono', monospace; + font-size: 0.6875rem; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--muted); + margin-bottom: 0; + display: flex; + align-items: center; + gap: var(--space-sm); + } + .vs-label::after { content: ''; flex: 1; height: 1px; background: var(--border); } + + /* ── INSIGHT NUDGE BANNER ─────────────────────────────────────── + Contextual sales insight shown at bottom of sidebar. + Conditions (evaluated in update() → renderNudge()): + amber — ZT active + green — BYOL selected + users > 0 + green — PWM not selected + users > 0 + Auto-rotates every 30s via startNudgeRotation() (setInterval). + Manual nav via cycleNudge(dir) — does NOT reset the timer. + .nudge-nav-btn — SVG chevron pills (‹ ›), hidden when only 1 nudge. + BOTH #nudgeBanner and #nudgeBanner_m are updated by renderNudge() + via applyNudge('') and applyNudge('_m'). + nudgeBanner sits between .sidebar-header and .sidebar-body. + ─────────────────────────────────────────────────────────────── */ + .nudge-banner { + margin: 0; + padding: var(--space-xl) 22px; + font-size: 0.84375rem; + line-height: 1.6; + min-height: 132px; + box-sizing: border-box; + border-radius: var(--radius-control); + transition: opacity 0.18s ease, background var(--transition-medium) ease, border-color var(--transition-medium) ease; + } + .nudge-banner.nudge-fading { opacity: 0; } + .nudge-banner.amber { + background: var(--surface-warning); + color: var(--amber); + border-left: 4px solid var(--amber); + } + .nudge-banner.green { + background: var(--surface-success); + color: var(--green); + border-left: 4px solid var(--green); + } + .nudge-banner-label { + font-family: 'DM Mono', monospace; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.12em; + opacity: 1; + display: block; + margin-bottom: 0; + } + #nudgeCounter { + font-size: 0.75rem; + opacity: 0.55; + } + .nudge-nav-btn { + background: var(--surface-ghost); + border: 1px solid var(--border-nudge-nav); + cursor: pointer; + padding: 0; + width: 34px; + height: 34px; + border-radius: 7px; + display: flex; + align-items: center; + justify-content: center; + color: inherit; + flex-shrink: 0; + transition: background var(--transition-fast), transform 0.1s; + } + .nudge-nav-btn:hover { background: var(--surface-ghost-hover); transform: scale(1.06); } + .nudge-nav-btn:active { transform: scale(0.95); } + .nudge-nav-btn .fa-icon { display: block; --icon-size: 16px; } + + /* ── QUOTE SETTINGS BAR ───────────────────────────────────────── + Sits below the client bar, above Section I. + Left: contract term 3-way toggle. + Right: HST checkbox + one-time fee input. + Tokenized left alignment stays matched to the section gutter. + ─────────────────────────────────────────────────────────────── */ + + /* ── SECTIONS TOOLBAR (Collapse All / Expand All) ───────────────── */ + .sections-toolbar { + display: flex; + justify-content: flex-start; + margin-left: var(--section-offset); + margin-bottom: var(--space-stack); + } + .btn-toggle-all { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + min-height: var(--control-min-height); + font-family: 'DM Mono', monospace; + font-size: var(--text-meta-size); + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--muted); + background: none; + border: 1px solid var(--border); + border-radius: var(--radius-control); + padding: var(--control-pad-y) var(--control-pad-x); + cursor: pointer; + touch-action: manipulation; + transition: color var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast); + } + .btn-toggle-all:hover { + color: var(--ink); + border-color: var(--accent); + background: var(--surface-accent-soft); + } + + .quote-settings-bar { + margin-left: var(--section-offset); + padding: clamp(18px, 2.4vw, 22px) clamp(20px, 2.8vw, 26px); + display: grid; + grid-template-columns: minmax(0, 1.55fr) 1px minmax(18rem, 0.95fr); + align-items: stretch; + gap: 0; + background: var(--surface-settings); + border: 1px solid var(--border); + border-radius: var(--radius-card); + overflow: hidden; + } + .qs-group { + display: flex; + flex-direction: column; + gap: var(--space-stack-tight); + container-type: inline-size; + min-width: 0; + width: 100%; + padding: 0 clamp(18px, 2.5cqi, 24px) 0 0; + max-width: none; + } + .qs-label { + font-family: 'DM Mono', monospace; + font-size: var(--text-meta-size); + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--muted); + } + .qs-label-row { + display: flex; + flex-direction: column; + gap: var(--space-sm); + margin-bottom: 2px; + } + .qs-term-wrap { + margin-bottom: 0; + border: 1px solid var(--border-term-wrap); + border-radius: var(--space-stack); + background: var(--surface-term-wrap); + box-shadow: var(--shadow-term-wrap); + } + .qs-term-wrap .tier-seg { + padding: clamp(14px, 2.5cqi, 16px) clamp(12px, 2.3cqi, 14px) clamp(14px, 2.4cqi, 15px); + background: var(--surface-term-tile); + transition: background 0.18s, border-color 0.18s, box-shadow 0.18s, transform 0.18s; + } + .qs-term-wrap .tier-seg:hover { + background: var(--surface-term-tile-hover); + } + .qs-term-wrap .tier-seg.active { + background: var(--surface-term-tile-active); + border-color: var(--border-term-tile-active); + box-shadow: var(--shadow-term-tile-active); + } + .qs-term-wrap .tier-name { + font-size: 12px; + font-weight: 400; + margin-bottom: 6px; + color: var(--text-term-name); + word-break: normal; + overflow-wrap: normal; + text-wrap: pretty; + } + .qs-term-wrap .tier-seg.active .tier-name { + color: var(--text-term-name-active); + font-weight: 700; + } + .qs-term-wrap .tier-sub { + margin-top: 0; + line-height: var(--text-compact-line); + overflow-wrap: break-word; + word-break: normal; + text-wrap: pretty; + color: var(--text-term-sub); + font-weight: 400; + } + .qs-term-wrap .tier-seg.active .tier-sub { + color: var(--text-term-sub-active); + font-weight: 400; + } + .qs-term-wrap .tier-price { display: none; } + + /* Best Value badge on 24-month */ + .qs-best-badge { + display: inline-flex; + max-width: 100%; + font-family: 'DM Mono', monospace; + font-size: 8px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-best-value); + background: var(--surface-best-value); + border: 1px solid var(--border-best-value); + border-radius: var(--radius-control); + padding: 2px var(--space-sm); + vertical-align: middle; + margin-left: 5px; + line-height: 1.5; + white-space: nowrap; + } + .tier-seg.active .qs-best-badge { + color: var(--text-best-value-active); + background: var(--surface-best-value-active); + border-color: var(--border-best-value-active); + } + + /* Discount sub-text — !important overrides parent .tier-seg color inheritance */ + .qs-discount-sub { + color: var(--text-term-discount) !important; + font-weight: 700; + } + .tier-seg.active .qs-discount-sub { color: var(--text-term-discount-active) !important; } + .qs-savings-stack { + display: flex; + flex-direction: column; + gap: var(--space-stack-tight); + margin-top: var(--space-stack); + } + + /* Dynamic savings row — appears below selector when discounted term active */ + .qs-savings-row { + display: flex; + align-items: center; + gap: var(--space-stack-tight); + font-family: 'DM Mono', monospace; + font-size: 0.8125rem; + letter-spacing: 0.04em; + color: var(--green); + margin-top: 0; + padding: var(--space-stack) var(--space-lg); + border: 1px solid color-mix(in srgb, var(--green) 22%, var(--border)); + border-left: 4px solid var(--green); + border-radius: var(--radius-control); + background: color-mix(in srgb, var(--surface-positive-badge) 60%, transparent); + line-height: 1.5; + } + .qs-savings-row.hidden { display: none; } + .qs-savings-row .fa-icon { color: var(--green); flex-shrink: 0; opacity: 0.3; } + .qs-savings-row strong { color: var(--green); font-weight: 700; } + + /* Vertical divider between contract term and onboarding fee */ + .qs-divider { + width: 1px; + height: auto; + background: var(--surface-settings-divider); + margin: 0; + align-self: stretch; + } + + .qs-right { + display: flex; + flex-direction: column; + gap: var(--space-md); + padding: 0 0 0 clamp(18px, 2.5cqi, 24px); + justify-content: center; + min-width: 0; + max-width: none; + } + + @container (max-width: 1040px) { + .quote-settings-bar { + grid-template-columns: 1fr; + row-gap: var(--space-lg); + } + + .qs-group { + padding-right: 0; + } + + .qs-divider { + width: auto; + height: 1px; + } + + .qs-right { + padding-left: 0; + } + } + + /* ── Custom toggle switch (replaces native checkbox for Waive) ── */ + .qs-toggle-row { + display: flex; + align-items: center; + gap: 9px; + cursor: pointer; + user-select: none; + } + .qs-toggle-row input[type=checkbox] { display: none; } + .qs-toggle-row input[type=checkbox]:focus-visible + .qs-switch { + outline: 2px solid var(--accent); + outline-offset: 2px; + } + .qs-switch { + width: 34px; + height: 20px; + background: var(--surface-switch-off); + border-radius: 10px; + position: relative; + transition: background 0.2s; + flex-shrink: 0; + } + .qs-switch::after { + content: ''; + position: absolute; + width: 14px; + height: 14px; + background: var(--surface-switch-knob); + border-radius: 50%; + top: 3px; + left: 3px; + transition: left 0.2s, background 0.2s; + box-shadow: var(--shadow-switch-knob); + } + .qs-toggle-row input:checked ~ .qs-switch { background: var(--surface-switch-on); } + .qs-toggle-row input:checked ~ .qs-switch::after { left: 17px; } + .qs-fee-waive:has(input:disabled) { opacity: 0.5; cursor: default; } + .qs-fee-input:disabled { opacity: 0.4; cursor: not-allowed; } + + .qs-toggle-label { + font-family: 'DM Mono', monospace; + font-size: 12px; + color: var(--muted); + letter-spacing: 0.04em; + } + .qs-fee-row { + display: flex; + flex-direction: column; + gap: var(--space-stack-tight); + padding: var(--space-sm) 0 0; + } + .qs-fee-header { + display: flex; + align-items: center; + gap: var(--space-stack-tight); + } + .qs-fee-waive { + margin-left: 0; + align-self: flex-start; + padding: var(--control-pad-y-tight) var(--control-pad-x-tight); + border: 1px solid var(--border); + border-radius: 999px; + background: color-mix(in srgb, var(--surface-input) 85%, transparent); + } + .qs-fee-label { + font-family: 'DM Mono', monospace; + font-size: 13px; + color: var(--ink); + letter-spacing: 0.04em; + line-height: var(--text-compact-line); + } + .qs-fee-input-wrap { + display: flex; + align-items: center; + background: var(--surface-input); + border: 1px solid var(--border); + border-radius: 10px; + overflow: hidden; + width: 100%; + } + .qs-fee-dollar { + padding: 6px var(--space-sm); + font-family: 'DM Mono', monospace; + font-size: 14px; + color: var(--muted); + background: var(--surface-input); + border-right: 1px solid var(--border); + } + .qs-fee-input { + background: var(--surface-input); + border: none; + color: var(--ink); + font-family: 'DM Mono', monospace; + font-size: 18px; + width: 100%; + min-width: 0; + text-align: left; + padding: var(--space-md) var(--space-stack); + outline: none; + } + .qs-fee-input::placeholder { + color: color-mix(in srgb, var(--muted) 86%, transparent); + opacity: 1; + font-size: 15px; + } + .qs-fee-input::-webkit-inner-spin-button, + .qs-fee-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } + .qs-fee-input[type=number] { -moz-appearance: textfield; } + /* ── INLINE-STYLE REPLACEMENT CLASSES ─────────────────────────── + These replace presentational style="" attributes that previously + bypassed the design token system. All colours use tokens. + ─────────────────────────────────────────────────────────────── */ + + /* Section I — admin fee display row */ + /* .admin-fee-header base styles merged into the waived section definition at line ~1079 */ + .admin-fee-title { font-family: 'Poppins', sans-serif; font-weight: 600; font-size: 1.375rem; } + .admin-fee-val { font-family: 'DM Mono', monospace; font-size: 1.375rem; color: var(--accent); } + .admin-fee-sub { font-size: 0.75rem; color: var(--muted); margin-bottom: var(--space-stack); line-height: var(--text-compact-line); } + .floor-note { font-size: 12px; color: var(--muted); margin-top: var(--space-stack-tight); margin-bottom: var(--space-stack-roomy); font-family: 'DM Mono', monospace; letter-spacing: 0.08em; text-transform: uppercase; line-height: var(--text-compact-line); } + + /* Sidebar sub-line rows (users/endpoints/admin breakdown text) */ + .sl-sub { + font-size: var(--sidebar-mono-size); + color: var(--muted); + font-family: 'DM Mono', monospace; + line-height: 1.6; + letter-spacing: 0.02em; + padding: var(--space-xs) 0 var(--space-stack) var(--space-2xl); + } + .sl-sub-row { + display: grid; + align-items: start; + } + .sl-sub-row-base { + grid-template-columns: minmax(0, 1fr); + } + .sl-sub-row-addon { + grid-template-columns: minmax(0, 1fr) auto; + column-gap: var(--space-md); + padding: 5px 0 0; + border-top: 1px dotted color-mix(in srgb, var(--sidebar-rule-color) 88%, transparent); + } + .sl-sub-row + .sl-sub-row { + margin-top: 2px; + } + .sl-sub-copy { + min-width: 0; + } + .sl-sub-row-addon .sl-sub-copy { + padding-left: var(--space-md); + } + .sl-sub-val { + color: var(--muted); + text-align: right; + white-space: nowrap; + } + + /* Per-user cost section */ + .per-user-cost-sub { display: inline-block; font-size: 0.625rem; opacity: 0.6; font-weight: 400; margin-top: 3px; } + .sidebar-note-mono { font-size: var(--sidebar-mono-size); padding: 2px 0 6px; font-family: 'DM Mono', monospace; line-height: 1.45; } + + /* VS Comparison block */ + .vs-comparison-wrap { + margin-top: var(--space-stack-roomy); + margin-bottom: var(--space-stack); + padding: var(--space-xl) var(--space-xl) var(--space-lg); + background: var(--surface-compare); + border: 1px solid var(--border-compare); + border-radius: var(--radius-card); + } + .vs-svs-label { font-size: 0.9375rem; color: var(--text-vs-heading); font-weight: 600; line-height: 1.3; } + .vs-val-accent { color: var(--text-vs-accent); font-weight: 600; font-size: 0.9375rem; } + .vs-td-muted { color: var(--text-vs-muted); font-size: 0.8125rem; line-height: 1.45; } + .vs-td-icon { margin-right: 6px; opacity: 0.7; vertical-align: middle; } + .vs-footnote { + font-size: 0.6875rem; + color: var(--muted); + margin-top: var(--space-stack); + padding-top: var(--space-md); + border-top: 1px solid var(--border); + line-height: 1.6; + font-style: italic; + } + + /* Side note icons and savings highlight */ + .savings-amount { color: var(--green); } + .sl-otf-waived > span:first-child { color: var(--green); text-decoration: line-through; text-decoration-color: var(--green); } + .sl-otf-waived .val { color: var(--green); } + .sl-otf-waived .otf-amt { text-decoration: none; } + .sl-otf-waived .otf-amt-strike { text-decoration: line-through; text-decoration-color: var(--green); opacity: 0.7; margin-right: var(--space-xs); } + .sl-otf-waived .otf-waived-label { text-decoration: none; font-weight: 600; letter-spacing: 0.06em; font-size: 0.7rem; margin-right: var(--space-xs); } + + /* ── ADMIN FEE WAIVED display */ + .admin-fee-header { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-stack-tight); margin-bottom: 6px; } + .admin-fee-waive-toggle { margin-left: auto; } + .admin-fee-strike { text-decoration: line-through; color: var(--muted); text-decoration-color: var(--muted); } + .admin-fee-waived-badge { font-family: 'DM Mono', monospace; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; color: var(--green); background: var(--surface-positive-badge); border: 1px solid var(--border-positive-badge); border-radius: var(--radius-control); padding: 2px var(--space-sm); vertical-align: middle; } + .sl-admin-waived > span:first-child { text-decoration: line-through; text-decoration-color: var(--muted); color: var(--muted); } + .admin-waive-savings { display: flex; align-items: center; gap: var(--space-sm); font-family: 'DM Mono', monospace; font-size: 0.78125rem; letter-spacing: 0.04em; color: var(--green); background: var(--surface-positive-panel); border: 1px solid var(--border-positive-panel); border-radius: var(--radius-control); padding: var(--space-stack-tight) var(--space-stack); margin-top: var(--space-md); margin-bottom: 6px; } + .admin-waive-savings.hidden { display: none; } + #adminWaivedAmt { font-weight: 700; } + + /* Nudge banner internal flex rows */ + .nudge-header-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-stack-tight); } + .nudge-nav-group { display: flex; gap: var(--space-sm); } + + /* VoIP savings prompt */ + .savings-prompt { font-size: 0.8125rem; color: var(--muted); margin-top: var(--space-sm); } + + /* ── SIDEBAR UTILITY CLASSES ───────────────────────────────────── + .sl-muted — de-emphasised row labels/values + .sl-discount-val — green discount amount + .sl-hst-val — muted HST amount + ─────────────────────────────────────────────────────────────── */ + /* Sidebar utility classes — !important overrides .sidebar-line .val compound selector */ + .sl-muted { color: var(--muted) !important; font-size: 0.6875rem; } + .sl-discount-val { color: var(--green) !important; } + .sl-discount-detail { font-size: 0.7rem; opacity: 0.7; } + .sl-hst-val { color: var(--text-money) !important; font-size: var(--sidebar-copy-size); } + .sidebar-line-discount { border-bottom-style: dashed; border-bottom-color: var(--sidebar-rule-color); opacity: 0.8; } + .sidebar-line.sidebar-line-hst { + margin-top: 0; + padding-top: var(--space-stack-tight); + padding-bottom: var(--space-stack-tight); + border-top: 1px var(--sidebar-line-rule-style) var(--sidebar-rule-color); + border-bottom: none; + } + .sidebar-line.sidebar-line-total { + font-weight: 600; + margin-top: 2px; + border-top: 1px var(--sidebar-total-rule-style) var(--sidebar-total-rule); + border-bottom: none; + padding-top: var(--space-stack); + } + .sl-hst-toggle { + justify-content: flex-start; + margin: var(--space-sm) 0; + } + + body.sidebar-focus-open { overflow: hidden; } + body.sidebar-focus-open .sidebar-focus-backdrop { + opacity: 1; + pointer-events: auto; + } + body.sidebar-focus-open .side-col { + position: relative; + z-index: 530; + } + body.sidebar-focus-open .sidebar-utility { display: none; } + body.sidebar-focus-open .side-col .export-wrap { display: none; } + body.sidebar-focus-open .sidebar-focus-print-btn { display: inline-flex; } + body.sidebar-focus-open .side-col .sidebar { + position: fixed; + top: calc(var(--top-bar-sticky-offset) + 10px); + left: 50%; + right: auto; + transform: translateX(-50%); + width: min(var(--page-max-width), calc(100vw - 20px)); + max-height: calc(100vh - var(--top-bar-sticky-offset) - 20px); + display: flex; + flex-direction: column; + z-index: 540; + box-shadow: var(--shadow-sidebar-focus); + } + body.sidebar-focus-open .side-col .sidebar-focus-client { + display: block; + } + body.sidebar-focus-open .side-col .sidebar-body { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; + padding: var(--space-4xl) var(--space-4xl) var(--space-3xl); + } + body.sidebar-focus-open .side-col .nudge-banner { + display: none !important; + } + body.sidebar-focus-open .side-col #vsComparison { + display: none !important; + } + body.sidebar-focus-open .side-col .sidebar-body > * { + min-width: 0; + } + body.sidebar-focus-open .side-col .sidebar-hero { + margin-bottom: var(--space-2xl); + } + body.sidebar-focus-open .side-col .sidebar-focus-columns { + display: grid; + grid-template-columns: minmax(0, 1.12fr) minmax(560px, 0.98fr); + gap: 0 42px; + align-items: start; + } + body.sidebar-focus-open .side-col .sidebar-focus-col { + display: flex; + flex-direction: column; + gap: var(--space-lg); + min-width: 0; + } + body.sidebar-focus-open .side-col .sidebar-focus-col + .sidebar-focus-col { + margin-top: 0; + } + body.sidebar-focus-open .side-col .sidebar-focus-col > .sidebar-group { + margin-top: 0; + } + body.sidebar-focus-open .side-col .sidebar-focus-col > #vsComparison { + margin-top: 0; + } + /* .export-wrap hidden in focus mode — see rule near line 1704 */ + body.sidebar-focus-open .sidebar-focus-icon-open { display: none; } + body.sidebar-focus-open .sidebar-focus-icon-close { display: inline-flex; } + .mobile-panel-sheet .sidebar-focus-toggle { display: none; } + .mobile-panel-sheet .sidebar-focus-print-btn { display: none; } + + /* ── VS COMPARISON CSS CLASSES (replace inline styles) ────────── + .vs-save-green — green "YOU SAVE" row background + .vs-save-amber — amber "Costs more" row background + .vs-val-green — green text for savings value/label + .vs-val-amber — amber text for "costs more" value/label + ─────────────────────────────────────────────────────────────── */ + .vs-save-green td { background: var(--surface-compare-success); } + .vs-save-amber td { background: var(--surface-compare-warning); } + /* VS value utility classes — !important overrides inherited td color */ + .vs-val-green { color: var(--green) !important; } + .vs-val-amber { color: var(--amber) !important; } + + /* ── SECTION CARD HOVER / OPEN POLISH ─────────────────────────── + Subtle left accent glow on hover; stronger treatment when open. + ─────────────────────────────────────────────────────────────── */ + .section { + transition: border-color 0.2s, box-shadow 0.2s; + } + .section:hover { + border-color: var(--section-hover-border); + box-shadow: var(--section-hover-shadow); + } + .section.sec-open { + border-color: var(--section-open-border); + box-shadow: var(--section-open-shadow); + } + + /* ── ADDON ROW SELECTED — stronger check indicator ────────────── + .selected gets a more prominent border + check indicator via + the checkbox's native accent-color. No pseudo-element needed. + ─────────────────────────────────────────────────────────────── */ + @keyframes addonPulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.015); } + 100% { transform: scale(1); } + } + .addon-row.selected { + background: var(--surface-selected); + border-color: var(--accent); + box-shadow: inset 3px 0 0 0 var(--accent); + animation: addonPulse 0.2s ease; + } + .addon-row.selected .addon-name { color: var(--ink); } + .addon-row.selected .addon-price { color: var(--text-selected-accent); } + .addon-row.selected .addon-desc { color: color-mix(in srgb, var(--ink) 86%, var(--muted)); } + + /* ── QUOTE NOTES ──────────────────────────────────────────────── + Free-text notes field above export buttons. + Persisted in localStorage, included in print + JSON export. + ─────────────────────────────────────────────────────────────── */ + .quote-notes-wrap { + padding: var(--space-md) var(--space-xl) 0; + } + .quote-notes-label { + font-family: 'DM Mono', monospace; + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--muted); + display: block; + margin-bottom: 6px; + } + .quote-notes-input { + width: 100%; + background: var(--surface-card); + border: 1px solid var(--border); + border-radius: var(--radius-control); + color: var(--ink); + font-family: 'Poppins', sans-serif; + font-size: 12px; + padding: var(--space-sm) var(--space-stack-tight); + resize: vertical; + min-height: 56px; + max-height: 160px; + outline: none; + transition: border-color var(--transition-fast); + } + .quote-notes-input:focus { + border-color: var(--accent); + } + .quote-notes-input::placeholder { + color: var(--muted); + opacity: 0.5; + } + + /* ── EXPORT BUTTONS ───────────────────────────────────────────── + Primary CTA: Print / Save PDF + Secondary: Export JSON + Copy + ─────────────────────────────────────────────────────────────── */ + .export-wrap { + padding: var(--space-stack-roomy) var(--space-xl) var(--space-lg); + background: transparent; + border-top: none; + display: flex; + flex-direction: column; + gap: var(--space-stack-tight); + } + .btn-export { + width: 100%; + background: var(--accent); + color: var(--btn-primary-fg); + border: none; + 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.12em; + text-transform: uppercase; + cursor: pointer; + transition: background var(--transition-fast), transform 0.1s, filter var(--transition-fast), box-shadow var(--transition-fast); + display: flex; + align-items: center; + justify-content: center; + } + .btn-export:hover { + background: var(--btn-primary-hover); + filter: brightness(1.15); + box-shadow: var(--shadow-export-hover); + } + .btn-export:active { transform: scale(0.97); filter: brightness(0.95); } + .btn-export-secondary { + background: transparent; + border: 1px solid var(--border); + color: var(--muted); + font-size: 0.71875rem; + padding: var(--space-stack-tight) var(--space-stack); + } + .btn-export-secondary:hover { background: var(--card); border-color: var(--accent); color: var(--ink); } + + /* ── VOIP PHONE BILL SAVINGS ESTIMATOR (Section VI) ──────────── + Optional input: current monthly phone bill. + updateSavings(q) compares against voipTotal and shows green + savings message or amber warning if VoIP costs more. + On mobile stacks vertically (flex-direction:column). + ─────────────────────────────────────────────────────────────── */ + .savings-input-row { + display: flex; + align-items: center; + gap: var(--space-md); + margin-top: var(--space-stack-roomy); + padding-top: var(--space-stack-roomy); + border-top: 1px solid var(--border); + } + .savings-input-row label { + font-size: 0.875rem; + color: var(--muted); + flex: 1; + } + .savings-input-row input { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius-control); + color: var(--ink); + font-family: 'DM Mono', monospace; + font-size: 1.125rem; + width: 120px; + text-align: center; + padding: var(--control-pad-y-tight) var(--control-pad-x-tight); + outline: none; + } + .savings-result { + margin-top: var(--space-md); + background: var(--surface-success); + border: 1px solid var(--surface-success-border); + border-radius: var(--radius-control); + padding: var(--control-pad-y) var(--control-pad-x); + font-family: 'DM Mono', monospace; + font-size: 0.875rem; + color: var(--green); + line-height: 1.65; + } + /* Amber modifier — toggled by JS (classList.add/remove) when VoIP quote > current bill */ + .savings-result.savings-amber { + background: var(--surface-warning-panel); + border-color: var(--surface-warning-border); + color: var(--amber); + } + + /* ── BOTTOM PITCH BANNER ──────────────────────────────────────── + 4-column grid (2-col on tablet/mobile) outside the .outer grid. + .pitch-inner has margin-left:96px to align with section cards. + Columns: Security-First | Ottawa-Based | Flat-Rate | Scales With You + .pitch-footer — green strip at bottom with tagline. + Icons are inline SVG (FA Free 6.5.0 paths), accent blue. + ─────────────────────────────────────────────────────────────── */ + .pitch-wrap { + width: 100%; + padding: 0; + margin: 0; + } + .pitch-inner { + margin-left: 0; + background: var(--card); + border: none; + border-top: 1px solid var(--border); + border-radius: 0; + overflow: hidden; + } + .pitch-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + } + .pitch-item { + padding: 26px 22px; + border-right: 1px solid var(--border); + } + .pitch-item:last-child { border-right: none; } + .pitch-head { + display: flex; + align-items: center; + gap: var(--space-md); + margin-bottom: var(--space-stack-tight); + min-height: 28px; + } + .pitch-icon { + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + font-size: 20px; + color: var(--accent); + } + .pitch-title { + font-family: 'Poppins', sans-serif; + font-weight: 600; + font-size: 16px; + line-height: 1.3; + margin: 0; + } + .pitch-desc { font-size: var(--text-copy-size); color: var(--muted); line-height: var(--text-copy-line); } + .pitch-footer { + background: var(--surface-success); + border-top: 1px solid var(--surface-success-border); + padding: var(--space-md) var(--space-3xl); + font-family: 'DM Mono', monospace; + font-size: 13px; + color: var(--green); + letter-spacing: 0.05em; + text-align: center; + } + + @media (min-width: 1500px) { + .quote-settings-bar { + gap: var(--space-stack-tight); + } + + .section-header { + margin-bottom: var(--space-3xl); + } + + .sidebar-mrr { + font-size: 52px; + } + + .pill-toggle label { + padding: var(--space-xl) 22px; + } + } + + diff --git a/pre-alpha/SVS-MSP-Calculator-glass.css b/pre-alpha/SVS-MSP-Calculator-glass.css new file mode 100644 index 0000000..0144b9b --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator-glass.css @@ -0,0 +1,623 @@ +/* ══════════════════════════════════════════════════════════════ + SVS MSP Calculator — Glass Dark Theme + Imported dynamically by the theme toggle as a third test theme. + Keeps the existing HTML structure intact and overrides presentation only. + ══════════════════════════════════════════════════════════════ */ + +html { + color-scheme: dark; +} + +:root { + --ink: #eef6ff; + --paper: #08111c; + --accent: #69c8ff; + --muted: #9fb3c9; + --border: rgba(143, 183, 221, 0.2); + --card: rgba(10, 18, 31, 0.62); + --green: #63d8a2; + --amber: #ffbe68; + --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); + --group-strip: rgba(105, 200, 255, 0.2); + --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: var(--glass-heading); + --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: var(--glass-panel-border); + --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: var(--glass-heading); + --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: var(--glass-panel-border); + --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: var(--glass-heading); + --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: var(--glass-panel-border); + --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: var(--glass-ghost-hover-bg); + --glass-selected-border: var(--glass-ghost-hover-border); + --glass-selected-text: var(--glass-heading); + --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: var(--glass-ghost-hover-border); + --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: var(--glass-ghost-hover-border); + --glass-reset-hover-text: var(--glass-heading); + --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.04); + --sidebar-zone-invoice: rgba(105, 200, 255, 0.07); + --sidebar-zone-value: rgba(99, 216, 162, 0.04); + --sidebar-zone-summary: rgba(105, 200, 255, 0.03); + --sidebar-row-stripe: rgba(105, 200, 255, 0.03); + --sidebar-line-rule: rgba(143, 183, 221, 0.12); + --sidebar-total-rule: rgba(143, 183, 221, 0.22); +} + +body { + background: var(--glass-page-bg); + background-attachment: fixed; + color: var(--ink); + position: relative; +} + +body::before { + content: ""; + position: fixed; + inset: 0; + pointer-events: none; + background: var(--glass-page-overlay); + opacity: 0.9; + z-index: 0; +} + +.outer, +.pitch-wrap { + position: relative; + z-index: 1; +} + +::selection { + background: var(--selection-bg); + color: var(--selection-text); +} + +.top-bar { + border-bottom-width: 1px !important; + box-shadow: var(--top-bar-shadow) !important; + backdrop-filter: blur(18px) saturate(135%); + -webkit-backdrop-filter: blur(18px) saturate(135%); +} + +@media (min-width: 1101px) { + .outer { + padding-top: var(--sidebar-top-gap) !important; + } +} + +.section, +.quote-settings-bar, +.sidebar, +.sidebar-utility .btn-export, +.sidebar-utility .btn-export-secondary, +.mobile-panel-sheet, +.mobile-panel-close-row, +.mobile-panel-actions, +.confirm-modal-card, +.vs-comparison-wrap, +.pitch-inner { + background: var(--glass-panel-bg) !important; + border-color: var(--glass-panel-border) !important; + box-shadow: var(--glass-panel-shadow) !important; + backdrop-filter: blur(18px) saturate(135%); + -webkit-backdrop-filter: blur(18px) saturate(135%); +} + +.section { + background: var(--glass-section-bg) !important; +} + +.section-num { + color: var(--glass-section-num) !important; + text-shadow: var(--glass-section-num-glow); +} + +.section-title, +.confirm-modal-title, +.sidebar-mrr, +.sidebar-line .val, +.vs-svs-label { + color: var(--glass-heading) !important; +} + +.section-subtitle, +.feature-card-desc, +.sidebar-note, +.sidebar-note-mono, +.sl-sub, +.vs-label, +.vs-td-muted, +.savings-prompt, +.pitch-desc, +.qs-label, +.qs-toggle-label, +.qs-fee-label, +.qs-fee-dollar, +.sidebar-line, +.section-title-tag { + color: var(--muted) !important; +} + +.client-input { + color: var(--glass-heading) !important; + border-bottom-color: var(--glass-client-border) !important; +} + +.client-input::placeholder { + color: var(--glass-client-placeholder) !important; +} + +.sec-chevron, +.addon-preview-pill, +.btn-toggle-all, +.confirm-btn-secondary, +.btn-export-secondary, +.mobile-panel-close, +.nudge-nav-btn { + background: var(--glass-ghost-bg) !important; + border-color: var(--glass-ghost-border) !important; + color: var(--muted) !important; + box-shadow: var(--glass-ghost-shadow); +} + +.sec-open .sec-chevron, +.section-toggle:hover .sec-chevron, +.btn-toggle-all:hover, +.confirm-btn-secondary:hover, +.btn-export-secondary:hover, +.nudge-nav-btn:hover, +.mobile-panel-close:hover { + background: var(--glass-ghost-hover-bg) !important; + border-color: var(--glass-ghost-hover-border) !important; + color: var(--glass-ghost-hover-text) !important; +} + +.pill-toggle, +.tier-seg-wrap, +.qs-fee-input-wrap { + background: var(--glass-group-surface) !important; + border-color: var(--glass-ghost-border) !important; +} + +.pill-toggle label, +.tier-seg { + background: transparent !important; +} + +.pill-toggle label:hover, +.tier-seg:hover, +.addon-row:hover { + background: var(--glass-ghost-bg) !important; +} + +.tier-seg.active, +.btn-export, +.confirm-btn-danger, +.mobile-quote-pill, +.progress-fill { + background: var(--glass-cta-gradient) !important; + color: var(--text-on-accent) !important; +} + +.pill-toggle input:checked + label { + background: var(--glass-cta-checked-bg) !important; + color: var(--text-on-accent) !important; + box-shadow: var(--glass-cta-checked-shadow) !important; +} + +.sidebar-header { + background: var(--glass-sidebar-header-bg) !important; + color: var(--text-on-accent) !important; + box-shadow: var(--glass-sidebar-header-shadow) !important; +} + +.pill-toggle input:checked + label .pill-desc, +.pill-toggle input:checked + label .pill-price, +.tier-seg.active .tier-name, +.tier-seg.active .tier-price, +.tier-seg.active .tier-sub { + color: var(--text-on-accent-strong) !important; +} + +.num-input, +.qs-fee-input, +.qs-fee-dollar, +.qs-fee-input-wrap, +.mobile-panel-sheet .sidebar-body { + background: var(--glass-input-surface) !important; +} + +.num-input, +.qs-fee-input { + border-color: var(--glass-input-border) !important; + color: var(--glass-ghost-hover-text) !important; + box-shadow: var(--glass-input-inset-shadow); +} + +.num-input:focus, +.qs-fee-input:focus, +.client-input:focus-visible { + border-color: var(--glass-input-focus-border) !important; + box-shadow: var(--glass-input-focus-shadow) !important; +} + +.qs-switch { + background: var(--surface-switch-off) !important; +} + +.qs-toggle-row input:checked ~ .qs-switch { + background: var(--surface-switch-on) !important; +} +.qs-switch::after { + background: var(--glass-switch-knob) !important; + box-shadow: var(--glass-switch-shadow); +} + +.addon-row.selected { + background: var(--glass-selected-bg) !important; + border-color: var(--glass-selected-border) !important; +} + +.addon-row.selected .addon-name, +.addon-row.selected .addon-price, +.addon-row.selected .addon-desc { + color: var(--glass-selected-text) !important; +} + +.feature-card { + 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, +.nudge-banner.green, +.admin-waive-savings { + background: var(--glass-success-bg) !important; + border-color: var(--glass-success-border) !important; + color: var(--green) !important; +} + +.callout-red { + background: var(--glass-danger-bg) !important; + border-color: var(--glass-danger-border) !important; + color: var(--glass-danger-text) !important; +} + +.nudge-banner.amber, +.admin-fee-waived-badge { + background: var(--glass-warning-bg) !important; + border-color: var(--glass-warning-border) !important; + color: var(--amber) !important; +} + +.admin-waive-savings, +.admin-fee-waived-badge, +.addon-preview-pill.active { + box-shadow: var(--glass-pill-inset-shadow); +} + +.addon-preview-pill.active { + background: var(--glass-addon-active-bg) !important; + border-color: var(--glass-addon-active-border) !important; + color: var(--glass-addon-active-text) !important; +} + +.collapsible-header, +.pitch-item, +.vs-label::after, +.pitch-footer, +.mobile-panel-close-row, +.mobile-panel-actions { + border-color: var(--glass-divider) !important; +} +.sidebar-line { + border-bottom-color: var(--glass-divider) !important; +} +.sidebar-line.sidebar-line-total { + border-top-color: var(--sidebar-total-rule) !important; + border-bottom: none !important; +} + +.sidebar-title, +.sidebar-client.placeholder { + color: var(--glass-sidebar-placeholder) !important; +} + +.sidebar-body, +.mobile-panel-sheet .sidebar, +.mobile-panel-sheet .sidebar-body { + background: transparent !important; +} + +.sidebar-note strong, +.sl-discount-val, +.savings-amount { + color: var(--green) !important; +} + +.sl-hst-toggle, +.sl-hst-val, +.sl-muted { + color: var(--muted) !important; +} + +.vs-comparison-wrap { + background: var(--glass-compare-bg) !important; +} + +.vs-save-green td { + background: var(--glass-compare-success) !important; +} + +.vs-save-amber td { + background: var(--glass-compare-warning) !important; +} + +.export-wrap { + background: transparent !important; + border-top: none !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 { + box-shadow: var(--glass-export-shadow) !important; +} + +.btn-export:hover, +.mobile-quote-pill:hover { + 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 { + color: var(--glass-reset-text) !important; +} + +.btn-reset-quote:hover { + background: var(--glass-reset-hover-bg) !important; + border-color: var(--glass-reset-hover-border) !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 { + background: var(--glass-modal-backdrop) !important; + backdrop-filter: blur(10px) saturate(125%); + -webkit-backdrop-filter: blur(10px) saturate(125%); +} + +.confirm-modal-card { + background: var(--glass-modal-bg) !important; +} + +.pitch-inner { + background: var(--glass-pitch-bg) !important; +} + +.pitch-title { + color: var(--glass-heading-soft) !important; +} + +.pitch-footer { + background: var(--glass-pitch-footer-bg) !important; + color: var(--glass-pitch-footer-text) !important; +} + +.mobile-panel-sheet { + background: var(--glass-mobile-sheet-bg) !important; +} + +.mobile-panel-close-row, +.mobile-panel-actions { + background: var(--glass-mobile-row-bg) !important; +} + +@media (max-width: 1100px) { + .top-bar { + background: var(--glass-top-bar-mobile-bg) !important; + } +} + +@media (max-width: 1100px) { + body { + background-attachment: scroll; + } +} + +@media (max-width: 600px) { + body { + background: var(--glass-page-bg-mobile); + background-attachment: scroll; + } + + body::before { + background: var(--glass-page-overlay-mobile); + opacity: 0.82; + } + + .theme-toggle-btn { + box-shadow: var(--glass-theme-toggle-mobile-shadow) !important; + } + + .section, + .quote-settings-bar, + .sidebar, + .mobile-panel-sheet, + .confirm-modal-card { + box-shadow: var(--glass-mobile-panel-shadow) !important; + } +} diff --git a/pre-alpha/SVS-MSP-Calculator-layout.css b/pre-alpha/SVS-MSP-Calculator-layout.css new file mode 100644 index 0000000..c9dfb83 --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator-layout.css @@ -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: static; 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: var(--btn-primary-fg); + 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; + } + + diff --git a/pre-alpha/SVS-MSP-Calculator-light.css b/pre-alpha/SVS-MSP-Calculator-light.css new file mode 100644 index 0000000..52790f1 --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator-light.css @@ -0,0 +1,129 @@ +/* ══════════════════════════════════════════════════════════════ + SVS MSP Calculator — Light Theme + Phase 5 reduces this file to token overrides only so the light mode + stays aligned with the shared product system. + ══════════════════════════════════════════════════════════════ */ + +:root { + --ink: #2c2825; + --paper: #e2dccf; + --accent: #637f88; + --muted: #6a6157; + --border: #c3baab; + --border-soft: #cdc6ba; + --card: #ece4d6; + --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: var(--card); + --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: var(--text-term-name-active); + --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: var(--surface-export); + --surface-modal: var(--surface-input); + --surface-mobile-sheet: #e5dfd6; + --surface-mobile-close-row: #dbd4cb; + --surface-mobile-actions: var(--surface-mobile-close-row); + --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: var(--surface-input); + --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: var(--surface-warning); + --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; + --group-strip: rgba(99, 127, 136, 0.18); + --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: var(--border-sidebar); + --border-export-top: #ccc4ba; + --border-mobile-sheet: var(--border-sidebar); + --border-mobile-row: var(--border-export-top); + --sidebar-zone-services: rgba(0, 0, 0, 0.03); + --sidebar-zone-invoice: rgba(0, 0, 0, 0.05); + --sidebar-zone-value: rgba(33, 112, 69, 0.04); + --sidebar-zone-summary: rgba(0, 0, 0, 0.02); + --sidebar-row-stripe: rgba(0, 0, 0, 0.02); + --sidebar-line-rule: color-mix(in srgb, var(--border) 70%, transparent); + --sidebar-total-rule: color-mix(in srgb, var(--border) 90%, transparent); + --surface-switch-off: #b5ad9f; + --surface-switch-on: var(--green); +} + +.btn-import-quote:hover { + background: color-mix(in srgb, var(--sky) 8%, transparent); + border-color: color-mix(in srgb, var(--sky) 35%, transparent); + color: var(--sky); +} diff --git a/pre-alpha/SVS-MSP-Calculator-print.css b/pre-alpha/SVS-MSP-Calculator-print.css new file mode 100644 index 0000000..e51ebe5 --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator-print.css @@ -0,0 +1,158 @@ +/* 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; } + .group-label { display: none !important; } + #sec-02::after, #sec-03::after, #sec-01::after { display: none !important; } + .group-divider { 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; + } + } + diff --git a/pre-alpha/SVS-MSP-Calculator-responsive.css b/pre-alpha/SVS-MSP-Calculator-responsive.css new file mode 100644 index 0000000..a5f926c --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator-responsive.css @@ -0,0 +1,436 @@ +/* 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); } + #sec-02::after, #sec-03::after, #sec-01::after { display: none; } + .group-label { margin-left: 0; } + /* Mobile grouping — accent left border on Managed IT sections */ + #sec-02, #sec-03, #sec-01 { + border-left: 3px solid var(--group-strip); + } + .group-divider { margin-left: 0; margin-right: 0; } + .mobile-quote-pill { top: 12vh; } + + /* 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: calc(var(--top-bar-sticky-offset) + var(--space-lg)); + 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; } + } diff --git a/pre-alpha/SVS-MSP-Calculator-tokens.css b/pre-alpha/SVS-MSP-Calculator-tokens.css new file mode 100644 index 0000000..f7ecbc9 --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator-tokens.css @@ -0,0 +1,264 @@ +/* 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: var(--surface-warning); + --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-soft: rgba(33,112,69,0.08); + --surface-positive-strong: rgba(33,112,69,0.13); + --border-positive-soft: rgba(33,112,69,0.22); + --border-positive-strong: rgba(33,112,69,0.3); + --surface-positive-pill: var(--surface-positive-soft); + --surface-positive-badge: var(--surface-positive-strong); + --border-positive-badge: var(--border-positive-strong); + --surface-positive-badge-strong: var(--surface-positive-strong); + --border-positive-badge-strong: var(--border-positive-strong); + --surface-positive-panel: var(--surface-positive-soft); + --border-positive-panel: var(--border-positive-soft); + --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-soft: rgba(255,255,255,0.85); + --text-on-accent-subtle: rgba(255,255,255,0.7); + --text-on-accent-strong: var(--text-on-accent-soft); + --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); + --group-strip: rgba(45, 122, 168, 0.18); + --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); + --surface-overlay-btn: rgba(255,255,255,0.06); + --surface-overlay-btn-hover: rgba(255,255,255,0.12); + --border-overlay-btn: rgba(255,255,255,0.18); + --border-overlay-btn-hover: rgba(255,255,255,0.28); + --focus-ring-overlay: rgba(255,255,255,0.5); + --surface-sidebar-focus-backdrop: rgba(5, 11, 19, 0.58); + --shadow-sidebar: 0 18px 42px rgba(0,0,0,0.12); + --shadow-sidebar-focus: 0 28px 64px rgba(0,0,0,0.28); + --shadow-export-hover: 0 2px 8px rgba(0,0,0,0.25); + --border-nudge-nav: rgba(255,255,255,0.06); + --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-zone-services: rgba(255, 255, 255, 0.04); + --sidebar-zone-invoice: rgba(255, 255, 255, 0.07); + --sidebar-zone-value: rgba(58, 184, 112, 0.04); + --sidebar-zone-summary: rgba(255, 255, 255, 0.03); + --sidebar-zone-tax: transparent; + --sidebar-line-rule: color-mix(in srgb, var(--border) 88%, transparent); + --sidebar-line-rule-style: dashed; + --sidebar-total-rule: var(--border); + --sidebar-total-rule-style: solid; + --sidebar-row-stripe: rgba(255, 255, 255, 0.018); + --sidebar-group-title-color: var(--muted); + --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)); + } + diff --git a/pre-alpha/SVS-MSP-Calculator.css b/pre-alpha/SVS-MSP-Calculator.css new file mode 100644 index 0000000..9449940 --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator.css @@ -0,0 +1,10 @@ +/* SVS MSP Calculator - Base Manifest */ +/* Phase 5 keeps the original filename stable for the HTML shell while the + actual styles are split into concern-specific files behind it. */ + +@import url('SVS-MSP-Calculator-tokens.css'); +@import url('SVS-MSP-Calculator-base.css'); +@import url('SVS-MSP-Calculator-layout.css'); +@import url('SVS-MSP-Calculator-components.css'); +@import url('SVS-MSP-Calculator-responsive.css'); +@import url('SVS-MSP-Calculator-print.css'); diff --git a/pre-alpha/SVS-MSP-Calculator.html b/pre-alpha/SVS-MSP-Calculator.html new file mode 100644 index 0000000..01b08fa --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator.html @@ -0,0 +1,1012 @@ + + + + + +SVS MSP — Live Quote Calculator + + + + + + + + + + +
+
+
+
+
+ Quote Summary + +
+
+ + +
+ +
+
+
+ + + +
+
+ +
+ SVS-00000000-0000
+ +
+ +
+
+ + +
+ +
+ + +
+
Prepared for
+ +
+
Prepared by
+ +
+
+ +
+ +
+ + +
+
+
+
Contract Term & Incentives
+
+
+ + + + + + +
+
+ + +
+
+
+
+
+
+ +
+
+ $ + +
+ +
+
+
+ +
Managed IT Services (Sections I, II, III)
+ + +
+ + +
+
+ Managed Service Threshold +
+
+
+
+
+
+ +
+ + +
+ + +
+ + +
+ + +
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + + +
+ + + +
+ +
+ + +
+
+
+
+
+
+
Security-First MSP
+
+
Every engagement is built on a security baseline — EDR, MFA, patch management, and cyber warranty included.
+
+
+
+
+
Ottawa-Based Team
+
+
Local presence, Canadian data sovereignty, and an account team that knows your business and your region.
+
+
+
+
+
Flat-Rate, No Surprises
+
+
Predictable monthly billing with no per-ticket charges — aligned incentives mean we fix things right the first time.
+
+
+
+
+
Scales With You
+
+
Add users, endpoints, servers, ZT networking, or VoIP as you grow — one vendor, one invoice, one relationship.
+
+
+ +
+
+ + + + + + + + + + + + + + + + + diff --git a/pre-alpha/SVS-MSP-Calculator.js b/pre-alpha/SVS-MSP-Calculator.js new file mode 100644 index 0000000..8b9ceca --- /dev/null +++ b/pre-alpha/SVS-MSP-Calculator.js @@ -0,0 +1,393 @@ +// Pricing defaults and JSON loading live in quote-pricing.js. +// This file intentionally consumes the pricing globals exposed there +// so Phase 1 can stay low-risk and preserve the current runtime API. + +// Rendering helpers and nudge state live in quote-render.js. +// Persistence, export/print, theme, and mobile sync live in dedicated +// modules so this file can stay focused on quote calculation orchestration. + +// --- CALC --- +// ── calcQuote() ───────────────────────────────────────────────── +// Compatibility wrapper around the pure Phase 2 quote engine. +// Reads live form state from the DOM, then delegates pricing math to +// calculateQuote(state, pricing) without changing the rest of the app. +function calcQuote() { + return calculateQuote(readFormState(), getPricingConfig()); +} + +// --- MAIN UPDATE --- +// ── update() ───────────────────────────────────────────────────── +// Master DOM update function. Called on every input change. +// Sequence: calcQuote → update all displays → renderNudge → +// updateSavings → updateVsComparison → updateSectionSummaries +// Also wrapped by the mobile IIFE below to sync _m elements. +// Do not call renderNudge() directly from other functions; +// always go through update() to keep _m panel in sync. +function update() { + const q = calcQuote(); + const pricing = getPricingConfig(); + const bestM365Rate = Math.max(pricing.RATE_M365_M2M || 0, pricing.RATE_M365 || 0); + const m365BundleSavings = Math.max(0, bestM365Rate - pricing.RATE_BYOL); + const render = window.SVSQuoteRender; + + // ── Onboarding fee logic ── + // m2m: auto = 50% MRR, manual override allowed, waive toggle available + // 12-month: auto = 50% off onboarding (25% of MRR), manual override allowed + // 24-month: complimentary (fully waived), input disabled + const waivedEl = document.getElementById('onboardingWaived'); + const feeEl = document.getElementById('oneTimeFee'); + const fullOnboarding = Math.round(q.MRR / 2); + + if (waivedEl && q.contractTerm === '24mo') { + // 24-month: fully complimentary + waivedEl.checked = true; + waivedEl.disabled = true; + waivedEl.dataset.autoWaived = '1'; + // Preserve any manual override so it survives the round-trip back to m2m/12mo + if (feeEl && feeEl.dataset.manual) { + feeEl.dataset.manualValue = feeEl.value; + delete feeEl.dataset.manual; + } + } else if (waivedEl && q.contractTerm === '12mo') { + // 12-month: 50% off onboarding, not waived — clear any auto-waive state + waivedEl.disabled = false; + if (waivedEl.dataset.autoWaived) { + waivedEl.checked = false; + delete waivedEl.dataset.autoWaived; + } + // Restore manual override if user had one before switching to 24mo + if (feeEl && !feeEl.dataset.manual && feeEl.dataset.manualValue) { + feeEl.dataset.manual = '1'; + feeEl.value = feeEl.dataset.manualValue; + delete feeEl.dataset.manualValue; + } + } else if (waivedEl) { + // m2m: full manual control + waivedEl.disabled = false; + if (waivedEl.dataset.autoWaived) { + waivedEl.checked = false; + delete waivedEl.dataset.autoWaived; + } + // Restore manual override if user had one before switching to 24mo + if (feeEl && !feeEl.dataset.manual && feeEl.dataset.manualValue) { + feeEl.dataset.manual = '1'; + feeEl.value = feeEl.dataset.manualValue; + delete feeEl.dataset.manualValue; + } + } + + const waived = waivedEl?.checked || false; + let oneTimeFee; + if (waived) { + oneTimeFee = 0; + if (feeEl) { feeEl.value = ''; feeEl.disabled = true; feeEl.placeholder = 'Complimentary'; } + } else if (q.contractTerm === '12mo') { + // 12-month: 50% off the standard onboarding fee + if (feeEl) { feeEl.disabled = false; feeEl.placeholder = '50% off'; } + if (feeEl && !feeEl.dataset.manual) { + oneTimeFee = Math.round(fullOnboarding / 2); + feeEl.value = oneTimeFee > 0 ? oneTimeFee : ''; + } else { + oneTimeFee = parseFloat(feeEl?.value) || 0; + } + } else { + // m2m: standard auto-calc + if (feeEl) { feeEl.disabled = false; feeEl.placeholder = 'auto'; } + if (feeEl && !feeEl.dataset.manual) { + oneTimeFee = fullOnboarding; + feeEl.value = oneTimeFee > 0 ? oneTimeFee : ''; + } else { + oneTimeFee = parseFloat(feeEl?.value) || 0; + } + } + q.oneTimeFee = oneTimeFee; + const renderOptions = { + m365BundleSavings, + oneTimeFee, + onboardingWaived: waived, + onboardingWouldBe: fullOnboarding, + onboardingHalfOff: q.contractTerm === '12mo' && !waived + }; + + render.renderQuoteUi(q, renderOptions); + render.renderSidebar(q, renderOptions); + render.setNudges(render.buildNudges(q, renderOptions)); + + renderNudge(); + updateSavings(q); + updateVsComparison(q); + updateSectionSummaries(q); + + debouncedSave(); +} + +// ── onWaiveToggle() ────────────────────────────────────────────── +// Called from onchange on #onboardingWaived checkbox. +// Clears the manual override flag on the fee input so auto-calc resumes, +// then runs update(). Extracted from inline HTML attribute for clarity. +function onWaiveToggle() { + const feeInput = document.getElementById('oneTimeFee'); + if (feeInput) feeInput.removeAttribute('data-manual'); + update(); +} + +// ── toggleSection(id) ──────────────────────────────────────────── +// Collapses/expands a numbered section card. +// Adds/removes .sec-open on the section element. +// .sec-open → chevron rotates 180deg (CSS), body shown (JS display). +// Calls updateSectionSummaries() to show/hide summary badges. +// Map: section ID → collapsible IDs that should auto-expand when section opens +const _sectionCollapsibles = {}; + +function finishSectionAnimation(body, isOpen) { + body.style.transition = ''; + body.style.overflow = ''; + body.style.height = ''; + body.style.opacity = ''; + body.style.display = isOpen ? '' : 'none'; +} + +function animateSectionBody(body, open) { + if (!body) return; + + if (body._sectionAnimationCleanup) { + body._sectionAnimationCleanup(); + body._sectionAnimationCleanup = null; + } + + body.style.overflow = 'hidden'; + body.style.transition = 'height 0.34s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.22s ease'; + + if (open) { + body.style.display = ''; + body.style.height = '0px'; + body.style.opacity = '0'; + body.getBoundingClientRect(); + + const targetHeight = body.scrollHeight; + requestAnimationFrame(() => { + body.style.height = targetHeight + 'px'; + body.style.opacity = '1'; + }); + } else { + const startHeight = body.scrollHeight || body.offsetHeight; + body.style.display = ''; + body.style.height = startHeight + 'px'; + body.style.opacity = '1'; + body.getBoundingClientRect(); + + requestAnimationFrame(() => { + body.style.height = '0px'; + body.style.opacity = '0'; + }); + } + + const onEnd = (event) => { + if (event.target !== body || event.propertyName !== 'height') return; + body.removeEventListener('transitionend', onEnd); + body._sectionAnimationCleanup = null; + finishSectionAnimation(body, open); + }; + + body._sectionAnimationCleanup = () => { + body.removeEventListener('transitionend', onEnd); + finishSectionAnimation(body, open); + }; + + body.addEventListener('transitionend', onEnd); +} + +function toggleSection(id) { + const section = document.getElementById(id); + const body = document.getElementById(id + '-body'); + if (!section || !body) return; + const isOpen = section.classList.toggle('sec-open'); + // Sync aria-expanded on the section-toggle header + const header = section.querySelector('.section-toggle'); + if (header) header.setAttribute('aria-expanded', String(isOpen)); + animateSectionBody(body, isOpen); + // Auto-expand inner collapsibles when section opens + if (isOpen && _sectionCollapsibles[id]) { + _sectionCollapsibles[id].forEach(cid => { + const cBody = document.getElementById(cid); + const cIcon = document.getElementById(cid + '-icon'); + const cPreview = document.getElementById(cid + '-preview'); + if (cBody && !cBody.classList.contains('open')) { + cBody.classList.add('open'); + if (cIcon) cIcon.classList.add('open'); + if (cPreview) cPreview.style.display = 'none'; + } + }); + } + updateSectionSummaries(calcQuote()); + updateToggleAllBtn(); +} + +// ── toggleAllSections() / updateToggleAllBtn() ──────────────────── +// Collapse all if any are open; expand all if all are closed. +// Button label reflects current state. +const _allSecIds = ['sec-02','sec-03','sec-01','sec-04','sec-05','sec-06']; +function toggleAllSections() { + const anyOpen = _allSecIds.some(id => document.getElementById(id)?.classList.contains('sec-open')); + _allSecIds.forEach(id => { + const section = document.getElementById(id); + const body = document.getElementById(id + '-body'); + if (!section || !body) return; + if (anyOpen) { + section.classList.remove('sec-open'); + animateSectionBody(body, false); + } + else { + section.classList.add('sec-open'); + animateSectionBody(body, true); + // Auto-expand inner collapsibles + if (_sectionCollapsibles[id]) { + _sectionCollapsibles[id].forEach(cid => { + const cBody = document.getElementById(cid); + const cIcon = document.getElementById(cid + '-icon'); + const cPreview = document.getElementById(cid + '-preview'); + if (cBody && !cBody.classList.contains('open')) { + cBody.classList.add('open'); + if (cIcon) cIcon.classList.add('open'); + if (cPreview) cPreview.style.display = 'none'; + } + }); + } + } + }); + updateSectionSummaries(calcQuote()); + updateToggleAllBtn(); +} +function updateToggleAllBtn() { + const anyOpen = _allSecIds.some(id => document.getElementById(id)?.classList.contains('sec-open')); + const btn = document.getElementById('toggleAllBtn'); + if (!btn) return; + const collapseSpan = btn.querySelector('.toggle-all-collapse-icon'); + const expandSpan = btn.querySelector('.toggle-all-expand-icon'); + const textSpan = btn.querySelector('.toggle-all-label'); + if (collapseSpan) collapseSpan.style.display = anyOpen ? '' : 'none'; + if (expandSpan) expandSpan.style.display = anyOpen ? 'none' : ''; + if (textSpan) textSpan.textContent = anyOpen ? 'Collapse All' : 'Expand All'; +} + +// ── toggleCollapsible(id) ───────────────────────────────────────── +// Collapses/expands inner content panels (What's Included, Add-Ons). +// Separate from section-level toggle. Toggles .open on .collapsible-body. +// Also toggles preview pills (shown when collapsed, hidden when open). +function toggleCollapsible(id) { + const body = document.getElementById(id); + const icon = document.getElementById(id + '-icon'); + const preview = document.getElementById(id + '-preview'); + if (!body) return; + const open = body.classList.toggle('open'); + if (icon) icon.classList.toggle('open', open); + if (preview) preview.style.display = open ? 'none' : 'flex'; + // Sync aria-expanded on the collapsible header that controls this body + const header = body.previousElementSibling; + if (header && header.classList.contains('collapsible-header')) { + header.setAttribute('aria-expanded', String(open)); + } +} + +// ── toggleAddon(checkId, rowId) ───────────────────────────────── +// Flips the hidden checkbox + toggles .selected on the visible row. +// Called via onclick on the